openvdb/ 0000755 0000000 0000000 00000000000 12252453157 011217 5 ustar root root openvdb/doc/ 0000755 0000000 0000000 00000000000 12252453247 011764 5 ustar root root openvdb/doc/faq.txt 0000644 0000000 0000000 00000031726 12252452022 013273 0 ustar root root /**
@page faq Frequently Asked Questions
@section Contents
- @ref sWhatIsVDB
- @ref sWhatLicense
- @ref sWhatCLA
- @ref sWhyUseVDB
- @ref sVersionNumbering
- @ref sGeneralizedOctree
- @ref sLevelSet
- @ref sCustomizeVDB
- @ref sAdaptiveGrid
- @ref sMeaningOfVDB
- @ref sAccessor
- @ref sValue
- @ref sState
- @ref sVoxel
- @ref sTile
- @ref sBackground
- @ref sThreadSafe
- @ref sMaxRes
- @ref sCompareVDB
- @ref sReplaceDense
- @ref sFuture
- @ref sContribute
@section sWhatIsVDB What is OpenVDB?
OpenVDB is a library comprising a compact hierarchical data structure and a
suite of tools for the efficient manipulation of sparse, possibly time-varying,
volumetric data discretized on a three-dimensional grid. It is based on
VDB, which was developed by Ken Museth at DreamWorks Animation, and it
offers an effectively infinite 3D index space, compact storage (both in
memory and on disk), fast data access (both random and sequential), and
a collection of algorithms specifically optimized for the data structure
for common tasks such as filtering, constructive solid geometry (CSG),
discretization of partial differential equations, voxelization of polygons,
skinning of particles, volumetric compositing and sampling. The technical
details of VDB are described in the paper
"VDB: High-Resolution Sparse Volumes with Dynamic Topology".
@section sWhatLicense What license is OpenVDB distributed under?
OpenVDB is released under the Mozilla Public License Version 2.0, which is a
free, open source, and detailed software license developed and maintained by
the Mozilla Foundation. It is characterized as a hybridization of the modified
BSD license and GNU General Public License (GPL) that seeks to balance the
concerns of proprietary and open source developers. For more information
about this license, see the
Mozilla FAQ.
@section sWhatCLA Is there a Contributor License Agreement for OpenVDB?
Yes, developers who wish to contribute code to be considered for inclusion
in the OpenVDB distribution must first complete this
Contributor License Agreement
and submit it to DreamWorks (directions are in the CLA).
@section sWhyUseVDB Why should I use OpenVDB?
The typical reasons to adopt OpenVDB are if you are storing sparse
data and/or if you are performing sparse computations. OpenVDB
is also effectively unbounded, which makes it very convenient for
applications where the topology of the data is dynamic or unknown. Unlike
many existing sparse data structures, OpenVDB is also optimized for
numerical simulations, multithreaded volume compositing, near real-time
boolean CSG operations, fast random and sequential data access and
voxelization of points and polygons.
@section sVersionNumbering What is the version numbering system for OpenVDB?
We currently use a major.minor.patch version numbering system,
and with every release of OpenVDB we change one or more of the three numbers.
The patch number is incremented for new features and bug fixes that change
neither the API nor the file format of the library nor the ABIs of the @c Grid
and @c Transform classes and their constituent classes.
The minor version is incremented for releases that change the API without
changing the @c Grid or @c Transform ABIs, and for releases that change
the file format in such a way that older files can still be read.
The major version is incremented when the @c Grid or @c Transform ABIs
change, or when the file format changes in a non-backward-compatible way
(which should be rare).
No release of OpenVDB guarantees ABI compatibility across the entire library,
but rather only for the @c Grid and @c Transform classes, and primarily
to allow third-party app plugins compiled against different versions of
the library to coexist and exchange data.
@section sCustomizeVDB Can I customize the configuration of OpenVDB?
Yes! OpenVDB is specifically developed to be highly customizable. That is,
the user can define the sizes of all the nodes at each level of a tree as
well as the number of tree levels and the data type of values stored in the
tree. However, note that since OpenVDB makes extensive use of C++
templating, configurations are fixed at compile time rather than at run
time. This is a fundamental design characteristic of OpenVDB, and it leads
to very high performance, thanks to techniques like inlining and template
metaprogramming.
@section sGeneralizedOctree Is OpenVDB merely a generalized octree or N-tree?
No! While OpenVDB can conceptually be configured as a (height-balanced)
octree, it is much more than an octree or N-tree. Whereas octrees and
N-trees have fixed branching factors of respectively two and N in each
coordinate direction, OpenVDB's branching factors typically vary between
tree levels and are only limited to be powers of two. To understand why
and also learn about other unique features of OpenVDB, refer to the
paper in ACM Transactions on Graphics.
@section sLevelSet Is OpenVDB primarily for level set applications?
No! Don't let the fact that OpenVDB can represent and operate so well on
level sets mislead you into thinking that it is limited to or even focusing
on this special type of sparse volumetric application. OpenVDB was developed
for general-purpose volumetric processing and numerical simulation, and we
have even had success using it for volumetric applications that traditionally
call for blocked or dense grids. However, the fact remains that narrow-band
level sets play an essential role in many volumetric applications, and this
explains why OpenVDB includes so many tools and algorithms specifically
for level sets.
@section sAdaptiveGrid Is OpenVDB an adaptive grid?
Let's first stress that the term "adaptive grid" is somewhat ambiguous. Some
use it to mean a grid that can store data sampled at adaptive voxel sizes
typically derived from a so-called "refinement oracle", whereas others mean
multiple grids with different fixed voxel sizes all sampling the same data.
An example of the former is an octree and of the latter is a mipmap. Since
OpenVDB stores both data values and child nodes at each level of the tree, it
is adaptive only in the first sense, not the second. The level of adaptivity
or refinement between the tree levels is defined by the branching factors of
the nodes, which are fixed at compile time.
@section sMeaningOfVDB What does "VDB" stand for?
Over the years VDB has been interpreted to mean different things, none of
which are very descriptive: "Voxel Data Base", "Volumetric Data Blocks",
"Volumetric Dynamic B+tree", etc. In early presentations of VDB we even used
a different name, "DB+Grid", which was abandoned to emphasize its
distinction from similarly named, but different, existing sparse data
structures like DT-Grid or DB-Grid. The simple truth is that "VDB" is just a
name. :-)
@section sAccessor Why are there no coordinate-based access methods on the grid?
It might surprise you that the @c Grid class doesn't directly provide access
to voxels via their @ijk coordinates. Instead, the recommended procedure
is to ask the grid for a "value accessor", which is an accelerator object
that performs bottom-up tree traversal using cached information from
previous traversals. Caching greatly improves performance, but it is
inherently not thread-safe. However, a single grid may have multiple
value accessors, so each thread can safely be assigned its own value accessor.
Uncached—and therefore slower, but thread-safe—random access is possible
through a grid's tree, for example with a call like
grid.tree().getValue(ijk).
@section sValue How and where does OpenVDB store values?
OpenVDB stores voxel data in a tree with a fixed maximum height (chosen
at compile time), with a root node that has a dynamic branching factor,
with internal nodes that have fixed branching factors, and with leaf nodes
of fixed dimensions. Values can be stored in nodes at all levels of
the tree. Values stored in leaf nodes correspond to individual voxels;
all other values correspond to "tiles" (see below).
@section sState What are active and inactive values?
Every value in a grid has a binary state that we refer to as its
"active state".
The interpretation of this binary state is application-specific, but
typically an active (on) value is "interesting" in some sense, and an
inactive (off) value is less interesting or uninteresting.
For example, the values in a narrow-band level set are all active, and
values outside the narrow band are all inactive.
Active states of values are stored in very compact bit masks that
support sparse iteration, so visiting all active (or inactive) values
in a grid can be done very efficiently.
@section sVoxel How are voxels represented in OpenVDB?
Values stored in nodes of type @c LeafNode (which, when they exist,
have a fixed depth), correspond to individual voxels.
These are the smallest addressable units of index space.
@section sTile What are tiles?
Values stored in nodes of type @c RootNode or @c InternalNode correspond to
regions of index space with a constant value and active state and with
power of two dimensions. We refer to such regions as "tiles". By
construction, tiles have no child nodes.
@section sBackground What is the background value?
A tree's background value is the value that is returned whenever
one accesses a region of index space that is not explicitly represented by
voxels or tiles in the tree. Thus, you can think of the background value
as the default value associated with an empty tree.
Note that the background value is always inactive!
@section sThreadSafe Is OpenVDB thread-safe?
Yes and no. If you're asking if OpenVDB can safely be used in a
multithreaded application then the answer is a resounding yes. In fact, many
of the tools included in OpenVDB are multithreaded (using Intel's Threading
Building Blocks library). However, like virtually all data structures that
employ caching and allocate-on-insert, certain operations in OpenVDB are not
thread-safe in the general sense. In particular, it is not safe to retrieve
voxels from a grid while another thread is inserting voxels.
For multithreaded insertion operations we typically assign a separate grid
to each thread and then merge the grids as threads terminate.
This technique works remarkably well: because OpenVDB
is sparse and hierarchical, merging is very efficient.
For more details, please consult the @subpage codeExamples and
Appendix B in the Transactions on Graphics paper.
@section sMaxRes Is OpenVDB unbounded?
Yes, to within available memory and the 32-bit precision of the coordinates
used to index voxels. And OpenVDB supports signed coordinates, unlike most
existing sparse data structures, so there are almost no restrictions on the
available grid resolution or the index range of OpenVDB grids.
@section sCompareVDB How does OpenVDB compare to existing sparse data structures?
OpenVDB is very different from existing sparse data structures that you are
likely to have heard of. Foremost it is hierarchical (unlike DT-Grid and
Field3D), supports simulations and dynamic topology (unlike GigaVoxels), is
effectively unbounded (unlike Field3D), and offers fast random and sequential
voxel access.
@section sReplaceDense Does OpenVDB replace dense grids?
This depends a lot on your application of dense grids and your configuration
of OpenVDB. Clearly, if you are storing or processing sparse data, OpenVDB
will offer a smaller memory footprint and faster (sparse) data processing.
However, even in some cases where both data and computation are dense, OpenVDB
can offer benefits like improved CPU cache performance due to its underlying
blocking and hierarchical tree structure. Exceptions are of course algorithms
that expect dense data to be laid out linearly in memory, or applications that
use very small grids. The simple truth is only a benchmark comparison can tell
you the preferred data structure, but for what it's worth it is our experience
that for the volumetric applications we encounter in production, OpenVDB is
almost always superior.
@section sFuture What future improvements to OpenVDB are planned?
We are continuing to expand the OpenVDB toolset and improve its performance.
In the near future we plan to add OpenVDB Maya plugins, Python bindings to
more library tools, support for accelerated particle storage and lookup, level
set morphing and measures (e.g. area and volume), more rendering options, a fluid
pressure solver, improved methods for particle skinning, asynchronous I/O,
optimization by means of SIMD instructions, out-of-core support, advection of
densities (vs. level sets) and much more. We would very much like to hear your
suggestions and preferences.
@section sContribute How can I contribute to OpenVDB?
If you have bug reports or ideas for improvements or new features,
please contact us! If you want to contribute code, please see our
License page.
*/
openvdb/doc/api_0_98_0.txt 0000644 0000000 0000000 00000020163 12252452022 014244 0 ustar root root /**
@page api_0_98_0 Porting to OpenVDB 0.98.0
Starting in OpenVDB 0.98.0, @vdblink::tree::Tree Tree@endlink and
@vdblink::math::Transform Transform@endlink objects (and
@vdblink::Grid Grid@endlink objects in the context of Houdini SOPs)
are passed and accessed primarily by reference
rather than by shared pointer.
(This is partly for performance reasons; the overhead of copying shared
pointers, especially in a threaded environment, can be significant.)
Furthermore, those objects now exhibit copy-on-write semantics, so that
in most cases it is no longer necessary to make explicit deep copies.
These changes were, for the most part, requested and implemented by
Side Effects.
Accessor methods like @vdblink::Grid::tree() Grid::tree@endlink,
@vdblink::Grid::transform() Grid::transform@endlink and
@c GEO_PrimVDB::getGrid that used to return shared pointers now return const
references.
Variants like @c Grid::constTree, which returned const shared
pointers, have been removed, and new read/write accessors have been added.
The latter, including @c Grid::treeRW(), @c Grid::transformRW() and
@c GEO_PrimVDB::getGridRW, return non-const references, but they also ensure
that ownership of the returned object is exclusive to the container
(that is, they ensure that the grid has exclusive ownership of the tree or
transform and that the primitive has exclusive ownership of the grid).
The logic is as follows: if a grid, for example, has sole ownership of a
tree, then @c Grid::treeRW() returns a non-const reference to that tree;
but if the tree is shared (with other grids, perhaps), then
@c Grid::treeRW() assigns the grid a new, deep copy of the tree
and returns a non-const reference to the new tree.
Shared pointers to @c Tree, @c Transform and @c Grid objects can still be
requested from their respective containers via @c Grid::sharedTree(),
@c Grid::sharedTransform() and @c GEO_PrimVDB::getSharedGrid,
but their use is now discouraged.
For Houdini SOPs, there are additional changes. First, VDB primitives are
now normally processed in-place. That is, rather than extract a
primitive's grid, make a deep copy of it and construct a new primitive to
hold the copy, one now requests read/write access to a primitive's grid and
then modifies the resulting grid. (SOPs that generate primitives or that
replace grids of one type with another type can still do so using the old
approach, however.)
Second, grid metadata such as a grid's class (level set, fog volume, etc.),
value type (@c float, @c vec3s, etc.), background value and so on (the full
list is hardcoded into @c GEO_PrimVDB) is now exposed via "intrinsic"
attributes of grid primitives, rather than via primitive attributes as before.
As a result, this information no longer appears in a SOP's geometry
spreadsheet, nor does any extra metadata that a SOP might add to a grid during
processing. The metadata is still preserved in the @c Grid objects, though.
Third, @c openvdb_houdini::processTypedGrid, which passes a shared grid
pointer to a functor that also takes a shared pointer, is now deprecated in
favor of @c openvdb_houdini::UTvdbProcessTypedGrid, which accepts shared
pointers, raw pointers or references to grids (provided that the functor
accepts an argument of the same kind). Below is a comparison of the old
and new usage in the typical case of a SOP that processes input grids:
Old API (0.97.0 and earlier)
@code
struct MyGridProcessor
{
template
void operator()(typename GridT::Ptr grid) const
{
// Process the grid's tree.
(1) grid->tree()->pruneInactive();
}
};
SOP_OpenVDB_Example::cookMySop(OP_Context& context)
{
...
duplicateSource(0, context);
// Process each VDB primitive that belongs to the selected group.
for (openvdb_houdini::VdbPrimIterator it(gdp, group); it; ++it) {
openvdb_houdini::GU_PrimVDB* vdbPrim = *it;
// Deep copy the primitive's grid if it is going to be modified.
openvdb_houdini::GridPtr grid = vdbPrim->getGrid()->deepCopyGrid();
// Otherwise, retrieve a read-only grid pointer.
//openvdb_houdini::GridCPtr grid = vdbPrim->getGrid();
// Process the grid.
MyGridProcessor proc;
(2) openvdb_houdini::processTypedGrid(grid, proc);
// Create a new VDB primitive that contains the modified grid,
// and in the output detail replace the original primitive with
// the new one.
(3) openvdb_houdini::replaceVdbPrimitive(*gdp, grid, *vdbPrim);
}
}
@endcode
New API (0.98.0 and later)
@code
struct MyGridProcessor {
template
void operator()(GridT& grid) const
{
// Request write access to the grid's tree, then process the tree.
(1) grid.treeRW().pruneInactive();
}
};
SOP_OpenVDB_Example::cookMySop(OP_Context& context)
{
...
duplicateSource(0, context);
// Process each VDB primitive that belongs to the selected group.
for (openvdb_houdini::VdbPrimIterator it(gdp, group); it; ++it) {
openvdb_houdini::GU_PrimVDB* vdbPrim = *it;
// Get write access to the grid associated with the primitive.
// If the grid is shared with other primitives, this will
// make a deep copy of it.
openvdb_houdini::Grid& grid = vdbPrim->getGridRW();
// If the grid is not going to be modified, get read-only access.
//const openvdb_houdini::Grid& grid = vdbPrim->getGrid();
// Process the grid.
MyGridProcessor proc;
(2) openvdb_houdini::UTvdbProcessTypedGrid(
(4) vdbPrim->getStorageType(), grid, proc);
}
}
@endcode
@b Notes
-
In the old API, @vdblink::Grid::tree() Grid::tree@endlink returned either
a shared, non-const pointer to a tree or a shared const pointer, depending
on whether the grid itself was non-const or const. In the new API,
@vdblink::Grid::tree() Grid::tree@endlink always returns a const reference,
and @c Grid::treeRW() always returns a non-const reference.
-
@c openvdb_houdini::processTypedGrid (old API) accepts only shared pointers
to grids (or shared pointers to const grids), together with functors that
accept shared pointers to grids. @c openvdb_houdini::UTvdbProcessTypedGrid
(new API) accepts const and non-const references, shared pointers and raw
pointers, together with matching functors. That is, all of the following
are valid pairs of grid and functor arguments to @c UTvdbProcessTypedGrid():
@code
openvdb_houdini::Grid& grid = vdbPrim->getGridRW();
struct MyProc { template operator()(GridT&) {...} };
const openvdb_houdini::Grid& grid = vdbPrim->getGrid();
struct MyProc { template operator()(const GridT&) {...} };
openvdb_houdini::GridPtr grid = vdbPrim->getSharedGrid();
struct MyProc { template operator()(typename GridT::Ptr) {...} };
openvdb_houdini::GridCPtr grid = vdbPrim->getSharedConstGrid();
struct MyProc { template operator()(typename GridT::ConstPtr) {...} };
openvdb_houdini::Grid* grid = &vdbPrim->getGridRW();
struct MyProc { template operator()(GridT*) {...} };
const openvdb_houdini::Grid* grid = &vdbPrim->getGrid();
struct MyProc { template operator()(const GridT*) {...} };
@endcode
-
In the old API, input grid primitives were (usually) deleted after
processing, and a new primitive was created for each output grid.
@c openvdb_houdini::replaceVdbPrimitive() did both operations in one step
and had the side effect of transferring the output grid's metadata to
primitive attributes. In the new API, @c replaceVdbPrimitive() is rarely
needed, because input grids can usually be processed in-place, and
most commonly-used metadata is exposed via intrinsic attributes, which
don't need to be manually updated.
-
The first argument to @c openvdb_houdini::UTvdbProcessTypedGrid() is the
grid's storage type, which can be retrieved either by passing the grid
to @c openvdb_houdini::UTvdbGetGridType() or by calling
@c GEO_PrimVDB::getStorageType() on the primitive.
*/
openvdb/doc/python.txt 0000644 0000000 0000000 00000042175 12252452022 014045 0 ustar root root /**
@page python Using OpenVDB in Python
This section describes the OpenVDB Python module and includes Python code
snippets and some complete programs that illustrate how to perform common tasks.
(An API reference is also available,
if Epydoc is installed.)
As of OpenVDB 2.0, the Python module exposes most of the functionality
of the C++ @vdblink::Grid Grid@endlink class, including I/O, metadata
management, voxel access and iteration, but almost none of the many
@ref secToolUtils "tools".
We expect to add support for tools in forthcoming releases.
The Python module supports a fixed set of grid types.
If the symbol @c PY_OPENVDB_WRAP_ALL_GRID_TYPES is defined at compile time,
most of the grid types declared in openvdb.h are accessible in Python,
otherwise only @b FloatGrid, @b BoolGrid and @b Vec3SGrid are accessible.
To add support for grids with other value types or configurations,
search for @c PY_OPENVDB_WRAP_ALL_GRID_TYPES in the module source code,
update the code as appropriate and recompile the module.
(It is possible that this process will be streamlined in the future
with a plugin mechanism.)
Note however that adding grid types can significantly increase the time
and memory needed to compile the module and can significantly increase
the size of the resulting executable.
In addition, grids of custom types that are saved to .vdb files
or pickled will not be readable by clients using the standard version
of the module.
Also note that the @vdblink::tree::Tree Tree@endlink class is not exposed
in Python.
Much of its functionality is either available through the
@vdblink::Grid Grid@endlink or is too low-level to be generally useful
in Python.
Although trees are not accessible in Python, they can of course be operated on
indirectly.
Of note are the grid methods @b copy, which returns a new grid that shares
its tree with the original grid, @b deepCopy, which returns a new grid
that owns its own tree, and @b sharesWith, which reports whether two grids
share a tree.
@section Contents
- @ref sPyBasics
- @ref sPyHandlingMetadata
- @ref sPyAccessors
- @ref sPyIteration
- @ref sPyNumPy
- @ref sPyCppAPI
@section sPyBasics Getting started
The following example is a complete program that illustrates some of the
basic steps to create grids and write them to disk:
@code{.py}
import pyopenvdb as vdb
# A grid comprises a sparse tree representation of voxel data,
# user-supplied metadata and a voxel space to world space transform,
# which defaults to the identity transform.
# A FloatGrid stores one single-precision floating point value per voxel.
# Other grid types include BoolGrid and Vec3SGrid. The module-level
# attribute pyopenvdb.GridTypes gives the complete list.
cube = vdb.FloatGrid()
cube.fill(min=(100, 100, 100), max=(199, 199, 199), value=1.0)
# Name the grid "cube".
cube.name = 'cube'
# Populate another FloatGrid with a sparse, narrow-band level set
# representation of a sphere with radius 50 voxels, located at
# (1.5, 2, 3) in index space.
sphere = vdb.createLevelSetSphere(radius=50, center=(1.5, 2, 3))
# Associate some metadata with the grid.
sphere['radius'] = 50.0
# Associate a scaling transform with the grid that sets the voxel size
# to 0.5 units in world space.
sphere.transform = vdb.createLinearTransform(voxelSize=0.5)
# Name the grid "sphere".
sphere.name = 'sphere'
# Write both grids to a VDB file.
vdb.write('mygrids.vdb', grids=[cube, sphere])
@endcode
This example shows how to read grids from files, and some ways to modify
grids:
@code{.py}
import pyopenvdb as vdb
# Read a .vdb file and return a list of grids populated with
# their metadata and transforms, but not their trees.
filename = 'mygrids.vdb'
grids = vdb.readAllGridMetadata(filename)
# Look for and read in a level-set grid that has certain metadata.
sphere = None
for grid in grids:
if (grid.gridClass == vdb.GridClass.LEVEL_SET and 'radius' in grid
and grid['radius'] > 10.0):
sphere = vdb.read(filename, grid.name)
else:
print 'skipping grid', grid.name
if sphere:
# Convert the level set sphere to a narrow-band fog volume, in which
# interior voxels have value 1, exterior voxels have value 0, and
# narrow-band voxels have values varying linearly from 0 to 1.
outside = sphere.background
width = 2.0 * outside
# Visit and update all of the grid's active values, which correspond to
# voxels in the narrow band.
for iter in sphere.iterOnValues():
dist = iter.value
iter.value = (outside - dist) / width
# Visit all of the grid's inactive tile and voxel values and update
# the values that correspond to the interior region.
for iter in sphere.iterOffValues():
if iter.value < 0.0:
iter.value = 1.0
iter.active = False
# Set exterior voxels to 0.
sphere.background = 0.0
sphere.gridClass = vdb.GridClass.FOG_VOLUME
@endcode
@section sPyHandlingMetadata Handling metadata
Metadata of various types (string, bool, int, float, and 2- and 3-element
sequences of ints or floats) can be attached both to individual grids
and to files on disk, either by supplying a Python dictionary of
(name, value) pairs or, in the case of grids, through
a dictionary-like interface.
Add (name, value) metadata pairs to a grid as you would to a dictionary.
A new value overwrites an existing value if the name matches an existing name.
@code{.py}
>>> import pyopenvdb as vdb
>>> grid = vdb.Vec3SGrid()
>>> grid['vector'] = 'gradient'
>>> grid['radius'] = 50.0
>>> grid['center'] = (10, 15, 10)
>>> grid.metadata
{'vector': 'gradient', 'radius': 50.0, 'center': (10, 15, 10)}
>>> grid['radius']
50.0
>>> 'radius' in grid, 'misc' in grid
(True, False)
# OK to overwrite an existing value with a value of another type:
>>> grid['center'] = 0.0
# A 4-element sequence is not a supported metadata value type:
>>> grid['center'] = (0, 0, 0, 0)
File "", line 1, in
TypeError: metadata value "(0, 0, 0, 0)" of type tuple is not allowed
# Metadata names must be strings:
>>> grid[0] = (10.5, 15, 30)
File "", line 1, in
TypeError: expected str, found int as argument 1 to __setitem__()
@endcode
Alternatively, replace all or some of a grid’s metadata by supplying
a (name, value) dictionary:
@code{.py}
>>> metadata = {
... 'vector': 'gradient',
... 'radius': 50.0,
... 'center': (10, 15, 10)
... }
# Replace all of the grid's metadata.
>>> grid.metadata = metadata
>>> metadata = {
... 'center': [10.5, 15, 30],
... 'scale': 3.14159
... }
# Overwrite "center" and add "scale":
>>> grid.updateMetadata(metadata)
@endcode
Iterate over a grid’s metadata as you would over a dictionary:
@code{.py}
>>> for key in grid:
... print '%s = %s' % (key, grid[key])
...
vector = gradient
radius = 50.0
scale = 3.14159
center = (10.5, 15.0, 30.0)
@endcode
Removing metadata is also straightforward:
@code{.py}
>>> del grid['vector']
>>> del grid['center']
>>> del grid['vector'] # error: already removed
File "", line 1, in
KeyError: 'vector'
>>> grid.metadata = {} # remove all metadata
@endcode
Some grid metadata is exposed in the form of properties, either because
it might be frequently accessed (a grid’s name, for example)
or because its allowed values are somehow restricted:
@code{.py}
>>> grid = vdb.createLevelSetSphere(radius=10.0)
>>> grid.metadata
{'class': 'level set'}
>>> grid.gridClass = vdb.GridClass.FOG_VOLUME
>>> grid.metadata
{'class': 'fog volume'}
# The gridClass property requires a string value:
>>> grid.gridClass = 123
File "", line 1, in
TypeError: expected str, found int as argument 1 to setGridClass()
# Only certain strings are recognized; see pyopenvdb.GridClass
# for the complete list.
>>> grid.gridClass = 'Hello, world.'
>>> grid.metadata
{'class': 'unknown'}
>>> grid.metadata = {}
>>> grid.vectorType = vdb.VectorType.COVARIANT
>>> grid.metadata
{'vector_type': 'covariant'}
>>> grid.name = 'sphere'
>>> grid.creator = 'Python'
>>> grid.metadata
{'vector_type': 'covariant', 'name': 'sphere', 'creator': 'Python'}
@endcode
Setting these properties to @c None removes the corresponding metadata,
but the properties retain default values:
@code{.py}
>>> grid.creator = grid.vectorType = None
>>> grid.metadata
{'name': 'sphere'}
>>> grid.creator, grid.vectorType
('', 'invariant')
@endcode
Metadata can be associated with a .vdb file at the time the file
is written, by supplying a (name, value) dictionary
as the optional @c metadata argument to the @b write function:
@code{.py}
>>> metadata = {
... 'creator': 'Python',
... 'time': '11:05:00'
... }
>>> vdb.write('mygrids.vdb', grids=grid, metadata=metadata)
@endcode
File-level metadata can be retrieved with either the @b readMetadata
function or the @b readAll function:
@code{.py}
>>> metadata = vdb.readMetadata('mygrids.vdb')
>>> metadata
{'creator': 'Python', 'time': '11:05:00'}
>>> grids, metadata = vdb.readAll('mygrids.vdb')
>>> metadata
{'creator': 'Python', 'time': '11:05:00'}
>>> [grid.name for grid in grids]
['sphere']
@endcode
@section sPyAccessors Voxel access
Grids provide read-only and read/write accessors for voxel lookup via @ijk
index coordinates.
Accessors store references to their parent grids, so a grid will not
be deleted while it has accessors in use.
@code{.py}
>>> import pyopenvdb as vdb
# Read two grids from a file.
>>> grids, metadata = vdb.readAll('smoke2.vdb')
>>> [grid.name for grid in grids]
['density', 'v']
# Get read/write accessors to the two grids.
>>> dAccessor = grids[0].getAccessor()
>>> vAccessor = grids[1].getAccessor()
>>> ijk = (100, 103, 101)
>>> dAccessor.probeValue(ijk)
(0.17614534497261047, True)
# Change the value of a voxel.
>>> dAccessor.setValueOn(ijk, 0.125)
>>> dAccessor.probeValue(ijk)
(0.125, True)
>>> vAccessor.probeValue(ijk)
((-2.90625, 9.84375, 0.84228515625), True)
# Change the active state of a voxel.
>>> vAccessor.setActiveState(ijk, False)
>>> vAccessor.probeValue(ijk)
((-2.90625, 9.84375, 0.84228515625), False)
# Get a read-only accessor to one of the grids.
>>> dAccessor = grids[0].getConstAccessor()
>>> dAccessor.setActiveState(ijk, False)
File "", line 1, in
TypeError: accessor is read-only
# Delete the accessors once they are no longer needed,
# so that the grids can be garbage-collected.
>>> del dAccessor, vAccessor
@endcode
@section sPyIteration Iteration
Grids provide read-only and read/write iterators over their values.
Iteration is over sequences of value objects (BoolGrid.Values,
FloatGrid.Values, etc.) that expose properties such as the number
of voxels spanned by a value (one, for a voxel value, more than one
for a tile value), its coordinates and its active state.
Value objects returned by read-only iterators are immutable; those
returned by read/write iterators permit assignment to their active state
and value properties, which modifies the underlying grid.
Value objects store references to their parent grids, so a grid will not
be deleted while one of its value objects is in use.
@code{.py}
>>> import pyopenvdb as vdb
>>> grid = vdb.read('smoke2.vdb', gridname='v')
>>> grid.__class__.__name__
'Vec3SGrid'
# Iterate over inactive values and print the coordinates of the first
# five voxel values and the bounding boxes of the first five tile values.
>>> voxels = tiles = 0
... N = 5
... for item in grid.citerOffValues(): # read-only iterator
... if voxels == N and tiles == N:
... break
... if item.count == 1:
... if voxels < N:
... voxels += 1
... print 'voxel', item.min
... else:
... if tiles < N:
... tiles += 1
... print 'tile', item.min, item.max
...
tile (0, 0, 0) (7, 7, 7)
tile (0, 0, 8) (7, 7, 15)
tile (0, 0, 16) (7, 7, 23)
tile (0, 0, 24) (7, 7, 31)
tile (0, 0, 32) (7, 7, 39)
voxel (40, 96, 88)
voxel (40, 96, 89)
voxel (40, 96, 90)
voxel (40, 96, 91)
voxel (40, 96, 92)
# Iterate over and normalize all active values.
>>> from math import sqrt
>>> for item in grid.iterOnValues(): # read/write iterator
... vector = item.value
... magnitude = sqrt(sum(x * x for x in vector))
... item.value = [x / magnitude for x in vector]
...
@endcode
For some operations, it might be more convenient to use one of
the grid methods @b mapOn, @b mapOff or @b mapAll.
These methods iterate over a grid’s tiles and voxels
(active, inactive or both, respectively) and replace each value x
with f(x), where f is a callable object.
These methods are not multithreaded.
@code{.py}
>>> import pyopenvdb as vdb
>>> from math import sqrt
>>> grid = vdb.read('smoke2.vdb', gridname='v')
>>> def normalize(vector):
... magnitude = sqrt(sum(x * x for x in vector))
... return [x / magnitude for x in vector]
...
>>> grid.mapOn(normalize)
@endcode
Similarly, the @b combine method iterates over corresponding pairs of values
(tile and voxel) of two grids @e A and @e B of the same type
(FloatGrid, Vec3SGrid, etc.), replacing values in @e A
with f(a, b), where f is a callable object.
This operation assumes that index coordinates @ijk in both grids correspond
to the same physical, world space location.
Also, the operation always leaves grid @e B empty.
@code{.py}
>>> import pyopenvdb as vdb
>>> density = vdb.read('smoke2.vdb', gridname='density')
>>> density.__class__.__name__
'FloatGrid'
>>> sphere = vdb.createLevelSetSphere(radius=50.0, center=(100, 300, 100))
>>> density.combine(sphere, lambda a, b: min(a, b))
@endcode
For now, @b combine operates only on tile and voxel values,
not on their active states or other attributes.
@section sPyNumPy Working with NumPy arrays
Large data sets are often handled in Python using
NumPy.
The OpenVDB Python module can optionally be compiled with NumPy support.
With NumPy enabled, the @b copyFromArray and @b copyToArray grid methods
can be used to exchange data efficiently between scalar-valued grids
and three-dimensional NumPy arrays and between vector-valued grids and
four-dimensional NumPy arrays.
@code{.py}
>>> import pyopenvdb as vdb
>>> import numpy
>>> array = numpy.random.rand(200, 200, 200)
>>> array.dtype
dtype('float64')
# Copy values from a three-dimensional array of doubles
# into a grid of floats.
>>> grid = vdb.FloatGrid()
>>> grid.copyFromArray(array)
>>> grid.activeVoxelCount() == array.size
True
>>> grid.evalActiveVoxelBoundingBox()
((0, 0, 0), (199, 199, 199))
# Copy values from a four-dimensional array of ints
# into a grid of float vectors.
>>> vecarray = numpy.ndarray((60, 70, 80, 3), int)
>>> vecarray.fill(42)
>>> vecgrid = vdb.Vec3SGrid()
>>> vecgrid.copyFromArray(vecarray)
>>> vecgrid.activeVoxelCount() == 60 * 70 * 80
True
>>> vecgrid.evalActiveVoxelBoundingBox()
((0, 0, 0), (59, 69, 79))
@endcode
When copying from a NumPy array, values in the array that are equal to
the destination grid’s background value (or close to it, if the
@c tolerance argument to @b copyFromArray is nonzero) are set to the
background value and are marked inactive.
All other values are marked active.
@code{.py}
>>> grid.clear()
>>> grid.copyFromArray(array, tolerance=0.2)
>>> print '%d%% of copied voxels are active' % (
... round(100.0 * grid.activeVoxelCount() / array.size))
80% of copied voxels are active
@endcode
The optional @c ijk argument specifies the index coordinates of the voxel
in the destination grid into which to start copying values.
That is, array index (0, 0, 0) maps to voxel @ijk.
@code{.py}
>>> grid.clear()
>>> grid.copyFromArray(array, ijk=(-1, 2, 3))
>>> grid.evalActiveVoxelBoundingBox()
((-1, 2, 3), (198, 201, 202))
@endcode
The @b copyToArray method also accepts an @c ijk argument.
It specifies the index coordinates of the voxel to be copied to array
index (0, 0, 0).
@code{.py}
>>> grid = vdb.createLevelSetSphere(radius=10.0)
>>> array = numpy.ndarray((40, 40, 40), int)
>>> array.fill(0)
# Copy values from a grid of floats into
# a three-dimensional array of ints.
>>> grid.copyToArray(array, ijk=(-15, -20, -35))
>>> array[15, 20]
array([ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 2, 1, 0, -1, -2, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3])
@endcode
@b copyToArray has no @c tolerance argument, because there is no distinction
between active and inactive values in the destination array.
@section sPyCppAPI C++ glue routines
Python objects of type @b FloatGrid, @b Vec3SGrid, etc. are backed by
C structs that “inherit” from @c PyObject.
The OpenVDB Python extension module includes public functions that you can
call in your own extension modules to convert between
@vdblink::Grid openvdb::Grids@endlink and PyObjects.
See the pyopenvdb.h reference for a description of these functions
and a usage example.
Your extension module might need to link against the OpenVDB extension module
in order to access these functions.
On UNIX systems, it might also be necessary to specify the @c RTLD_GLOBAL
flag when importing the OpenVDB module, to allow its symbols to be shared
across modules.
See @b setdlopenflags in the Python @b sys module for one way to do this.
*/
openvdb/doc/changes.txt 0000644 0000000 0000000 00000150442 12252452022 014131 0 ustar root root /**
@page changes Release Notes
@htmlonly @endhtmlonly
@par
Version 2.1.0 - December 12, 2013
- Added a small number of Maya nodes, primarily for conversion of geometry
to and from OpenVDB volumes and for visualization of volumes.
- Added an initial implementation of
@vdblink::tools::LevelSetMorphing level set morphing@endlink
(with improvements to follow soon).
- Added @vdblink::tools::LevelSetMeasure tools::LevelSetMeasure@endlink,
which efficiently computes the surface area, volume and average
mean-curvature of narrow-band level sets, in both world and voxel units.
Those quantities are now exposed as intrinsic attributes on the Houdini
VDB primitive and can be queried using the native Measure SOP.
- @vdblink::tools::Dense tools::Dense@endlink now supports the XYZ memory
layout used by Houdini and Maya in addition to the ZYX layout used in
OpenVDB trees.
- Improved the performance of masking in the
@vdblink::tools::LevelSetFilter level set filter@endlink tool and
added inversion and scaling of the mask input, so that any scalar-valued
volume can be used as a mask, not just volumes with a [0, 1] range.
- Added optional masking to the non-level-set filters, to the grid
operators (CPT, curl, divergence, gradient, Laplacian, mean curvature,
magnitude, and normalize) and to the Analysis and Filter SOPs.
- Added more narrow band controls to the Rebuild Level Set SOP.
- Improved the accuracy of the
@vdblink::tools::levelSetRebuild() level set rebuild@endlink tool.
- Added @vdblink::tools::activate() tools::activate@endlink and
@vdblink::tools::deactivate() tools::deactivate@endlink, which set the
active states of tiles and voxels whose values are equal to or approximately
equal to a given value, and added a Deactivate Background Voxels toggle
to the Combine SOP.
- Added @vdblink::math::BBox::applyMap() BBox::applyMap@endlink and
@vdblink::math::BBox::applyInverseMap() BBox::applyInverseMap@endlink,
which allow for transformation of axis-aligned bounding boxes.
- Added a @vdblink::tools::PositionShader position shader@endlink to the
level set ray-tracer (primarily for debugging purposes).
- Added an @vdblink::io::Queue io::Queue@endlink class that manages a
concurrent queue for asynchronous serialization of grids to files or streams.
- Fixed a bug in @vdblink::io::Archive io::Archive@endlink whereby writing
unnamed, instanced grids (i.e., grids sharing a tree) to a file rendered
the file unreadable.
- Fixed a bug in the @vdblink::tools::VolumeToMesh volume to mesh@endlink
converter that caused it to generate invalid polygons when the zero crossing
lay between active and inactive regions.
- Fixed a bug in the @vdblink::tools::UniformPointScatter point scatter@endlink
tool (and the Scatter SOP) whereby the last voxel always remained empty.
- Fixed a bug in the Read SOP that caused grids with the same name
to be renamed with a numeric suffix (e.g., “grid[1]”
“grid[2]”, etc.).
- Fixed some unit test failures on 64-bit Itanium machines.
@par
API changes:
- The @vdblink::tools::Filter Filter@endlink tool is now templated on a
mask grid, and threading is controlled using a grain size, for consistency
with most of the other level set tools.
- The @vdblink::tools::LevelSetFilter LevelSetFilter@endlink tool is now
templated on a mask grid.
- All shaders now take a ray direction instead of a ray.
@htmlonly @endhtmlonly
@par
Version 2.0.0 - October 31, 2013
- Added a @ref python "Python module" with functions for basic manipulation
of grids (but no tools, yet).
- Added ray intersector tools for efficient, hierarchical intersection
of rays with @vdblink::tools::LevelSetRayIntersector level-set@endlink
and @vdblink::tools::VolumeRayIntersector generic@endlink volumes.
- Added a @vdblink::math::Ray Ray@endlink class and a hierarchical
@vdblink::math::DDA Digital Differential Analyzer@endlink for fast
ray traversal.
- Added a fully multi-threaded @vdblink::tools::LevelSetRayTracer
level set ray tracer@endlink and
@vdblink::tools::PerspectiveCamera camera@endlink
@vdblink::tools::OrthographicCamera classes@endlink
that mimic Houdini’s cameras.
- Added a simple, command-line renderer (currently for level sets only).
- Implemented a new meshing scheme that produces topologically robust
two-manifold meshes and is twice as fast as the previous scheme.
- Implemented a new, topologically robust (producing two-manifold meshes)
level-set-based seamless fracture scheme. The new scheme eliminates
visible scarring seen in the previous implementation by subdividing
internal, nonplanar quads near fracture seams. In addition,
fracture seam points are now tagged, allowing them to be used
to drive pre-fracture dynamics such as local surface buckling.
- Improved the performance of @vdblink::tree::Tree::evalActiveVoxelBoundingBox()
Tree::evalActiveVoxelBoundingBox@endlink and
@vdblink::tree::Tree::activeVoxelCount() Tree::activeVoxelCount@endlink,
and significantly improved the performance of
@vdblink::tree::Tree::evalLeafBoundingBox() Tree::evalLeafBoundingBox@endlink
(by about 30x).
- Added a tool (and a Houdini SOP) that fills a volume with
adaptively-sized overlapping or non-overlapping spheres.
- Added a Ray SOP that can be used to perform geometry projections
using level-set ray intersections or closest-point queries.
- Added a @vdblink::tools::ClosestSurfacePoint tool@endlink that performs
accelerated closest surface point queries from arbitrary points in
world space to narrow-band level sets.
- Increased the speed of masked level set filtering by 20% for
the most common cases.
- Added @vdblink::math::BoxStencil math::BoxStencil@endlink, with support
for trilinear interpolation and gradient computation.
- Added @vdblink::tree::Tree::topologyIntersection()
Tree::topologyIntersection@endlink, which intersects a tree’s active
values with those of another tree, and
@vdblink::tree::Tree::topologyDifference() Tree::topologyDifference@endlink,
which performs topological subtraction of one tree’s active values
from another’s. In both cases, the ValueTypes of the two
trees need not be the same.
- Added @vdblink::tree::Tree::activeTileCount() Tree::activeTileCount@endlink,
which returns the number of active tiles in a tree.
- Added @vdblink::math::MinIndex() math::MinIndex@endlink and
@vdblink::math::MaxIndex() math::MaxIndex@endlink, which find the minimum
and maximum components of a vector without any branching.
- Added @vdblink::math::BBox::minExtent() BBox::minExtent@endlink,
which returns a bounding box’s shortest axis.
- The default @vdblink::math::BBox BBox@endlink constructor now
generates an invalid bounding box rather than an empty bounding box
positioned at the origin. The new behavior is consistent with
@vdblink::math::CoordBBox CoordBBox@endlink.
[Thanks to Rick Hankins for suggesting this fix.]
- Added @vdblink::math::CoordBBox::reset() CoordBBox::reset@endlink,
which resets a bounding box to its initial, invalid state.
- Fixed a bug in the default @vdblink::math::ScaleMap ScaleMap@endlink
constructor that left some data used in the inverse uninitialized.
- Added @vdblink::math::MapBase::applyJT MapBase::applyJT@endlink, which
applies the Jacobian transpose to a vector (the Jacobian transpose takes
a range-space vector to a domain-space vector, e.g., world to index),
and added @vdblink::math::MapBase::inverseMap() MapBase::inverseMap@endlink,
which returns a new map representing the inverse of the original map
(except for @vdblink::math::NonlinearFrustumMap NonlinearFrustumMap@endlink,
which does not currently have a defined inverse map).
@b Note: Houdini 12.5 uses an earlier version of OpenVDB, and maps
created with that version lack virtual table entries for these
new methods, so do not call these methods from Houdini 12.5.
- Reimplemented @vdblink::math::RandomInt math::RandomInt@endlink using
Boost.Random instead of @c rand() (which is not thread-safe), and deprecated
@vdblink::math::randUniform math::randUniform@endlink and added
@vdblink::math::Random01 math::Random01@endlink to replace it.
- Modified @vdblink::tools::copyFromDense() tools::copyFromDense@endlink
and @vdblink::tools::copyToDense() tools::copyToDense@endlink to allow
for implicit type conversion (e.g., between a
@vdblink::tools::Dense Dense<Int32>@endlink and a
@vdblink::FloatTree FloatTree@endlink) and fixed several bugs
in @vdblink::tools::CopyFromDense tools::CopyFromDense@endlink.
- Fixed bugs in @vdblink::math::Stats math::Stats@endlink and
@vdblink::math::Histogram math::Histogram@endlink that could produce
NaNs or other incorrect behavior if certain methods were called
on populations of size zero.
- Renamed struct tolerance to
@vdblink::math::Tolerance math::Tolerance@endlink
and @c negative to @vdblink::math::negative() math::negative@endlink
and removed @c math::toleranceValue().
- Implemented a closest point on line segment algorithm,
@vdblink::math::closestPointOnSegmentToPoint()
math::closestPointOnSegmentToPoint@endlink.
- Fixed meshing issues relating to masking and automatic partitioning.
- @vdblink::Grid::merge() Grid::merge@endlink and
@vdblink::tree::Tree::merge() Tree::merge@endlink now accept an optional
@vdblink::MergePolicy MergePolicy@endlink argument that specifies one of
three new merging schemes. (The old merging scheme, which is no longer
available, used logic for each tree level that was inconsistent with
the other levels and that could result in active tiles being replaced
with nodes having only inactive values.)
- Renamed @c LeafNode::coord2offset(), @c LeafNode::offset2coord() and
@c LeafNode::offset2globalCoord() to
@vdblink::tree::LeafNode::coordToOffset() coordToOffset@endlink,
@vdblink::tree::LeafNode::offsetToLocalCoord() offsetToLocalCoord@endlink,
and @vdblink::tree::LeafNode::offsetToGlobalCoord()
offsetToGlobalCoord@endlink, respectively, and likewise for
@vdblink::tree::InternalNode::offsetToGlobalCoord() InternalNode@endlink.
[Thanks to Rick Hankins for suggesting this change.]
- Replaced @vdblink::tree::Tree Tree@endlink methods @c setValueOnMin,
@c setValueOnMax and @c setValueOnSum with
@vdblink::tools::setValueOnMin() tools::setValueOnMin@endlink,
@vdblink::tools::setValueOnMax() tools::setValueOnMax@endlink and
@vdblink::tools::setValueOnSum() tools::setValueOnSum@endlink
(and a new @vdblink::tools::setValueOnMult() tools::setValueOnMult@endlink)
and added @vdblink::tree::Tree::modifyValue() Tree::modifyValue@endlink
and @vdblink::tree::Tree::modifyValueAndActiveState()
Tree::modifyValueAndActiveState@endlink, which modify voxel values
in-place via user-supplied functors. Similarly, replaced
@c ValueAccessor::setValueOnSum() with
@vdblink::tree::ValueAccessor::modifyValue()
ValueAccessor::modifyValue@endlink
and @vdblink::tree::ValueAccessor::modifyValueAndActiveState()
ValueAccessor::modifyValueAndActiveState@endlink, and added a
@vdblink::tree::TreeValueIteratorBase::modifyValue() modifyValue@endlink
method to all value iterators.
- Removed @c LeafNode::addValue and @c LeafNode::scaleValue.
- Added convenience classes @vdblink::tree::Tree3 tree::Tree3@endlink and
@vdblink::tree::Tree5 tree::Tree5@endlink for custom tree configurations.
- Added an option to the From Particles SOP to generate an alpha mask,
which can be used to constrain level set filtering so as to preserve
surface details.
- The mesh to volume converter now handles point-degenerate polygons.
- Fixed a bug in the Level Set Smooth, Level Set Renormalize and
Level Set Offset SOPs that caused the group name to be ignored.
- Fixed various OS X and Windows build issues.
[Contributions from SESI and DD]
@htmlonly @endhtmlonly
@par
Version 1.2.0 - June 28 2013
- @vdblink::tools::LevelSetFilter Level set filters@endlink now accept
an optional alpha mask grid.
- Implemented sharp feature extraction for level set surfacing.
This enhances the quality of the output mesh and reduces aliasing
artifacts.
- Added masking options to the meshing tools, as well as a spatial
multiplier for the adaptivity threshold, automatic partitioning,
and the ability to preserve edges and corners when mesh adaptivity
is applied.
- The mesh to volume attribute transfer scheme now takes surface
orientation into account, which improves accuracy in proximity to
edges and corners.
- Added a @vdblink::tree::LeafManager::foreach() foreach@endlink method
to @vdblink::tree::LeafManager tree::LeafManager@endlink that, like
@vdblink::tools::foreach() tools::foreach@endlink, applies a user-supplied
functor to each leaf node in parallel.
- Rewrote the particle to level set converter, simplifying the API,
improving performance (especially when particles have a fixed radius),
adding the capability to transfer arbitrary point attributes,
and fixing a velocity trail bug.
- Added utility methods @vdblink::math::Sign() Sign@endlink,
@vdblink::math::SignChange() SignChange@endlink,
@vdblink::math::isApproxZero() isApproxZero@endlink,
@vdblink::math::Cbrt() Cbrt@endlink and
@vdblink::math::ZeroCrossing() ZeroCrossing@endlink to math/Math.h.
- Added a @vdblink::tree::ValueAccessor3::probeNode() probeNode@endlink method
to the value accessor and to tree nodes that returns a pointer to the node
that contains a given voxel.
- Deprecated @c LeafNode::addValue and @c LeafNode::scaleValue.
- Doubled the speed of the mesh to volume converter (which also improves
the performance of the fracture and level set rebuild tools) and
improved its inside/outside voxel classification near edges and corners.
- @vdblink::tools::GridSampler GridSampler@endlink now accepts either a grid,
a tree or a value accessor, and it offers faster index-based access methods
and much better performance in cases where many instances are allocated.
- Extended @vdblink::tools::Dense tools::Dense@endlink to make it more
compatible with existing tools.
- Fixed a crash in @vdblink::io::Archive io::Archive@endlink whenever
the library was unloaded from memory and then reloaded.
[Contributed by Ollie Harding]
- Fixed a bug in @c GU_PrimVDB::buildFromPrimVolume(), seen during the
conversion from Houdini volumes to OpenVDB grids, that could cause
signed flood fill to be applied to non-level set grids, resulting in
active tiles with incorrect values.
- Added a Prune SOP with several pruning schemes.
@htmlonly @endhtmlonly
@par
Version 1.1.1 - May 10 2013
- Added a simple @vdblink::tools::Dense dense grid class@endlink and tools
to copy data from dense voxel arrays into OpenVDB grids and vice-versa.
- Starting with Houdini 12.5.396, plugins built with this version
of OpenVDB can coexist with native Houdini OpenVDB nodes.
- The level set fracture tool now smooths seam line edges during
mesh extraction, eliminating staircase artifacts.
- Significantly improved the performance of the
@vdblink::util::leafTopologyIntersection()
leafTopologyIntersection@endlink and
@vdblink::util::leafTopologyDifference() leafTopologyDifference@endlink
utilities and added a @vdblink::tree::LeafNode::topologyDifference()
LeafNode::topologyDifference@endlink method.
- Added convenience functions that provide simplified interfaces
to the @vdblink::tools::meshToLevelSet() mesh to volume@endlink
and @vdblink::tools::volumeToMesh() volume to mesh@endlink converters.
- Added a @vdblink::tools::accumulate() tools::accumulate@endlink function
that is similar to @vdblink::tools::foreach() tools::foreach@endlink
but can be used to accumulate the results of computations over the values
of a grid.
- Added @vdblink::tools::statistics() tools::statistics@endlink,
@vdblink::tools::opStatistics() tools::opStatistics@endlink and
@vdblink::tools::histogram() tools::histogram@endlink, which efficiently
compute statistics (mean, variance, etc.) and histograms of grid values
(using @vdblink::math::Stats math::Stats@endlink and
@vdblink::math::Histogram math::Histogram@endlink).
- Modified @vdblink::math::CoordBBox CoordBBox@endlink to adhere to
TBB’s splittable type requirements, so that, for example,
a @c CoordBBox can be used as a blocked iteration range.
- Added @vdblink::tree::Tree::addTile() Tree::addTile@endlink,
@vdblink::tree::Tree::addLeaf() Tree::addLeaf@endlink and
@vdblink::tree::Tree::stealNode() Tree::stealNode@endlink, for fine
control over tree construction.
- Addressed a numerical stability issue when performing Gaussian
filtering of level set grids.
- Changed the return type of @vdblink::math::CoordBBox::volume()
CoordBBox::volume@endlink to reduce the risk of overflow.
- When the input mesh is self-intersecting, the mesh to volume converter
now produces a level set with a monotonic gradient field.
- Fixed a threading bug in the mesh to volume converter that caused it
to produce different results for the same input.
- Fixed a bug in the particle to level set converter that prevented
particles with zero velocity from being rasterized in Trail mode.
- Added an optional input to the Create SOP into which to merge
newly-created grids.
- Fixed a bug in the Resample SOP that caused it to produce incorrect
narrow-band widths when resampling level set grids.
- Fixed a bug in the To Polygons SOP that caused intermittent crashes
when the optional reference input was connected.
- Fixed a bug in the Advect Level Set SOP that caused a crash
when the velocity input was connected but empty.
- The Scatter and Sample Point SOPs now warn instead of erroring
when given empty grids.
- Fixed a crash in @c vdb_view when stepping through multiple grids
after changing render modes.
- @c vdb_view can now render fog volumes and vector fields, and it now
features interactively adjustable clipping planes that enable
one to view the interior of a volume.
@htmlonly @endhtmlonly
@par
Version 1.1.0 - April 4 2013
- The @vdblink::tools::resampleToMatch() resampleToMatch@endlink tool,
the Resample SOP and the Combine SOP now use level set rebuild to correctly
and safely resample level sets. Previously, scaling a level set would
invalidate the signed distance field, leading to holes and other artifacts.
- Added a mask-based topological
@vdblink::tools::erodeVoxels erosion tool@endlink, and rewrote and
simplified the @vdblink::tools::dilateVoxels dilation tool@endlink.
- The @vdblink::tools::LevelSetAdvection LevelSetAdvection@endlink tool
can now advect forward or backward in time.
- @vdblink::tree::Tree::pruneLevelSet() Tree::pruneLevelSet@endlink now
replaces each pruned node with a tile having the inside or outside
background value, instead of arbitrarily selecting one of the node’s
tile or voxel values.
- When a grid is saved to a file with
@vdblink::Grid::saveFloatAsHalf() saveFloatAsHalf@endlink set to @c true,
the grid’s background value is now also quantized to 16 bits.
(Not quantizing the background value caused a mismatch with the values
of background tiles.)
- As with @vdblink::tools::foreach() tools::foreach@endlink, it is now
possible to specify whether functors passed to
@vdblink::tools::transformValues() tools::transformValues@endlink
should be shared across threads.
- @vdblink::tree::LeafManager tree::LeafManager@endlink can now be
instantiated with a @const tree, although buffer swapping with @const trees
is disabled.
- Added a @vdblink::Grid::signedFloodFill() Grid::signedFloodFill@endlink
overload that allows one to specify inside and outside values.
- Fixed a bug in @vdblink::Grid::setBackground() Grid::setBackground@endlink
so that now only the values of inactive voxels change.
- Fixed @vdblink::Grid::topologyUnion() Grid::topologyUnion@endlink so that
it actually unions tree topology, instead of just the active states
of tiles and voxels. The previous behavior broke multithreaded code
that relied on input and output grids having compatible tree topology.
- @vdblink::math::Transform math::Transform@endlink now includes an
@vdblink::math::Transform::isIdentity() isIdentity@endlink predicate
and methods to @vdblink::math::Transform::preMult(const Mat4d&) pre-@endlink
and @vdblink::math::Transform::postMult(const Mat4d&) postmultiply@endlink
by a matrix.
- Modified the @link NodeMasks.h node mask@endlink classes to permit
octree-like tree configurations (i.e., with a branching factor of two)
and to use 64-bit operations instead of 32-bit operations.
- Implemented a new, more efficient
@vdblink::math::closestPointOnTriangleToPoint() closest point
on triangle@endlink algorithm.
- Implemented a new vertex normal scheme in the volume to mesh
converter, and resolved some overlapping polygon issues.
- The volume to mesh converter now meshes not just active voxels
but also active tiles.
- Fixed a bug in the mesh to volume converter that caused unsigned
distance field conversion to produce empty grids.
- Fixed a bug in the level set fracture tool whereby the cutter overlap
toggle was ignored.
- Fixed an infinite loop bug in @c vdb_view.
- Updated @c vdb_view to use the faster and less memory-intensive
OpenVDB volume to mesh converter instead of marching cubes,
and rewrote the shader to be OpenGL 3.2 and GLSL 1.2 compatible.
- Given multiple input files or a file containing multiple grids,
@c vdb_view now displays one grid at a time. The left and right
arrow keys cycle between grids.
- The To Polygons SOP now has an option to associate the input grid’s
name with each output polygon.
@htmlonly @endhtmlonly
@par
Version 1.0.0 - March 14 2013
- @vdblink::tools::levelSetRebuild() tools::levelSetRebuild@endlink
now throws an exception when given a non-scalar or non-floating-point grid.
- The tools in tools/GridOperators.h are now interruptible, as is
the Analysis SOP.
- Added a
@vdblink::tree::LeafManager::LeafRange::Iterator leaf node iterator@endlink
and a TBB-compatible
@vdblink::tree::LeafManager::LeafRange range class@endlink
to the LeafManager.
- Modified the @vdblink::tools::VolumeToMesh VolumeToMesh@endlink tool
to handle surface topology issues around fracture seam lines.
- Modified the Makefile to allow @c vdb_view to compile on OS X systems
(provided that GLFW is available).
- Fixed a bug in the Create SOP that resulted in "invalid parameter name"
warnings.
- The Combine SOP now optionally resamples the A grid into the B grid’s
index space (or vice-versa) if the A and B transforms differ.
- The Vector Split and Vector Merge SOPs now skip inactive voxels
by default, but they can optionally be made to include inactive voxels,
as they did before.
- The @vdblink::tools::LevelSetFracture LevelSetFracture@endlink tool now
supports custom rotations for each cutter instance, and the Fracture SOP
now uses quaternions to generate uniformly-distributed random rotations.
@htmlonly @endhtmlonly
@par
Version 0.104.0 - February 15 2013
- Added a @vdblink::tools::levelSetRebuild() tool@endlink and a SOP
to rebuild a level set from any scalar volume.
- @c .vdb files are now saved using a mask-based compression scheme
that is an order of magnitude faster than Zip and produces comparable
file sizes for level set and fog volume grids. (Zip compression
is still enabled by default for other classes of grids).
- The @vdblink::tools::Filter Filter@endlink and
@vdblink::tools::LevelSetFilter LevelSetFilter@endlink tools now
include a Gaussian filter, and mean (box) filtering is now 10-50x faster.
- The isosurface @vdblink::tools::VolumeToMesh meshing tool@endlink
is now more robust (to level sets with one voxel wide narrow bands,
for example).
- Mesh to volume conversion is on average 1.5x faster and up to 5.5x
faster for high-resolution meshes where the polygon/voxel size ratio
is small.
- Added @vdblink::createLevelSet() createLevelSet@endlink and
@vdblink::createLevelSetSphere() createLevelSetSphere@endlink
factory functions for level set grids.
- @vdblink::tree::ValueAccessor tree::ValueAccessor@endlink is now faster
for trees of height 2, 3 and 4 (the latter is the default), and it now
allows one to specify, via a template argument, the number of node levels
to be cached, which can also improve performance in special cases.
- Added a toggle to @vdblink::tools::foreach() tools::foreach@endlink
to specify whether or not the functor should be shared across threads.
- Added @vdblink::Mat4SMetadata Mat4s@endlink and
@vdblink::Mat4DMetadata Mat4d@endlink metadata types.
- Added explicit pre- and postmultiplication methods to the @c Transform,
@c Map and @c Mat4 classes and deprecated the old accumulation methods.
- Modified @vdblink::math::NonlinearFrustumMap NonlinearFrustumMap@endlink
to be more compatible with Houdini’s frustum transform.
- Fixed a @vdblink::tools::GridTransformer GridTransformer@endlink bug
that caused it to translate the output grid incorrectly in some cases.
- Fixed a bug in the tree-level
@vdblink::tree::LeafIteratorBase LeafIterator@endlink that resulted in
intermittent crashes in
@vdblink::tools::dilateVoxels() tools::dilateVoxels@endlink.
- The @c Hermite data type and Hermite grids are no longer supported.
- Added tools/GridOperators.h, which includes new, cleaner implementations
of the @vdblink::tools::cpt() closest point transform@endlink,
@vdblink::tools::curl() curl@endlink,
@vdblink::tools::divergence() divergence@endlink,
@vdblink::tools::gradient() gradient@endlink,
@vdblink::tools::laplacian() Laplacian@endlink,
@vdblink::tools::magnitude() magnitude@endlink,
@vdblink::tools::meanCurvature() mean curvature@endlink and
@vdblink::tools::normalize() normalize@endlink tools.
- Interrupt support has been improved in several tools, including
@vdblink::tools::ParticlesToLevelSet tools::ParticlesToLevelSet@endlink.
- Simplified the API of the @vdblink::math::BaseStencil Stencil@endlink class
and added an @vdblink::math::BaseStencil::intersects() intersects@endlink
method to test for intersection with a specified isovalue.
- Renamed @c voxelDimensions to @c voxelSize in transform classes
and elsewhere.
- Deprecated @c houdini_utils::ParmFactory::setChoiceList in favor of
@c houdini_utils::ParmFactory::setChoiceListItems, which requires
a list of token, label string pairs.
- Made various changes for Visual C++ compatibility.
[Contributed by SESI]
- Fixed a bug in @c houdini_utils::getNodeChain() that caused the
Offset Level Set, Smooth Level Set and Renormalize Level Set SOPs
to ignore frame changes.
[Contributed by SESI]
- The From Particles SOP now provides the option to write into
an existing grid.
- Added a SOP to edit grid metadata.
- The Fracture SOP now supports multiple cutter objects.
- Added a To Polygons SOP that complements the Fracture SOP and allows
for elimination of seam lines, generation of correct vertex normals
and grouping of polygons when surfacing fracture fragments, using
the original level set or mesh as a reference.
@htmlonly @endhtmlonly
@par
Version 0.103.1 - January 15 2013
- @vdblink::tree::ValueAccessor tree::ValueAccessor@endlink read operations
are now faster for four-level trees.
(Preliminary benchmark tests suggest a 30-40% improvement.)
- For vector-valued grids, @vdblink::tools::compMin() tools::compMin@endlink
and @vdblink::tools::compMax() tools::compMax@endlink now compare
vector magnitudes instead of individual components.
- Migrated grid sampling code to a new file, Interpolation.h,
and deprecated old files and classes.
- Added a level-set @vdblink::tools::LevelSetFracture fracture tool@endlink
and a Fracture SOP.
- Added @vdblink::tools::sdfInteriorMask() tools::sdfInteriorMask@endlink,
which creates a mask of the interior region of a level set grid.
- Fixed a bug in the mesh to volume converter that produced unexpected
nonzero values for voxels at the intersection of two polygons,
and another bug that produced narrow-band widths that didn’t respect
the background value when the half-band width was less than three voxels.
- @c houdini_utils::ParmFactory can now correctly generate ramp multi-parms.
- Made various changes for Visual C++ compatibility.
[Contributed by SESI]
- The Convert SOP can now convert between signed distance fields and
fog volumes and from volumes to meshes.
[Contributed by SESI]
- For level sets, the From Mesh and From Particles SOPs now match
the reference grid’s narrow-band width.
- The Scatter SOP can now optionally scatter points in the interior
of a level set.
@htmlonly @endhtmlonly
@par
Version 0.103.0 - December 21 2012
- The mesh to volume converter is now 60% faster at generating
level sets with wide bands, and the From Mesh SOP is now interruptible.
- Fixed a threading bug in the recently-added
@vdblink::tools::compReplace() compReplace@endlink tool
that caused it to produce incorrect output.
- Added a @vdblink::tree::Tree::probeConstLeaf() probeConstLeaf@endlink
method to the @vdblink::tree::Tree::probeConstLeaf() Tree@endlink,
@vdblink::tree::ValueAccessor::probeConstLeaf() ValueAccessor@endlink
and @vdblink::tree::RootNode::probeConstLeaf() node@endlink classes.
- The Houdini VDB primitive doesn’t create a @c name attribute
unnecessarily (i.e., if its grid’s name is empty), but it now
correctly allows the name to be changed to the empty string.
- Fixed a crash in the Vector Merge SOP when fewer than three grids
were merged.
- The From Particles SOP now features a "maximum half-width" parameter
to help avoid runaway computations.
@htmlonly @endhtmlonly
@par
Version 0.102.0 - December 13 2012
- Added @vdblink::tools::compReplace() tools::compReplace@endlink,
which copies the active values of one grid into another, and added
a "Replace A With Active B" mode to the Combine SOP.
- @vdblink::Grid::signedFloodFill() Grid::signedFloodFill@endlink
no longer enters an infinite loop when filling an empty grid.
- Fixed a bug in the particle to level set converter that sometimes
produced level sets with holes, and fixed a bug in the SOP that
could result in random output.
- Fixed an issue in the frustum preview feature of the Create SOP
whereby rendering very large frustums could cause high CPU usage.
- Added streamline support to the constrained advection scheme
in the Advect Points SOP.
- Added an Advect Level Set SOP.
@htmlonly @endhtmlonly
@par
Version 0.101.1 - December 11 2012 (DWA internal release)
- Partially reverted the Houdini VDB primitive’s grid accessor methods
to their pre-0.98.0 behavior. A primitive’s grid can once again
be accessed by shared pointer, but now also by reference.
Accessor methods for grid metadata have also been added, and the
primitive now ensures that metadata and transforms are never shared.
- Fixed an intermittent crash in the From Particles SOP.
@htmlonly @endhtmlonly
@par
Version 0.101.0 - December 6 2012 (DWA internal release)
- Partially reverted the @vdblink::Grid Grid@endlink’s
@vdblink::Grid::tree() tree@endlink and
@vdblink::Grid::transform() transform@endlink accessor methods
to their pre-0.98.0 behavior, eliminating copy-on-write but
preserving their return-by-reference semantics. These methods
are now supplemented with a suite of
@vdblink::Grid::treePtr() shared@endlink
@vdblink::Grid::baseTreePtr() pointer@endlink
@vdblink::Grid::transformPtr() accessors@endlink.
- Restructured the @vdblink::tools::MeshToVolume
mesh to volume converter@endlink for a 40% speedup
and to be more robust to non-manifold geometry, to better preserve
sharp features, to support arbitrary tree configurations and
to respect narrow-band limits.
- Added a @c getNodeBoundingBox method to
@vdblink::tree::RootNode::getNodeBoundingBox() RootNode@endlink,
@vdblink::tree::InternalNode::getNodeBoundingBox() InternalNode@endlink
and @vdblink::tree::LeafNode::getNodeBoundingBox() LeafNode@endlink
that returns the index space spanned by a node.
- Made various changes for Visual C++ compatibility.
[Contributed by SESI]
- Renamed the Reshape Level Set SOP to Offset Level Set.
- Fixed a crash in the Convert SOP and added support for conversion
of empty grids.
@htmlonly @endhtmlonly
@par
Version 0.100.0 - November 30 2012 (DWA internal release)
- Greatly improved the performance of the level set to fog volume
@vdblink::tools::sdfToFogVolume() converter@endlink.
- Improved the performance of the
@vdblink::tools::Filter::median() median filter@endlink
and of level set @vdblink::tools::csgUnion() CSG@endlink operations.
- Reintroduced
@vdblink::tree::Tree::pruneLevelSet() Tree::pruneLevelSet@endlink,
a specialized @vdblink::tree::Tree::pruneInactive() pruneInactive@endlink
for level-set grids.
- Added utilities to the @c houdini_utils library to facilitate the
collection of a chain of adjacent nodes of a particular type
so that they can be cooked in a single step. (For example,
adjacent @c xform SOPs could be collapsed by composing their
transformation matrices into a single matrix.)
- Added pruning and flood-filling options to the Convert SOP.
- Reimplemented the Filter SOP, omitting level-set-specific filters
and adding node chaining (to reduce memory usage when applying
several filters in sequence).
- Added a toggle to the Read SOP to read grid metadata and
transforms only.
- Changed the attribute transfer scheme on the From Mesh and
From Particles SOPs to allow for custom grid names and
vector type metadata.
@htmlonly @endhtmlonly
@par
Version 0.99.0 - November 21 2012
- Added @vdblink::Grid Grid@endlink methods that return non-const
tree and transform references without triggering deep copies,
as well as @c const methods that return @c const shared pointers.
- Added @c Grid methods to @vdblink::Grid::addStatsMetadata populate@endlink
a grid’s metadata with statistics like the active voxel count, and to
@vdblink::Grid::getStatsMetadata retrieve@endlink that metadata.
By default, statistics are now computed and added to grids
whenever they are written to .vdb files.
- Added @vdblink::io::File::readGridMetadata io::File::readGridMetadata@endlink
and @vdblink::io::File::readAllGridMetadata
io::File::readAllGridMetadata@endlink methods to read just the
grid metadata and transforms from a .vdb file.
- Fixed numerical precision issues in the
@vdblink::tools::csgUnion csgUnion@endlink,
@vdblink::tools::csgIntersection csgIntersection@endlink
and @vdblink::tools::csgDifference csgDifference@endlink
tools, and added toggles to optionally disable postprocess pruning.
- Fixed an issue in @c vdb_view with the ordering of GL vertex buffer calls.
[Contributed by Bill Katz]
- Fixed an intermittent crash in the
@vdblink::tools::ParticlesToLevelSet ParticlesToLevelSet@endlink tool,
as well as a race condition that could cause data corruption.
- The @c ParticlesToLevelSet tool and From Particles SOP can now transfer
arbitrary point attribute values from the input particles to output voxels.
- Fixed a bug in the Convert SOP whereby the names of primitives
were lost during conversion, and another bug that resulted in
arithmetic errors when converting empty grids.
- Fixed a bug in the Combine SOP that caused the Operation selection
to be lost.
@htmlonly @endhtmlonly
@par
Version 0.98.0 - November 16 2012
- @vdblink::tree::Tree Tree@endlink and
@vdblink::math::Transform Transform@endlink objects (and
@vdblink::Grid Grid@endlink objects in the context of Houdini SOPs)
are now passed and accessed primarily by reference rather than by
shared pointer. See @subpage api_0_98_0 "Porting to OpenVDB 0.98.0"
for details about this important API change.
[Contributed by SESI]
- Reimplemented @vdblink::math::CoordBBox CoordBBox@endlink to address several
off-by-one bugs related to bounding box dimensions.
- Fixed an off-by-one bug in @vdblink::Grid::evalActiveVoxelBoundingBox()
evalActiveVoxelBoundingBox@endlink.
- Introduced the @vdblink::tree::LeafManager LeafManager@endlink class,
which will eventually replace the @c LeafArray class. @c LeafManager supports
dynamic buffers stored as a structure of arrays (SOA), unlike @c LeafArray,
which supports only static buffers stored as an array of structures (AOS).
- Improved the performance of the
@vdblink::tools::LevelSetFilter LevelSetFilter@endlink and
@vdblink::tools::LevelSetTracker LevelSetTracker@endlink tools by rewriting
them to use the new @vdblink::tree::LeafManager LeafManager@endlink class.
- Added @vdblink::tree::Tree::setValueOnly() Tree::setValueOnly@endlink and
@vdblink::tree::ValueAccessor::setValueOnly()
ValueAccessor::setValueOnly@endlink methods, which change the value of
a voxel without changing its active state, and
@vdblink::tree::Tree::probeLeaf() Tree::probeLeaf@endlink and
@vdblink::tree::ValueAccessor::probeLeaf() ValueAccessor::probeLeaf@endlink
methods that return the leaf node that contains a given voxel (unless
the voxel is represented by a tile).
- Added a @vdblink::tools::LevelSetAdvection LevelSetAdvection@endlink tool
that propagates and tracks narrow-band level sets.
- Introduced a new @vdblink::tools::GridSampler GridSampler@endlink class
that supports world-space (or index-space) sampling of grid values.
- Changed the interpretation of the
@vdblink::math::NonlinearFrustumMap NonlinearFrustumMap@endlink’s
@em taper parameter to be the ratio of the near and far plane depths.
- Added a @c ParmFactory::setChoiceList() overload that accepts
(@em token, @em label) string pairs, and a @c setDefault() overload that
accepts an STL string.
- Fixed a crash in the Combine SOP in Copy B mode.
- Split the Level Set Filter SOP into three separate SOPs,
Level Set Smooth, Level Set Reshape and Level Set Renormalize.
When two or more of these nodes are connected in sequence, they interact
to reduce memory usage: the last node in the sequence performs
all of the operations in one step.
- The Advect Points SOP can now output polyline streamlines
that trace the paths of the points.
- Added an option to the Analysis SOP to specify names for output grids.
- Added camera-derived frustum transform support to the Create SOP.
@htmlonly @endhtmlonly
@par
Version 0.97.0 - October 18 2012
- Added a narrow-band @vdblink::tools::LevelSetTracker level set
interface tracking tool@endlink (up to fifth-order in space but currently
only first-order in time, with higher temporal orders to be added soon).
- Added a @vdblink::tools::LevelSetFilter level set filter tool@endlink
to perform unrestricted surface smoothing (e.g., Laplacian flow),
filtering (e.g., mean value) and morphological operations (e.g.,
morphological opening).
- Added adaptivity to the @vdblink::tools::VolumeToMesh
level set meshing tool@endlink for faster mesh extraction with fewer
polygons, without postprocessing.
- Added a @vdblink::tree::ValueAccessor::touchLeaf()
ValueAccessor::touchLeaf@endlink method that creates (if necessary)
and returns the leaf node containing a given voxel. It can be used
to preallocate leaf nodes over which to run parallel algorithms.
- Fixed a bug in @vdblink::Grid::merge() Grid::merge@endlink whereby
active tiles were sometimes lost.
- Added @vdblink::tree::LeafManager LeafManager@endlink, which is similar
to @c LeafArray but supports a dynamic buffer count and allocates buffers
more efficiently. Useful for temporal integration (e.g., for level set
propagation and interface tracking), @c LeafManager is meant to replace
@c LeafArray, which will be deprecated in the next release.
- Added a @vdblink::tree::LeafNode::fill() LeafNode::fill@endlink method
to efficiently populate leaf nodes with constant values.
- Added a @vdblink::tree::Tree::visitActiveBBox() Tree::visitActiveBBox@endlink
method that applies a functor to the bounding boxes of all active tiles
and leaf nodes and that can be used to improve the performance of
ray intersection tests, rendering of bounding boxes, etc.
- Added a @vdblink::tree::Tree::voxelizeActiveTiles()
Tree::voxelizeActiveTiles@endlink method to densify active tiles.
While convenient and fast, this can produce large dense grids, so use
it with caution.
- Repackaged @c Tree::pruneLevelSet() as a
@vdblink::tree::Tree::pruneOp() Tree::pruneOp()@endlink-compatible
functor. @vdblink::tree::LevelSetPrune LevelSetPrune@endlink is a
specialized @vdblink::tree::Tree::pruneInactive() pruneInactive@endlink
for level-set grids and is used in interface tracking.
- Added a @vdblink::GridBase::pruneGrid() GridBase::pruneGrid@endlink method.
- Added a @vdblink::Grid::hasUniformVoxels() Grid:hasUniformVoxels@endlink
method.
- Renamed @c tools::dilate to
@vdblink::tools::dilateVoxels() dilateVoxels@endlink and improved its
performance. The new name reflects the fact that the current
implementation ignores active tiles.
- Added a @vdblink::tools::resampleToMatch() tools::resampleToMatch@endlink
function that resamples an input grid into an output grid with a
different transform such that, after resampling, the input and output grids
coincide, but the output grid’s transform is preserved.
- Significantly improved the performance of depth-bounded value
iterators (@vdblink::tree::Tree::ValueOnIter ValueOnIter@endlink,
@vdblink::tree::Tree::ValueAllIter ValueAllIter@endlink, etc.)
when the depth bound excludes leaf nodes.
- Exposed the value buffers inside leaf nodes with
@vdblink::tree::LeafNode::buffer() LeafNode::buffer@endlink.
This allows for very fast access (const and non-const) to voxel
values using linear array offsets instead of @ijk coordinates.
- In openvdb_houdini/UT_VDBTools.h, added operators for use with
@c processTypedGrid that resample grids in several different ways.
- Added a policy mechanism to @c houdini_utils::OpFactory that allows for
customization of operator names, icons, and Help URLs.
- Renamed many of the Houdini SOPs to make the names more consistent.
- Added an Advect Points SOP.
- Added a Level Set Filter SOP that allows for unrestricted surface
deformations, unlike the older Filter SOP, which restricts surface
motion to the initial narrow band.
- Added staggered vector sampling to the Sample Points SOP.
- Added a minimum radius threshold to the particle voxelization tool
and SOP.
- Merged the Composite and CSG SOPs into a single Combine SOP.
- Added a tool and a SOP to efficiently generate narrow-band level set
representations of spheres.
- In the Visualize SOP, improved the performance of tree topology
generation, which is now enabled by default.
@htmlonly @endhtmlonly
@par
Version 0.96.0 - September 24 2012
- Fixed a memory corruption bug in the mesh voxelizer tool.
- Temporarily removed the optional clipping feature from the level set mesher.
- Added "Staggered Vector Field" to the list of grid classes in the Create SOP.
@htmlonly @endhtmlonly
@par
Version 0.95.0 - September 20 2012
- Added a quad @vdblink::tools::VolumeToMesh meshing@endlink tool for
higher-quality level set meshing and updated the Visualizer SOP
to use it.
- Fixed a precision error in the @vdblink::tools::MeshToVolume
mesh voxelizer@endlink and improved the quality of inside/outside
voxel classification. Output grids are now also
@vdblink::Grid::setGridClass() classified@endlink as either level sets
or fog volumes.
- Modified the @vdblink::tools::GridResampler GridResampler@endlink
to use the signed flood fill optimization only on grids that are
tagged as level sets.
- Added a @vdblink::math::Quat quaternion@endlink class to the
math library and a method to return the
@vdblink::math::Mat3::trace trace@endlink of a @c Mat3.
- Fixed a bug in the
@vdblink::tree::ValueAccessor::ValueAccessor(const ValueAccessor&)
ValueAccessor@endlink copy constructor that caused the copy to reference
the original.
- Fixed a bug in @vdblink::tree::RootNode::setActiveState()
RootNode::setActiveState@endlink that caused a crash
when marking a (virtual) background voxel as inactive.
- Added a @c Tree::pruneLevelSet method that is similar to but faster than
@vdblink::tree::Tree::pruneInactive() pruneInactive@endlink
for level set grids.
- Added fast leaf node voxel access
@vdblink::tree::LeafNode::getValue(Index) const methods@endlink
that index by linear offset (as returned by
@vdblink::tree::LeafNode::ValueOnIter::pos() ValueIter::pos@endlink)
instead of by @ijk coordinates.
- Added a @vdblink::tree::Tree::touchLeaf() Tree::touchLeaf@endlink
method that can be used to preallocate a static tree topology over which
to safely perform multithreaded processing.
- Added a grain size argument to @c LeafArray for finer control of parallelism.
- Modified the Makefile to make it easier to omit the doc,
@c vdb_test and @c vdb_view targets.
- Added utility functions (in houdini/UT_VDBUtils.h) to convert
between Houdini and OpenVDB matrix and vector types.
[Contributed by SESI]
- Added accessors to @c GEO_PrimVDB that make it easier to directly access
voxel data and that are used by the HScript volume expression functions
in Houdini 12.5. [Contributed by SESI]
- As of Houdini 12.1.77, the native transform SOP operates on OpenVDB
primitives. [Contributed by SESI]
- Added a Convert SOP that converts OpenVDB grids to Houdini volumes
and vice-versa.
@htmlonly @endhtmlonly
@par
Version 0.94.1 - September 7 2012
- Fixed bugs in @vdblink::tree::RootNode RootNode@endlink and
@vdblink::tree::InternalNode InternalNode@endlink @c setValue*() and
@c fill() methods that could cause neighboring voxels to become inactive.
- Fixed a bug in
@vdblink::tree::Tree::hasSameTopology() Tree::hasSameTopology@endlink
that caused false positives when only active states and not values differed.
- Added a @vdblink::tree::Tree::hasActiveTiles() Tree::hasActiveTiles@endlink
method.
- For better cross-platform consistency, substituted bitwise AND operations
for right shifts in the @vdblink::tree::ValueAccessor ValueAccessor@endlink
hash key computation.
- @c vdb_view no longer aborts when asked to surface a vector-valued
grid—but it still doesn’t render the surface.
- Made various changes for Visual C++ compatibility.
[Contributed by SESI]
- Added an option to the MeshVoxelizer SOP to convert both open and
closed surfaces to unsigned distance fields.
- The Filter SOP now allows multiple filters to be applied in
user-specified order.
@htmlonly @endhtmlonly
@par
Version 0.94.0 - August 30 2012
- Added a @vdblink::Grid::topologyUnion() method@endlink to union
just the active states of voxels from one grid with those of
another grid of a possibly different type.
- Fixed an incorrect scale factor in the Laplacian diffusion
@vdblink::tools::Filter::laplacian() filter@endlink.
- Fixed a bug in @vdblink::tree::Tree::merge() Tree::merge@endlink
that could leave a tree with invalid value accessors.
- Added @vdblink::tree::TreeValueIteratorBase::setActiveState()
TreeValueIteratorBase::setActiveState@endlink and deprecated
@c setValueOn.
- Removed @c tools/FastSweeping.h. It will be replaced with a much more
efficient implementation in the near future.
- ZIP compression of .vdb files is now
@vdblink::io::File::setCompressionEnabled() optional@endlink,
but enabled by default. [Contributed by SESI]
- Made various changes for Clang and Visual C++ compatibility.
[Contributed by SESI]
- The MeshVoxelizer SOP can now transfer arbitrary point and primitive
attribute values from the input mesh to output voxels.
@htmlonly @endhtmlonly
@par
Version 0.93.0 - August 24 2012
- Renamed symbols in math/Operators.h to avoid ambiguities that
GCC 4.4 reports as errors.
- Simplified the API for the stencil version of the
closest-point transform @vdblink::math::CPT operator@endlink.
- Added logic to
@vdblink::io::Archive::readGrid() io::Archive::readGrid@endlink
to set the grid name metadata from the descriptor if the metadata
doesn’t already exist.
- Added guards to prevent nesting of @c openvdb_houdini::Interrupter::start()
and @c end() calls.
@htmlonly @endhtmlonly
@par
Version 0.92.0 - August 23 2012
- Added a Laplacian diffusion
@vdblink::tools::Filter::laplacian() filter@endlink.
- Fixed a bug in the initialization of the sparse contour tracer
that caused mesh-to-volume conversion to fail in certain cases.
- Fixed a bug in the curvature stencil that caused mean curvature
filtering to produce wrong results.
- Increased the speed of the
@vdblink::tools::GridTransformer GridTransformer@endlink
by as much as 20% for fog volumes.
- Added optional pruning to the Resample SOP.
- Modified the PointSample SOP to allow it to work with ungrouped,
anonymous grids.
- Fixed a crash in the LevelSetNoise SOP.
@htmlonly @endhtmlonly
@par
Version 0.91.0 - August 16 2012
- @vdblink::tools::GridTransformer tools::GridTransformer@endlink
and @vdblink::tools::GridResampler tools::GridResampler@endlink
now correctly (but not yet efficiently) process tiles in sparse grids.
- Added an optional @vdblink::CopyPolicy CopyPolicy@endlink argument
to @vdblink::GridBase::copyGrid() GridBase::copyGrid@endlink
and to @vdblink::Grid::copy() Grid::copy@endlink that specifies
whether and how the grid’s tree should be copied.
- Added a @vdblink::GridBase::newTree() GridBase::newTree@endlink
method that replaces a grid’s tree with a new, empty tree of the
correct type.
- Fixed a crash in
@vdblink::tree::Tree::setValueOff(const Coord& xyz, const ValueType& value)
Tree::setValueOff@endlink when the new value was equal to the
background value.
- Fixed bugs in @vdblink::tree::Tree::prune() Tree::prune@endlink
that could result in output tiles with incorrect active states.
- Added @c librt to the link dependencies to address build failures
on Ubuntu systems.
- Made various small changes to the Makefile and the source code
that should help with Mac OS X compatibility.
- The Composite and Resample SOPs now correctly copy the input grid’s
metadata to the output grid.
@htmlonly @endhtmlonly
@par
Version 0.90.1 - August 7 2012
- Fixed a bug in the
@vdblink::math::BBox::getCenter() BBox::getCenter()@endlink method.
- Added missing header files to various files.
- @vdblink::io::File::NameIterator::gridName()
io::File::NameIterator::gridName()@endlink now returns a unique name
of the form "name[1]", "name[2]", etc. if a file
contains multiple grids with the same name.
- Fixed a bug in the Writer SOP that caused grid names to be discarded.
- The Resample SOP now correctly sets the background value of the
output grid.
@htmlonly @endhtmlonly
@par
Version 0.90.0 - August 3 2012 (initial public release)
- Added a basic GL viewer for OpenVDB files.
- Greatly improved the performance of two commonly-used @c Tree methods,
@vdblink::tree::Tree::evalActiveVoxelBoundingBox()
evalActiveVoxelBoundingBox()@endlink
and @vdblink::tree::Tree::memUsage() memUsage()@endlink.
- Eliminated the @c GridMap class. File I/O now uses STL containers
of grid pointers instead.
- Refactored stencil-based tools (Gradient, Laplacian, etc.) and rewrote
some of them for generality and better performance. Most now behave
correctly for grids with nonlinear index-to-world transforms.
- Added a @link FiniteDifference.h library@endlink of index-space finite
difference operators.
- Added a @vdblink::math::Hermite "Hermite"@endlink grid type that compactly
stores each voxel’s upwind normals and can be used to convert volumes
to and from polygonal meshes.
- Added a @link PointScatter.h tool@endlink (and a Houdini SOP)
to scatter points randomly throughout a volume.
*/
openvdb/doc/codingstyle.txt 0000644 0000000 0000000 00000027065 12252452022 015051 0 ustar root root /**
@page codingStyle Coding Style
@section Introduction
This document details the coding practices that are used in the OpenVDB
codebase. Contributed code should conform to these guidelines to maintain
consistency and maintainability. If there is a rule that you would like
clarified, changed, or added, please send a note to openvdb@gmail.com.
@section Contents
- @ref sNamingConventions
- @ref sNamespaceConventions
- @ref sClassConventions
- @ref sClassMethods
- @ref sClassInstanceVariables
- @ref sClassStaticVariables
- @ref sLocalVariablesAndArguments
- @ref sConstants
- @ref sEnumerationNames
- @ref sEnumerationValues
- @ref sTypedefs
- @ref sGlobalVariables
- @ref sGlobalFunctions
- @ref sBooleans
- @ref sPractices
- @ref sGeneral
- @ref sFormatting
- @ref sIncludeStatements
- @ref sComments
- @ref sPrimitiveTypes
- @ref sMacros
- @ref sClasses
- @ref sConditionalStatements
- @ref sNamespaces
- @ref sExceptions
- @ref sTemplates
- @ref sMiscellaneous
@section sNamingConventions Naming Conventions
@subsection sNamespaceConventions Namespaces
-# Lowercase words, keep simple and short (e.g., @c tools, @c tree).
@subsection sClassConventions Classes and Structs
-# Mixed case; first letter uppercase (e.g., @c AffineMap, @c TreeIterator)
-# Do not use a prefix.
@subsection sClassMethods Class Methods
-# Mixed case; first letter lowercase (e.g., getAccessor(), gridType())
-# Accessors that return a member variable by reference or a primitive type
by value are just the variable name (e.g., Grid::tree()).
-# Accessors that involve construction of objects or other computations are
@c get + the variable name (e.g., Grid::getAccessor()).
-# Simple mutators are @c set + the variable name (e.g., Grid::setTree(tree);).
@subsection sClassInstanceVariables Class Instance Variables
-# Mixed case; always prefixed by @c m (e.g., @c mTree, @c mTransform)
@subsection sClassStaticVariables Class Static Variables
-# Mixed case; always prefixed by @c s (e.g., @c sInitialized)
@subsection sLocalVariablesAndArguments Local Variables and Arguments
-# Use mixed case with an initial lower case (e.g., @c ijk, @c offset,
@c range, @c rhsValue).
@subsection sConstants Constants
-# All uppercase; words separated by underscores. If not in a namespace or
local to a source file, then prefixed with the library name in all caps
(e.g., @c HALF_FLOAT_TYPENAME_SUFFIX, @c ZIP_COMPRESSION_LEVEL).
@subsection sEnumerationNames Enumeration Names
-# Mixed case; first letter uppercase (e.g., @c GridClass, @c VecType)
@subsection sEnumerationValues Enumeration Values
-# Same as constants; all uppercase; words separated by underscores
(e.g., @c GRID_LEVEL_SET, @c VEC_INVARIANT) and with a common prefix
(@c GRID_, @c VEC_, etc.)
@subsection sTypedefs Typedefs
-# Mixed case; first letter uppercase (e.g., @c ConstPtr, @c ValueType)
-# Do not use a prefix.
@subsection sGlobalVariables Global Variables
-# Mixed case; always prefixed by @c g (e.g., @c gRegistry)
-# In general, try to use class static data instead of globals.
@subsection sGlobalFunctions Global Functions
-# Mixed case; always prefixed by @c g (e.g., gFunc()).
-# In general, try to use class static members instead of global functions.
@subsection sBooleans Booleans
-# When naming boolean functions and variables, use names that read as a
condition (e.g., if (grid.empty()),
if (matrix.isInvertible())).
@section sPractices Practices
@subsection sGeneral General
-# Code must compile without any warning messages.
if closely related.
-# Prefer the C++ Standard Library to the C Standard Library.
-# Restrict variables to the smallest scopes possible, and avoid defining
local variables before giving them values. Prefer declarations inside
conditionals: if (Grid::Ptr grid = createGrid()) { ... } instead of
Grid::Ptr grid = createGrid(); if (grid) { ... }
-# For new files, be sure to use the right license boilerplate per our license
policy.
@subsection sFormatting Formatting
-# Use Doxygen-style comments to document public code.
-# Indentation is 4 spaces. Do not use tabs.
-# Use Unix-style carriage returns ("\n") rather than Windows/DOS ones ("\r\n").
-# Don’t put an @c else right after a @c return. It’s unnecessary
and increases indentation level.
-# Do not leave debug printfs or other debugging code lying around.
-# Leave a blank line between a group of variable declarations and other code.
-# Leave a space after the keywords @c if, @c switch, @c while, @c do, @c for,
and @c return.
-# Leave a space on each side of binary operators such as +, -, *, /, &&,
and ||. For clarity in mathematical situations, you may omit the spaces on
either side of * and / operators to illustrate their precedence over + and -.
-# Do not leave a space between any dereferencing operator
(such as *, &, [], ->, or .) and its operands.
-# In parameter lists, leave a space after each comma.
-# Do not leave a space after an opening parenthesis or before a closing
parenthesis.
-# Parentheses should be used to clarify operator precedence in expressions.
-# Do not leave a space before an end-of-statement semicolon.
-# Do not use literal tabs in strings or character constants. Rather, use
spaces or "\t".
-# If a parameter list is too long, break it between parameters. Group related
parameters on lines if appropriate.
-# Modified spacing is allowed to vertically align repetitive expressions.
-# Always begin numeric constants with a digit (e.g., 0.001 not .001).
-# Use K&R-style brace placement in public code.
-# You may leave off braces for simple, single line flow control statements.
-# The return type in a function definition should go on a line by itself.
@subsection sIncludeStatements Include Statements
-# Always use double quotes ("") to include header files that live in the same
directory as your source code.
-# Use angle brackets (<>) to include header files from outside a file’s
directory.
-# Do not use absolute paths in include directives.
-# If there is a header corresponding to a given source file, list it first,
followed by other local headers, library headers and then system headers.
@subsection sHeaderFiles Header Files
-# Header files have a .h extension.
-# All header files should be bracketed by @c \#ifdef "guard" statements.
-# In class definitions, list public, then protected, then private members.
-# List methods before variables.
-# Fully prototype all public functions and use descriptive naming for each
argument.
-# Declare every function defined in a header but outside a class definition
explicitly as @c inline.
-# Prefer forward declarations to @c \#include directives in headers.
-# Do not take advantage of indirect inclusion of header files.
@subsection sSourceFiles Source Files
-# Source files have a .cc extension.
-# Properly prototype all file static functions with usefully named arguments.
-# Whenever possible, put variables and functions in an anonymous namespace.
-# Avoid global variables.
-# For the main file of an executable, define @c main() at the top and then
utility functions below it in a top-down fashion.
@subsection sComments Comments
-# Use @c // style comments instead of / * * / style, even for multi-line
comments.
-# Use multi-line comments to describe following paragraphs of code.
-# Use end-of-line comments to describe variable declarations or to clarify a
single statement.
-# Large blocks of code should be commented out with \#if 0 and @c \#endif.
-# Do not leave obsolete code fragments within comments as an historical trail.
@subsection sPrimitiveTypes Primitive Types
-# Avoid writing code that is dependent on the bit size of primitive values,
but when specific bit sizes are required, use explicitly-sized types such as
@c int32_t or @c uint64_t.
@subsection sMacros Macros
-# Avoid macros for constants. Use static constants instead.
-# Avoid macro functions. Use @c inline and templates instead.
@subsection sClasses Classes
-# Constructors that can be called with only one argument should be prefixed
with the @c explicit keyword to avoid unintended type conversions.
-# Always list the destructor.
-# Never call virtual methods from destructors.
-# If you have a copy constructor, make sure you have an assignment operator.
-# If you have an assignment operator, you probably need a copy constructor.
-# If you have data members that are pointers to dynamically allocated memory,
make sure you have a copy constructor and an assignment operator, both of
which do the right thing with that memory.
-# Classes which are to be subclassed always have a virtual destructor, even if
it is empty.
-# Check against self assignment and return *this in assignment
operators.
-# Declare methods as const as much as possible.
-# Declare object-valued input arguments as const references wherever
possible. Primitives may be passed by value, however.
-# Arithmetic, logical, bitwise, dereference, and address of operators should
only be used when their semantics are clear, obvious, and unambiguous.
-# The application operator [ () ] is allowed for functors.
-# Conversion operators should be avoided.
-# Never return const references to stack allocated or implicitly computed
objects.
-# If a class does not have a copy constructor and/or assignment operator,
consider creating a private unimplemented copy constructor and/or assignment
operator to prevent automatically generated versions from being used.
@subsection sConditionalStatements Conditional Statements
-# For test expressions, use a form that indicates as clearly as possible the
types of operands by avoiding implicit casts.
-# Assignments that occur within conditional statements must have no effect in
the enclosing scope.
-# Allow for arithmetic imprecision when comparing floating point values.
-# In switch statements, always comment fallthroughs and empty cases.
@section sNamespaces Namespaces
-# Namespaces should be used whenever possible.
-# Avoid pulling in entire namespaces with @c using directives
(e.g., using namespace std;).
-# @c using declarations are allowed for individual symbols (e.g.,
using std::vector;), but they should never appear in a header file.
-# Define global operators in the namespace of their arguments.
-# Namespaces are not indented.
@subsection sExceptions Exceptions
-# Appropriate use of exceptions is encouraged.
-# Methods should declare all exceptions they might throw using comments,
but not exception specifications.
-# Throw scope local exception instances, not pointers or references or globals.
-# Catch exceptions by reference.
-# Never allow an exception to escape from a destructor.
@subsection sTemplates Templates
-# Use @c typename rather than @c class when declaring template type parameters.
@subsection sMiscellaneous Miscellaneous
-# Don’t use pointers to run through arrays of non-primitive types. Use explicit array indexing, iterators or generic algorithms instead.
-# Use C++ casts (static_cast<int>(x) or int(x)),
not C casts ((int)x).
-# Multiple variables of the same data type may be declared on the same line
-# Library code must never deliberately terminate the application in response
to an error condition.
-# Avoid using malloc/free when new/delete can be used instead.
-# Avoid @c goto.
-# Avoid \"magic numbers\". Use named constants when necessary.
-# If you use typeid/typeinfo, be aware that although all runtimes support
typeinfo::name(), the format of the string it returns varies
between compilers and even for a given compiler the value is not guaranteed
to be unique.
*/
openvdb/doc/doc.txt 0000644 0000000 0000000 00000051650 12252452022 013267 0 ustar root root /**
@mainpage OpenVDB
The @b OpenVDB library comprises a hierarchical data structure and a suite
of tools for the efficient manipulation of sparse, possibly time-varying,
volumetric data discretized on a three-dimensional grid. It is based on
VDB, which was developed by Ken Museth at DreamWorks Animation, and it
offers an effectively infinite 3D index space, compact storage (both in
memory and on disk), fast data access (both random and sequential), and
a collection of algorithms specifically optimized for the data structure
for common tasks such as filtering, CSG, compositing, sampling and
voxelization from other geometric representations. The technical details
of VDB are described in the paper “VDB: High-Resolution Sparse Volumes with Dynamic Topology”.
@b OpenVDB is maintained by
DreamWorks Animation
and was developed primarily by
- Ken Museth
- Peter Cucka
- Mihai Aldén
- David Hill
See the @subpage overview "Overview" for an introduction to the library.
See @subpage transformsAndMaps "Transforms and Maps" for more
discussion of the transforms used in @b OpenVDB.
See the @subpage faq "FAQ" for frequently asked questions about @b OpenVDB.
See the @subpage codeExamples "Cookbook" to get started using @b OpenVDB.
See @subpage python "Using OpenVDB in Python" to get started with the @b OpenVDB
Python module.
See the @subpage changes "Release Notes" for what's new in this version
of @b OpenVDB.
Contributors, please familiarize yourselves with our
@subpage codingStyle "coding standards".
@page overview OpenVDB Overview
@section Contents
- @ref secOverview
- @ref secTree
- @ref subsecTreeConfig
- @ref secSparsity
- @ref subsecValues
- @ref subsecInactive
- @ref secSpaceAndTrans
- @ref subsecVoxSpace
- @ref subsecWorSpace
- @ref subsecTrans
- @ref secToolUtils
- @ref secIterator
- @ref subsecTreeIter
- @ref subsecNodeIter
- @ref subsecValueAccessor
- @ref subsecTraversal
@section secOverview Introduction
This document is a high-level summary of the terminology and basic
components of the OpenVDB library and is organized around two key
motivating concepts. First, OpenVDB is designed specifically to
work efficiently with sparse volumetric data locally sampled at a high
spatial frequency, although it will function well for dense volumetric
data. From this follows the need for a memory efficient representation of
this sparsity and the need for fast iterators (and other tools) that
respect sparsity. Second, data storage is separated from data
interpretation. OpenVDB uses unit-less three-dimensional integer
coordinates to address the sparse data, but introduces a unit-less
continuous index space for interpolation, along with a transform to
place the data in physical space.
When manipulating data in OpenVDB, the three essential objects are (1) the
@vdblink::tree::Tree Tree@endlink, a B-tree-like three-dimensional data
structure; (2) the @vdblink::math::Transform Transform@endlink, which relates
voxel indices @ijk to physical locations @xyz in @ref subsecWorSpace
"world" space; and (3) the @vdblink::Grid Grid@endlink, a container
that associates a @c Tree with a @c Transform and additional metadata.
For instancing purposes (i.e., placing copies
of the same volume in multiple locations), the same tree may be referenced
(via smart pointers) by several different Grids, each having a
unique transform.
We now proceed to discuss the @c Tree and ideas of sparsity in some
detail, followed by a briefer description of the different spaces and
transforms as well as some of the tools that act on the sparse data.
@section secTree The Tree
In OpenVDB the @c Tree data structure exists to answer the question
What value is stored at location @ijk in three-dimensional index
space? Here i, j and k are arbitrary signed 32-bit
integers, and the data type of the associated value (float,
bool, vector, etc.) is the same for all @ijk. While the @c Tree
serves the same purpose as a large three-dimensional array, it is a
specially designed data structure that, given sparse unique values,
minimizes the overall memory footprint while retaining fast access times.
This is accomplished, as the name suggests, via a tree-based acceleration
structure comprising a @vdblink::tree::RootNode RootNode@endlink,
@vdblink::tree::LeafNode LeafNodes@endlink and usually one or more levels
of @vdblink::tree::InternalNode InternalNodes@endlink with prescribed
branching factors.
@subsection subsecTreeConfig Tree Configuration
The tree-based acceleration structure can be configured in various ways,
but with the restriction that for a given tree all the LeafNodes
are at the same depth. Conceptually, the @c RootNode and
@c InternalNodes increasingly subdivide the three-dimensional index
space, and the LeafNodes hold the actual unique voxels.
The type of a @c Tree encodes both the type of the data to be
stored in the tree (float, bool, etc.) and the
tree's node configuration. In practice a four-level (root,
internal, internal, leaf) configuration is standard, and several
common tree types are defined in openvdb.h. For example,
@code
typedef tree::Tree4::Type FloatTree;
typedef tree::Tree4::Type BoolTree;
@endcode
These predefined tree types share the same branching factors, which dictate
the number of children of a given node. The branching factors (5, 4, 3)
are specified as base two logarithms and should be read backwards from the
leaf nodes up the tree.
In the default tree configuration, each @c LeafNode holds a
three-dimensional grid of 23 voxels on a side (i.e., an
@f$8\times8\times8@f$ voxel grid). Internally, the @c LeafNode is said
to be at "level 0" of the tree. At "level 1" of this tree is the first
@c InternalNode, and it indexes a @f$2^4\times2^4\times2^4 =
16\times16\times16@f$ grid, each entry of which is either a @c LeafNode
or a constant value that represents an @f$8\times8\times8@f$ block of
voxels. At "level 2" is the second @c InternalNode in this
configuration; it in turn indexes a @f$2^5\times2^5\times2^5 =
32\times32\times32@f$ grid of level-1 InternalNodes and/or values,
and so the @c InternalNode at level 2 subsumes a three-dimensional
block of voxels of size @f$32\times16\times8 = 4096@f$ on a side. Unlike
the InternalNodes and LeafNodes, the @c RootNode ("level
3" for the default configuration) is not explicitly restricted in the number
of children it may have, so the overall index space is limited only by the
range of the integer indices, which are 32-bit by default.
@section secSparsity Sparse Values and Voxels
Like a tree's node configuration, the type of data held by a tree is
determined at compile time. Conceptually the tree itself
employs two different notions of data sparsity to reduce the memory footprint
and at the same time accelerate access to its contents. The first is largely
hidden from the user and concerns ways in which large regions of uniform values
are compactly represented, and the second allows for fast sequential iteration,
skipping user-specified "uninteresting" regions (that may or may not have
uniform values).
@subsection subsecValues Tile, Voxel, and Background Values
Although the data in a tree is accessed and set on a per-voxel level
(i.e., the value at @ijk) it need not be internally stored in that way.
To reduce the memory footprint and accelerate data access, data values are
stored in three distinct forms internal to the tree: voxel values,
tile values, and a background value. A voxel value is a unique
value indexed by the location of a voxel and is stored in the @c LeafNode
responsible for that voxel. A tile value is a uniform value assigned to all
voxels subsumed by a given node. (For example, a tile Value belonging to an
@c InternalNode at level 1 is equivalent to a constant-value cube of voxels
of the same size, @f$8\times8\times8@f$, as a @c LeafNode.) The tile value
is returned when a request is made for the data associated with any @ijk
location within the uniform tile. The background value is a unique value
(stored at the root level) that is returned when accessing any @ijk location
that does not resolve to either a tile or a @c LeafNode.
@subsection subsecInactive Active and Inactive Voxels
Any voxel or tile can be classified as either @b active or @b inactive.
The interpretation of this state is application-specific, but generally
active voxels are "interesting", and inactive somehow less so. The locations
of active values may be sparse in the overall voxel topology, and OpenVDB
provides @ref secIterator "iterators" that access active values only (as
well as iterators over inactive values, all values, and general topology).
An example of active vs. inactive: the voxels used to store the distance
values of a narrow-band level set (i.e., close to a given surface) will be
marked as active while the other ("far") voxel locations will be marked as
inactive and will generally represent regions of space with constant distance
values (e.g., two constant distance values of opposite sign to distinguish
the enclosed inside region from the infinite outside or background embedding).
The @vdblink::tree::Tree::prune() prune()@endlink method replaces
with tile values any nodes that subsume voxels with the same values
and active states.
The resulting tree represents the same volume, but more sparsely.
@section secSpaceAndTrans Coordinate Systems and Transforms
The sampled data in the tree is accessed using signed index coordinates
@ijk, but associating each indicial coordinate with a specific physical
location is a job for a @c Transform. A simple linear transform assumes
a lattice-like structure with a fixed physical distance @f$\Delta@f$ between
indices, so that @f$(x,y,z) = (\Delta i, \Delta j, \Delta k)@f$.
@subsection subsecVoxSpace Index Space
To simplify transformations between physical space and lattice index
coordinates, a continuous generalization of the index lattice points called
index space is used.
For example, index space coordinate (1.0, 1.0, 1.0) corresponds to the same
point as (1,1,1) in the index lattice, but (1.5,1.0,1.0) also has meaning as
halfway between the index coordinates (1,1,1) and (2,1,1). Index space can
be used in constructing interpolated data values: given an arbitrary
location in physical space, one can use a transform to compute the point in
index space (which need not fall on an exact integer index) that maps to that
location and locally interpolate from values with neighboring index
coordinates.
@subsection subsecWorSpace World Space
The interpretation of the data in a tree takes place in world
space. For example, the tree might hold data sampled at discrete
physical locations in world space. @c Transform methods such as
@vdblink::math::Transform::indexToWorld() indexToWorld() @endlink
and its inverse @vdblink::math::Transform::worldToIndex()
worldToIndex() @endlink
may be used to relate coordinates in the two continuous spaces. In
addition, methods such as @vdblink::math::Transform::worldToIndexCellCentered()
worldToIndexCellCentered()@endlink actually return lattice points.
@subsection subsecTrans Transforms and Maps
A @c Grid contains smart pointers to both a @c Tree object and a
@vdblink::math::Transform Transform @endlink object. The transform
provides a context for interpreting the information held in the tree
by associating a location in world space with each entry in the tree.
The actual implementation of the @c Transform is managed by a
@vdblink::math::MapBase Map@endlink object, which is an
encapsulation of a continuous, mostly invertible function of three
variables. A @c Map is required to provide
@vdblink::math::MapBase::applyMap() applyMap()@endlink and
@vdblink::math::MapBase::applyInverseMap() applyInverseMap()@endlink
methods to relate locations in its domain to its range and vice versa.
A @c Map is also required to provide information about its local derivatives.
For more on these classes, see the
@subpage transformsAndMaps "Transforms and Maps" page.
@section secToolUtils Utilities and Tools
OpenVDB provides utility functions and classes for the manipulation of grids
and the data they hold.
Tools such as those found in GridOperators.h compute vector quantities from
scalar data or vice-versa.
Other tools perform filtering (Filter.h and LevelSetFilter.h) and
interpolation (Interpolation.h) as well as sampling (GridTransformer.h),
compositing and constructive solid geometry (Composite.h), and other
transformations (ValueTransformer.h).
OpenVDB also supports advanced finite difference computations through
a variety of local support stencils (Stencils.h).
@section secIterator Iterators
OpenVDB provides efficient, often multithreaded, implementations of a large
variety of morphological, filtering and other algorithms that address common
data manipulation tasks on three-dimensional grids. For more specialized
tasks, OpenVDB provides lower-level data accessors that enable fast
iteration over all or selected voxels and over the elements of a @c Tree.
These take several forms: iterator classes of various types, functor-based
@b visitor methods, and the
@vdblink::tree::ValueAccessor ValueAccessor@endlink,
an accelerator for indexed @ijk voxel lookups.
Iterator classes follow a fairly consistent naming scheme. First, the
@b CIter and @b Iter suffixes denote @const and non-@const iterators, i.e.,
iterators that offer, respectively, read-only and read/write access to the
underlying tree or node. Second, iterators over tile and voxel values are
denoted either @b On, @b Off or @b All, indicating that they visit only
active values, only inactive values, or both active and inactive values.
So, for example, @c Tree::ValueOnCIter is a read-only iterator over all
active values (both tile and voxel) of a tree, whereas
@c LeafNode::ValueAllIter is a read/write iterator over all values, both
active and inactive, of a single leaf node.
OpenVDB iterators are not STL-compatible in that one can always request
an iterator that points to the beginning of a collection of elements (nodes,
voxels, etc.), but one usually cannot request an iterator that points to the
end of the collection. (This is because finding the end might require a
full tree traversal.) Instead, all OpenVDB iterators implement a @c test()
method that returns @c true as long as the iterator is not exhausted and
@c false as soon as it is. Typical usage is as follows:
@code
typedef openvdb::FloatGrid GridType;
GridType grid = ...;
for (GridType::ValueOnCIter iter = grid.cbeginValueOn(); iter.test(); ++iter) ...
@endcode
or more compactly
@code
for (GridType::ValueOnCIter iter = grid.cbeginValueOn(); iter; ++iter) ...
@endcode
Note that the naming scheme for methods that return "begin" iterators
closely mirrors that of the iterators themselves. That is,
@c Grid::cbeginValueOn() returns a @const iterator to the first of a grid's
active values, whereas @c LeafNode::beginValueAll() returns a non-@const
iterator to the first of a leaf node's values, both active and inactive.
(Const overloads of @c begin*() methods are usually provided, so that if
the @c Grid is itself @const, @c Grid::begin*() will actually return a
@const iterator. This makes it more convenient to use these methods in
templated code.)
Finally, note that modifying the tree or node over which one is iterating
typically does not invalidate the iterator, though it might first need
to be incremented to point to the next existing element (for example,
if one deletes a child node to which the iterator is currently pointing).
@subsection subsecTreeIter Tree Iterators
@anchor treeValueIterRef
@par Tree::ValueIter
Tree-level value iterators traverse an entire tree, visiting each value
(tile or voxel) exactly once. (It is also possible to restrict the
traversal to minimum and maximum levels of the tree.) In addition to the
methods common to all OpenVDB iterators, such as @c test() and @c next(),
a @c Tree::ValueIter provides methods that return the depth in the tree of
the node within which the iterator is pointing (the root node has depth 0)
and the @ijk axis-aligned bounding box of the tile or voxel to which it is
pointing, and methods to get and set both the value and the active state of
the tile or voxel. See the
@vdblink::tree::TreeValueIteratorBase TreeValueIteratorBase @endlink
class for the complete list.
@anchor treeLeafIterRef
@par Tree::LeafIter
Narrow-band level sets are represented by three distinct regions of voxels:
a thin band of active voxels whose values are signed distances; an
@b outside (or background) region of inactive voxels having a constant,
positive distance; and an @b inside region of inactive voxels having a
constant, negative distance. By convention in OpenVDB, narrow-band voxels
are stored only at the leaf level of a tree, so to facilitate the
implementation of level set algorithms that operate on narrow-band voxels,
OpenVDB provides an iterator that visits each @c LeafNode in a tree exactly
once. See the @vdblink::tree::LeafIteratorBase LeafIteratorBase @endlink
class for details.
@anchor treeNodeIterRef
@par Tree::NodeIter
A node iterator traverses a tree in depth-first order, starting from its
root, and visits each node exactly once. (It is also possible to restrict
the traversal to minimum and maximum node depths—see the
@vdblink::tree::NodeIteratorBase NodeIteratorBase @endlink
class for details.) Like the tree-level value iterator, the node iterator
provides methods that return the depth in the tree of the node to which the
iterator is pointing (the root node has depth 0) and the @ijk axis-aligned
bounding box of the voxels subsumed by the node and all of its children.
@par
Naturally, a node iterator also provides access to the node to which it is
pointing, but this is complicated somewhat by the fact that nodes of the
various types (@c RootNode, @c InternalNode and @c LeafNode) do not inherit
from a common base class. For efficiency, OpenVDB generally avoids class
inheritance and virtual functions in favor of templates, allowing the
compiler to optimize away function calls. In particular, each node type is
templated on the type of its children, so even two InternalNodes at
different levels of a tree have distinct types. As a result, it is
necessary to know the type of the node to which a node iterator is pointing
in order to request access to that node. See the
@ref sNodeIterator "Cookbook" for an example of how to do this.
@subsection subsecNodeIter Node Iterators
Less commonly used than tree-level iterators (but found in the
implementations of some of the narrow-band level set algorithms referred to
@ref treeLeafIterRef "above") are node-level iterators. A node
value iterator visits the values (active, inactive or both) stored in
a single @c RootNode, @c InternalNode or @c LeafNode, whereas a node
child iterator visits the children of a single root or internal node.
(Recall that non-leaf nodes store either a tile value or a child node at
each grid position.)
@subsection subsecValueAccessor Value Accessor
When traversing a grid by @ijk index in a spatially coherent pattern,
such as when iterating over neighboring voxels, request a
@vdblink::tree::ValueAccessor ValueAccessor@endlink from the grid
(with @vdblink::Grid::getAccessor() Grid::getAccessor()@endlink)
and use the accessor's
@vdblink::tree::ValueAccessor::getValue() getValue()@endlink and
@vdblink::tree::ValueAccessor::setValue() setValue()@endlink methods, since
these will usually be significantly faster (a factor of three is typical)
than accessing voxels directly in the grid's tree.
The accessor records the sequence of nodes
visited during the most recent access; on the next access, rather than
traversing the tree from the root node down, it performs an inverted
traversal from the deepest recorded node up. For neighboring voxels, the
traversal need only proceed as far as the voxels' common ancestor node,
which more often than not is the first node in the sequence.
Multiple accessors may be associated with a single grid. In fact, for
multithreaded, read-only access to a grid, it is recommended that each
thread be assigned its own accessor. A thread-safe, mutex-locked accessor
is provided (see @vdblink::tree::ValueAccessorRW ValueAccessorRW@endlink),
but the locking negates much of the performance benefit of inverted traversal;
and because it is the accessor object that is thread-safe, not the grid,
concurrent reads and writes are not safe unless all threads share a single
accessor.
All accessors associated with a grid must be cleared after any operation that
removes nodes from the grid's tree, such as pruning, CSG or compositing.
For those and other built-in operations, this is done automatically via
a callback mechanism, but developers must be careful to call
@vdblink::tree::Tree::clearAllAccessors() Tree::clearAllAccessors()@endlink
whenever deleting nodes directly.
@subsection subsecTraversal Tree Traversal
To be written
*/
openvdb/doc/math.txt 0000644 0000000 0000000 00000043726 12252452022 013460 0 ustar root root /**
@page transformsAndMaps Transforms and Maps
@section Contents
- @ref sTransforms
- @ref sLinearTransforms
- @ref sFrustumTransforms
- @ref sCellVsVertex
- @ref sVoxels
- @ref sStaggered
- @ref sMaps
- @ref sGettingMat4
- @ref sCostOfMaps
- @ref sGradientAndMaps
@section sTransforms Transforms in OpenVDB
The OpenVDB @vdblink::tree::Tree Tree@endlink is a sparse representation
of a three-dimensional array of voxels, each element of which is addressed via
discrete, three-dimensional index space coordinates, typically in the form of
a @vdblink::math::Coord Coord@endlink.
For example, the following code retrieves the floating-point value of
a voxel with index coordinates (1, 2, 3):
@code
openvdb::FloatGrid grid = ...;
openvdb::FloatGrid::Accessor accessor = grid.getAccessor();
openvdb::Coord ijk(1,2,3);
float value = accessor.getValue(ijk);
@endcode
A @vdblink::math::Transform Transform@endlink relates index space coordinates
to world space coordinates that give a spatial context for the discretized data.Translation from index coordinates @ijk to world space coordinates @xyz is
done with a call to the
@vdblink::math::Transform::indexToWorld() indexToWorld@endlink
method, and from world space coordinates to index space coordinates with
a call to @vdblink::math::Transform::worldToIndex() worldToIndex@endlink:
@code
// Create a linear transform that scales i, j and k by 0.1
openvdb::math::Transform::Ptr linearTransform =
openvdb::math::Transform::createLinearTransform(0.1);
// Compute the location in world space that is the image of (1,2,3).
// The result will be (0.1, 0.2, 0.3).
openvdb::Coord ijk(1,2,3);
openvdb::Vec3d worldSpacePoint = linearTransform->indexToWorld(ijk);
// Compute the location in index space that is the pre-image of (0.1, 0.2, 0.3).
// The result will be (1.0, 2.0, 3.0).
openvdb::Vec3d indexSpacePoint = linearTransform->worldToIndex(worldSpacePoint);
@endcode
In the above example, there are two things to notice.
First, world space locations are specified as three-dimensional,
double-precision, floating-point vectors, and second, @c worldToIndex
does not return discrete coordinates, but rather a floating-point vector.
This is a reflection of the fact that not every location in a continuous
world space, i.e., not every @xyz, is the image of discrete integral
coordinates @ijk.
@subsection sLinearTransforms Linear Transforms
Currently two different types of transforms are supported: linear and
frustum transforms.
A linear transform can be composed of scale, translation, rotation, and shear;
essentially those things that can be mathematically represented by an
invertible, constant-coefficient, @f$3 \times 3@f$ matrix and a translation
(mathematically, an affine map).
An essential feature of a linear transformation is that it treats all regions
of index space equally: a small box in index space about origin @ijk=(0,0,0)
is mapped (sheared, scaled, rotated, etc.) in just the same way that
a small box about any other index point is mapped.
@code
// Create a linear transform from a 4x4 matrix (identity in this example).
openvdb::math::Mat4d mat = openvdb::math::Mat4d::identity();
openvdb::math::Transform::Ptr linearTransform =
openvdb::math::Transform::createLinearTransform(mat);
// Rotate the transform by 90 degrees about the X axis.
// As a result the j-index will now map into the -z physical direction,
// and the k-index will map to the +y physical direction.
linearTransform->preRotate(M_PI/2, openvdb::math::X_AXIS);
@endcode
@subsection sFrustumTransforms Frustum Transforms
The frustum transform is a nonlinear transform that treats different
index points differently.
And while the linear transform can be applied to any point in index
or world space, the frustum transform is designed to operate on a subset
of space. Specifically, it transforms a given box in index space to
a tapered box in world space that is a frustum of a rectangular pyramid.
@code
// Create the bounding box that will be mapped by the transform into
// the shape of a frustum.
// The points (0,0,0), (100,0,0), (0,50,0) and (100,50,0) will map to
// the corners of the near plane of the frustum, while the corners
// of the far plane will be the images of (0,0,120), (100,0,120),
// (0,50,120) and (100,50,120).
const openvdb::math::BBoxd bbox(/*min=*/openvdb::math::Vec3d(0,0,0),
/*max=*/openvdb::math::Vec3d(100,50,120));
// The far plane of the frustum will be twice as big as the near plane.
const double taper = 2;
// The depth of the frustum will be 10 times the x-width of the near plane.
cosnt double depth = 10;
// The x-width of the frustum in world space units
const double xWidth = 100;
// Construct a frustum transform that results in a frustum whose
// near plane is centered on the origin in world space.
openvdb::math::Transform::Ptr frustumTransform =
openvdb::math:::Transform::createFrustumTransform(
bbox, taper, depth, xWidth);
// The frustum shape can be rotated, scaled, translated and even
// sheared if desired. For example, the following call translates
// the frustum by 10,15,0 in world space:
frustumTransform->postTranslate(openvdb::math::Vec3d(10,15,0));
// Compute the world space image of a given point within
// the index space bounding box that defines the frustum.
openvdb::Coord ijk(20,10,18);
openvdb::Vec3d worldLocation = frustumTransform->indexToWorld(ijk);
@endcode
@subsection sCellVsVertex Cell-Centered vs. Vertex-Centered Transforms
When partitioning world space with a regular grid, two popular
configurations are cell-centered and vertex-centered grids. This is really
a question of interpretation and transforms.
The cell-centered interpretation imagines world space as divided
into discrete cells (e.g., cubes) centered on the image of the
index-space lattice points.
So the physical location @xyz that is the image (result of @c indexToWorld)
of a lattice point @ijk is the center of a cube.
In the vertex-centered approach, the images of the lattice points form the
vertices of cells, so the location @xyz would be a corner, not the
center, of a cube.
The link between transforms and cell-centered or vertex-centered
partitioning of world space is tenuous.
It boils down to this: the “first” cell vertex often is aligned
with the origin.
In the cell-centered case, when the cells are cubes of length @f$\Delta@f$
on a side, the transform
@f$(x,y,z) = (\Delta i + \Delta/2, \Delta j + \Delta/2, \Delta k + \Delta /2 )@f$
will place the center of the first cube (i.e., the image of @f$(0,0,0)@f$)
at the world space location of @f$(\Delta/2, \Delta/2, \Delta/2)@f$,
and the cube will have walls coincident with the @f$x=0@f$, @f$y=0@f$
and @f$z=0@f$ planes.
Using the OpenVDB transforms to create a so-called cell-centered transform
could be done like this:
@code
// -- Constructing a uniform, cell-centered transform --
// The grid spacing
const double delta = 0.1;
// The offset to cell-center points
const openvdb::math::Vec3d offset(delta/2., delta/2., delta/2.);
// A linear transform with the correct spacing
openvdb::math::Transform::Ptr transform =
openvdb::math:::Transform::createLinearTransform(delta);
// Add the offset.
transform->postTranslate(offset);
@endcode
In contrast, for the vertex-centered partitions of space the first
vertex is just the image of the first lattice point @f$ijk = (0,0,0)@f$,
and the transform would lack the offset used in the cell-centered case;
so it would simply be @f$(x,y,z) = (\Delta i , \Delta j ,\Delta k)@f$
@code
// -- Constructing a uniform, vertex-centered transform --
// The grid spacing
const double delta = 0.1;
// A linear transform with the correct spacing
openvdb::math::Transform::Ptr transform =
openvdb::math:::Transform::createLinearTransform(delta);
@endcode
@subsection sVoxels Voxel Interpretations
A similar and often related concept to cell- and vertex-centered
partitioning of world space is the idea of a voxel.
A voxel historically refers to the volumetric equivalent of a pixel
and as such implies a small region of world space.
A voxel could, for instance, be the image under transform of a vertex-centered
(or cell-centered) box in index space.
In this way the voxel can be seen as a generalization of regular grid cells.
The interpretation of data stored in a grid can be related to the concept
of a voxel but need not be.
An application might interpret the grid value indexed by @ijk as the
spatial average of a quantity in a defined world-space voxel centered
on the image of that lattice point.
But in other situations the value stored at @ijk might be a discrete sample
taken from a single point in world space.
The @vdblink::math::Transform Transform@endlink class does include
methods such as @vdblink::math::Transform::voxelSize() voxelSize@endlink
and @vdblink::math::Transform::voxelVolume() voxelVolume@endlink that suppose
a particular interpretation of a voxel.
They assume a voxel that is the image of a vertex-centered cube in index space,
so the @c voxelSize methods return the lengths of line segments in world space
that connect vertices:
@code
openvdb::Coord ijk(0,0,0);
openvdb::Coord tmp0(1,0,0), tmp1(0,1,0), tmp2(0,0,1);
openvdb::math::Vec3d size;
size.x() = (xform.indexToWorld(ijk + tmp0) - xform.indexToWorld(ijk)).length();
size.y() = (xform.indexToWorld(ijk + tmp1) - xform.indexToWorld(ijk)).length();
size.z() = (xform.indexToWorld(ijk + tmp2) - xform.indexToWorld(ijk)).length();
// The voxelSize() for the voxel at (0,0,0) is consistent with
// the computation above.
assert(xform.voxelSize(ijk) == size);
@endcode
In the case where the transform is linear, the result of @c voxelSize
will be independent of the actual location @ijk, but the voxel size for a
nonlinear transform such as a frustum will be spatially varying.
The related @c voxelVolume can not in general be computed from the
@c voxelSize, because there is no guarantee that a general transform
will convert a cube-shaped voxel into another cube.
As a result, the @c voxelVolume actually returns the determinant of the
transform, which will be a correct measure of volume even if the voxel
is sheared into a general parallelepiped.
@subsection sStaggered Staggered Velocity Grids
Staggered velocity data is often used in fluid simulations, and the
relationship between data interpretation and transforms is
somewhat peculiar when using a vector grid to hold
staggered velocity data.
In this case the lattice point @ijk identifies a cell in world space
by mapping to the cell’s center, but each element of the velocity
vector is identified with a different face of the cell.
The first element of the vector is identified with the image of the
@f$(i-1/2,j,k)@f$ face, the second element with @f$(i,j-1/2,k)@f$,
and the third element with @f$(i,j,k-1/2)@f$.
@section sMaps Maps in OpenVDB Transforms
The actual work of a @vdblink::math::Transform Transform@endlink is performed
by an implementation object called a @vdblink::math::MapBase Map@endlink.
The map in turn is a polymorphic object whose derived types are designed to
optimally represent the most common transformations.
Specifically, the @vdblink::math::Transform Transform@endlink holds a
@vdblink::math::MapBase MapBase@endlink pointer to a derived type that
has been simplified to minimize calculations.
When the transform is updated by prepending or appending a linear operation
(e.g., with @vdblink::math::Transform::preRotate() preRotate@endlink),
the implementation map is recomputed and simplified if possible.
For example, in many level-set-oriented applications the transform
between index space and world space is simply a uniform scaling of the
index points, i.e., @f$(x,y,z) = (\Delta i, \Delta j, \Delta k)@f$,
where @f$\Delta@f$ has some world space units.
For transforms such as this, the implementation is a
@vdblink::math::UniformScaleMap UniformScaleMap@endlink.
@code
// Create a linear transform that scales i, j and k by 0.1
openvdb::math::Transform::Ptr linearTransform =
openvdb::math::Transform::createLinearTransform(0.1);
// Create an equivalent map.
openvdb::math::UniformScaleMap uniformScale(0.1);
// At this time the map holds a openvdb::math::UniformScaleMap.
assert(linearTransform->mapType() == openvdb::math::UniformScaleMap::type());
openvdb::Coord ijk(1,2,3);
// Applying the transform...
openvdb::math::Vec3d transformResult = linearTransform->indexToWorld(ijk);
// ...is equivalent to applying the map.
openvdb::math::Vec3d mapResult = uniformScale.applyMap(ijk);
assert(mapResult == transformResult);
@endcode
There are specialized maps for a variety of common linear transforms:
pure translation (@vdblink::math::TranslationMap TranslationMap@endlink),
uniform scale (@vdblink::math::UniformScaleMap UniformScaleMap@endlink),
uniform scale and translation
(@vdblink::math::UniformScaleTranslateMap UniformScaleTranslateMap@endlink),
non-uniform scale (@vdblink::math::ScaleMap ScaleMap@endlink), and
non-uniform scale and translation
(@vdblink::math::ScaleTranslateMap ScaleTranslateMap@endlink).
A general affine map (@vdblink::math::AffineMap AffineMap@endlink) is used
for all other cases (those that include non-degenerate rotation or shear).
@subsection sGettingMat4 An Equivalent Matrix Representation
The matrix representation used within OpenVDB adheres to the minority
convention of right-multiplication of the matrix against a vector:
@code
// Example matrix transform that scales, next translates,
// and finally rotates an incoming vector
openvdb::math::Mat4d transform = openvdb::math::Mat4d::identity();
transform.preScale(openvdb::math::Vec3d(2,3,2));
transform.postTranslate(openvdb::math::Vec3d(1,0,0));
transform.postRotate(openvdb::math::X_AXIS, M_PI/3.0);
// Location of a point in index space
openvdb::math::Vec3d indexSpace(1,2,3);
// Apply the transform by right-multiplying the matrix.
openvdb::math::Vec3d worldSpace = indexSpace * transform;
@endcode
Any linear map can produce an equivalent
@vdblink::math::AffineMap AffineMap@endlink, which in turn can produce
an equivalent @f$4 \times 4@f$ matrix.
Starting with a linear transform one can produce a consistent matrix as follows:
@code
openvdb::math::Mat4d matrix;
if (transform->isLinear()) {
// Get the equivalent 4x4 matrix.
matrix = transform->getBaseMap()->getAffineMap()->getMat4();
}
@endcode
This could be used as an intermediate form when constructing new linear
transforms by combining old ones.
@code
// Get the matrix equivalent to linearTransformA.
openvdb::math::Mat4d matrixA =
linearTransformA->getBaseMap()->getAffineMap()->getMat4();
// Invert the matrix equivalent to linearTransformB.
openvdb::math::Mat4d matrixBinv =
(linearTransformB->getBaseMap()->getAffineMap()->getMat4()).inverse();
// Create a new transform that maps the index space of linearTransformA
// to the index space of linearTransformB.
openvdb::math::Transform::Ptr linearTransformAtoB =
openvdb::math::Trasform::createLinearTransform(matrixA * matrixBinv);
@endcode
Notice that in the above example, the internal representation used by
the transform will be simplified if possible to use one of the various
map types.
@subsection sCostOfMaps Working Directly with Maps
Accessing a transform’s map through virtual function calls
introduces some overhead and precludes compile-time optimizations
such as inlining.
For this reason, the more computationally intensive OpenVDB tools are
templated to work directly with any underlying map.
This allows the tools direct access to the map’s simplified
representation and gives the compiler a free hand to inline.
Maps themselves know nothing of index space and world space, but are
simply functions @f$x_{range} = f(x_{domain})@f$ that map 3-vectors from
one space (the domain) to another (the range), or from the range back to
the domain (@f$x_{domain} = f^{-1}(x_{range})@f$), by means of the methods
@vdblink::math::MapBase::applyMap() applyMap@endlink and
@vdblink::math::MapBase::applyInverseMap() applyInverseMap@endlink.
@code
// Create a simple uniform scale map that scales by 10.
openvdb::math::UniformScaleMap usm(10.0);
// A point in the domain
openvdb::math::Vec3d domainPoint(0,1,3);
// The resulting range point
openvdb::math::Vec3d rangePoint = usm.applyMap(domainPoint);
// The map is inverted thus:
assert(domainPoint == usm.applyInverseMap(rangePoint));
@endcode
In many tools, the actual map type is not known a priori and
must be deduced at runtime prior to calling the appropriate
map-specific or map-templated code. The type of map currently being
used by a transform can be determined using the
@vdblink::math::Transform::mapType() mapType@endlink method:
@code
// Test for a uniform scale map.
if (transform->mapType() == openvdb::math::UniformScaleMap::type()) {
// This call would return a null pointer in the case of a map type mismatch.
openvdb::math::UniformScaleMap::ConstPtr usm =
transform->map();
// Call a function that accepts UniformScaleMaps.
dofoo(*usm)
}
@endcode
To simplify this process, the function
@vdblink::math::processTypedMap processTypedMap@endlink has been provided.
It accepts a transform and a functor templated on the map type.
@subsection sGradientAndMaps Maps and Mathematical Operations
In addition to supporting the mapping of a point from one space to another,
maps also support mapping of local gradients.
This results from use of the chain rule of calculus in computing the
Jacobian of the map.
Essentially, the gradient calculated in the domain of a map can be converted
to the gradient in the range of the map, allowing one to compute a gradient
in index space and then transform it to a gradient in world space.
@code
// Compute the gradient at a point in index space in a
// floating-point grid using the second-order central difference.
openvdb::FloatGrid grid = ...;
openvdb::Coord ijk(2,3,5)
openvdb::math::Vec3d isGrad =
openvdb::math::ISGradient::result(grid, ijk);
// Apply the inverse Jacobian transform to convert the result to the
// gradient in the world space defined by a map that scales index space
// to create voxels of size 0.1 x 0.2 x 0.1
openvdb::math::ScaleMap scalemap(0.1, 0.2, 0.1);
openvdb::math::Vec3d wsGrad = scalemap.applyIJT(isGrad);
@endcode
*/
openvdb/doc/examplecode.txt 0000644 0000000 0000000 00000106721 12252452022 015010 0 ustar root root /**
@page codeExamples OpenVDB Cookbook
This section provides code snippets and some complete programs that
illustrate how to use OpenVDB and how to perform common tasks.
@section Contents
- @ref sHelloWorld
- @ref sCompilingHelloWorld
- @ref sAllocatingGrids
- @ref sPopulatingGrids
- @ref sModifyingGrids
- @ref sStreamIO
- @ref sHandlingMetadata
- @ref sAddingMetadata
- @ref sGettingMetadata
- @ref sRemovingMetadata
- @ref sIteration
- @ref sNodeIterator
- @ref sLeafIterator
- @ref sValueIterator
- @ref sXformTools
- @ref sResamplingTools
- @ref sValueXformTools
- @ref sCombiningGrids
- @ref sCsgTools
- @ref sCompTools
- @ref sCombineTools
- @ref sGenericProg
- @ref sTypedGridMethods
@section sHelloWorld "Hello, World" for OpenVDB
This is a very simple example showing how to create a grid and access
its voxels. OpenVDB supports both random access to voxels by coordinates
and sequential access by means of iterators. This example illustrates both
types of access:
@code
#include
#include
int main()
{
// Initialize the OpenVDB library. This must be called at least
// once per program and may safely be called multiple times.
openvdb::initialize();
// Create an empty floating-point grid with background value 0.
openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create();
std::cout << "Testing random access:" << std::endl;
// Get an accessor for coordinate-based access to voxels.
openvdb::FloatGrid::Accessor accessor = grid->getAccessor();
// Define a coordinate with large signed indices.
openvdb::Coord xyz(1000, -200000000, 30000000);
// Set the voxel value at (1000, -200000000, 30000000) to 1.
accessor.setValue(xyz, 1.0);
// Verify that the voxel value at (1000, -200000000, 30000000) is 1.
std::cout << "Grid" << xyz << " = " << accessor.getValue(xyz) << std::endl;
// Reset the coordinates to those of a different voxel.
xyz.reset(1000, 200000000, -30000000);
// Verify that the voxel value at (1000, 200000000, -30000000) is
// the background value, 0.
std::cout << "Grid" << xyz << " = " << accessor.getValue(xyz) << std::endl;
// Set the voxel value at (1000, 200000000, -30000000) to 2.
accessor.setValue(xyz, 2.0);
// Set the voxels at the two extremes of the available coordinate space.
// For 32-bit signed coordinates these are (-2147483648, -2147483648, -2147483648)
// and (2147483647, 2147483647, 2147483647).
accessor.setValue(openvdb::Coord::min(), 3.0f);
accessor.setValue(openvdb::Coord::max(), 4.0f);
std::cout << "Testing sequential access:" << std::endl;
// Print all active ("on") voxels by means of an iterator.
for (openvdb::FloatGrid::ValueOnCIter iter = grid->cbeginValueOn(); iter; ++iter) {
std::cout << "Grid" << iter.getCoord() << " = " << *iter << std::endl;
}
}
@endcode
Output:
@code
Testing random access:
Grid[1000, -200000000, 30000000] = 1
Grid[1000, 200000000, -30000000] = 0
Testing sequential access:
Grid[-2147483648, -2147483648, -2147483648] = 3
Grid[1000, -200000000, 30000000] = 1
Grid[1000, 200000000, -30000000] = 2
Grid[2147483647, 2147483647, 2147483647] = 4
@endcode
@subsection sCompilingHelloWorld Compiling
See the @c Makefile and @c INSTALL file included in this distribution for
details on how to build and install the OpenVDB library.
By default, installation is into the directory tree rooted at
/tmp/OpenVDB/, but this can be changed either by editing the value
of the @c INSTALL_DIR variable in the makefile or by setting the desired
value from the command line, as in the following example:
@code
make install INSTALL_DIR=/usr/local
@endcode
Once OpenVDB has been installed, the simplest way to compile a program
like the “Hello, World” example above is to examine the
commands that are used to build the @c vdb_print tool:
@code
rm vdb_print
make verbose=yes vdb_print
@endcode
and then replace “-o vdb_print” with, for example,
“-o helloworld”
and “cmd/openvdb_print/main.cc”
with “helloworld.cc”.
@section sAllocatingGrids Creating and writing a grid
This example is a complete program that illustrates some of the basic steps
to create grids and write them to disk. (See @ref sPopulatingGrids,
below, for the implementation of the @c makeSphere() function.)
@code
#include
int main()
{
openvdb::initialize();
// Create a shared pointer to a newly-allocated grid of a built-in type:
// in this case, a FloatGrid, which stores one single-precision floating point
// value per voxel. Other built-in grid types include BoolGrid, DoubleGrid,
// Int32Grid and Vec3SGrid (see openvdb.h for the complete list).
// The grid comprises a sparse tree representation of voxel data,
// user-supplied metadata and a voxel space to world space transform,
// which defaults to the identity transform.
openvdb::FloatGrid::Ptr grid =
openvdb::FloatGrid::create(/*background value=*/2.0);
// Populate the grid with a sparse, narrow-band level set representation
// of a sphere with radius 50 voxels, located at (1.5, 2, 3) in index space.
makeSphere(*grid, /*radius=*/50.0, /*center=*/openvdb::Vec3f(1.5, 2, 3));
// Associate some metadata with the grid.
grid->insertMeta("radius", openvdb::FloatMetadata(50.0));
// Associate a scaling transform with the grid that sets the voxel size
// to 0.5 units in world space.
grid->setTransform(
openvdb::math::Transform::createLinearTransform(/*voxel size=*/0.5));
// Identify the grid as a level set.
grid->setGridClass(openvdb::GRID_LEVEL_SET);
// Name the grid "LevelSetSphere".
grid->setName("LevelSetSphere");
// Create a VDB file object.
openvdb::io::File file("mygrids.vdb");
// Add the grid pointer to a container.
openvdb::GridPtrVec grids;
grids.push_back(grid);
// Write out the contents of the container.
file.write(grids);
file.close();
}
@endcode
The OpenVDB library includes optimized routines for many common tasks.
For example, most of the steps given above are encapsulated in the function
@vdblink::tools::createLevelSetSphere() tools::createLevelSetSphere()@endlink, so that
the above can be written simply as follows:
@code
#include
#include
int main()
{
openvdb::initialize();
// Create a FloatGrid and populate it with a narrow-band
// signed distance field of a sphere.
openvdb::FloatGrid::Ptr grid =
openvdb::tools::createLevelSetSphere(
/*radius=*/50.0, /*center=*/openvdb::Vec3f(1.5, 2, 3),
/*voxel size=*/0.5, /*width=*/4.0);
// Associate some metadata with the grid.
grid->insertMeta("radius", openvdb::FloatMetadata(50.0));
// Name the grid "LevelSetSphere".
grid->setName("LevelSetSphere");
// Create a VDB file object.
openvdb::io::File file("mygrids.vdb");
// Add the grid pointer to a container.
openvdb::GridPtrVec grids;
grids.push_back(grid);
// Write out the contents of the container.
file.write(grids);
file.close();
}
@endcode
@section sPopulatingGrids Populating a grid with values
The following code is templated so as to operate on grids containing values
of any scalar type, provided that the value type supports negation and
comparison. Note that this algorithm is only meant as an example and should
never be used in production; use the much more efficient routines in
tools/LevelSetSphere.h instead.
See @ref sGenericProg for more on processing grids of arbitrary type.
@anchor makeSphereCode
@code
// Populate the given grid with a narrow-band level set representation of a sphere.
// The width of the narrow band is determined by the grid's background value.
// (Example code only; use tools::createSphereSDF() in production.)
template
void
makeSphere(GridType& grid, float radius, const openvdb::Vec3f& c)
{
typedef typename GridType::ValueType ValueT;
// Distance value for the constant region exterior to the narrow band
const ValueT outside = grid.background();
// Distance value for the constant region interior to the narrow band
// (by convention, the signed distance is negative in the interior of
// a level set)
const ValueT inside = -outside;
// Use the background value as the width in voxels of the narrow band.
// (The narrow band is centered on the surface of the sphere, which
// has distance 0.)
int padding = int(openvdb::math::RoundUp(openvdb::math::Abs(outside)));
// The bounding box of the narrow band is 2*dim voxels on a side.
int dim = int(radius + padding);
// Get a voxel accessor.
typename GridType::Accessor accessor = grid.getAccessor();
// Compute the signed distance from the surface of the sphere of each
// voxel within the bounding box and insert the value into the grid
// if it is smaller in magnitude than the background value.
openvdb::Coord ijk;
int &i = ijk[0], &j = ijk[1], &k = ijk[2];
for (i = c[0] - dim; i < c[0] + dim; ++i) {
const float x2 = openvdb::math::Pow2(i - c[0]);
for (j = c[1] - dim; j < c[1] + dim; ++j) {
const float x2y2 = openvdb::math::Pow2(j - c[1]) + x2;
for (k = c[2] - dim; k < c[2] + dim; ++k) {
// The distance from the sphere surface in voxels
const float dist = openvdb::math::Sqrt(x2y2
+ openvdb::math::Pow2(k - c[2])) - radius;
// Convert the floating-point distance to the grid's value type.
ValueT val = ValueT(dist);
// Only insert distances that are smaller in magnitude than
// the background value.
if (val < inside || outside < val) continue;
// Set the distance for voxel (i,j,k).
accessor.setValue(ijk, val);
}
}
}
// Propagate the outside/inside sign information from the narrow band
// throughout the grid.
grid.signedFloodFill();
}
@endcode
@section sModifyingGrids Reading and modifying a grid
@code
#include
openvdb::initialize();
// Create a VDB file object.
openvdb::io::File file("mygrids.vdb");
// Open the file. This reads the file header, but not any grids.
file.open();
// Loop over all grids in the file and retrieve a shared pointer
// to the one named "LevelSetSphere". (This can also be done
// more simply by calling file.readGrid("LevelSetSphere").)
openvdb::GridBase::Ptr baseGrid;
for (openvdb::io::File::NameIterator nameIter = file.beginName();
nameIter != file.endName(); ++nameIter)
{
// Read in only the grid we are interested in.
if (nameIter.gridName() == "LevelSetSphere") {
baseGrid = file.readGrid(nameIter.gridName());
} else {
std::cout << "skipping grid " << nameIter.gridName() << std::endl;
}
}
file.close();
// From the example above, "LevelSetSphere" is known to be a FloatGrid,
// so cast the generic grid pointer to a FloatGrid pointer.
openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast(baseGrid);
// Convert the level set sphere to a narrow-band fog volume, in which
// interior voxels have value 1, exterior voxels have value 0, and
// narrow-band voxels have values varying linearly from 0 to 1.
const float outside = grid->background();
const float width = 2.0 * outside;
// Visit and update all of the grid's active values, which correspond to
// voxels on the narrow band.
for (openvdb::FloatGrid::ValueOnIter iter = grid->beginValueOn(); iter; ++iter) {
float dist = iter.getValue();
iter.setValue((outside - dist) / width);
}
// Visit all of the grid's inactive tile and voxel values and update the values
// that correspond to the interior region.
for (openvdb::FloatGrid::ValueOffIter iter = grid->beginValueOff(); iter; ++iter) {
if (iter.getValue() < 0.0) {
iter.setValue(1.0);
iter.setValueOff();
}
}
// Set exterior voxels to 0.
grid->setBackground(0.0);
@endcode
@section sStreamIO Stream I/O
The @vdblink::io::Stream io::Stream@endlink class allows grids
to be written to and read from streams that do not support random access,
with the restriction that all grids must be written or read at once.
(With @vdblink::io::File io::File@endlink,
grids can be read individually by name, provided that they were originally
written with @c io::File, rather than streamed to a file.)
@code
#include
#include
openvdb::initialize();
openvdb::GridPtrVecPtr grids(new GridPtrVec);
grids->push_back(...);
// Stream the grids to a string.
std::ostringstream ostr(std::ios_base::binary);
openvdb::io::Stream(ostr).write(*grids);
// Stream the grids to a file.
std::ofstream ofile("mygrids.vdb", std::ios_base::binary);
openvdb::io::Stream(ofile).write(*grids);
// Stream in grids from a string.
// Note that io::Stream::getGrids() returns a shared pointer
// to an openvdb::GridPtrVec.
std::istringstream istr(ostr.str(), std::ios_base::binary);
openvdb::io::Stream strm(istr);
grids = strm.getGrids();
// Stream in grids from a file.
std::ifstream ifile("mygrids.vdb", std::ios_base::binary);
grids = openvdb::io::Stream(ifile).getGrids();
@endcode
@section sHandlingMetadata Handling metadata
Metadata of various types (string, floating point, integer, etc.—see
metadata/Metadata.h for more) can be attached both to individual Grids
and to files on disk.
The examples that follow refer to Grids, but the usage is the same
for the @vdblink::MetaMap MetaMap@endlink that can optionally be supplied
to a @vdblink::io::File::write() file@endlink or
@vdblink::io::Stream::write() stream@endlink for writing.
@subsection sAddingMetadata Adding metadata
The @vdblink::Grid::insertMeta() Grid::insertMeta()@endlink method either
adds a new (@em name, @em value) pair if the name is unique, or overwrites
the existing value if the name matches an existing one. An existing value
cannot be overwritten with a new value of a different type; the old metadata
must be removed first.
@code
#include
openvdb::Vec3SGrid::Ptr grid = openvdb::Vec3SGrid::create();
grid->insertMeta("vector type", openvdb::StringMetadata("covariant (gradient)"));
grid->insertMeta("radius", openvdb::FloatMetadata(50.0));
grid->insertMeta("center", openvdb::Vec3SMetadata(openvdb::Vec3S(10, 15, 10)));
// OK, overwrites existing value:
grid->insertMeta("center", openvdb::Vec3SMetadata(openvdb::Vec3S(10.5, 15, 30)));
// Error (throws openvdb::TypeError), can't overwrite a value of type Vec3S
// with a value of type float:
grid->insertMeta("center", openvdb::FloatMetadata(0.0));
@endcode
@subsection sGettingMetadata Retrieving metadata
Call @vdblink::Grid::metaValue() Grid::metaValue()@endlink to retrieve
the value of metadata of a known type. For example,
@code
std::string s = grid->metaValue("vector type");
float r = grid->metaValue("radius");
// Error (throws openvdb::TypeError), can't read a value of type Vec3S as a float:
float center = grid->metaValue("center");
@endcode
@vdblink::Grid::beginMeta() Grid::beginMeta()@endlink and
@vdblink::Grid::beginMeta() Grid::beginMeta()@endlink return STL @c std::map
iterators over all of the metadata associated with a grid:
@code
for (openvdb::MetaMap::MetaIterator iter = grid->beginMeta();
iter != grid->endMeta(); ++iter)
{
const std::string& name = iter->first;
openvdb::Metadata::Ptr value = iter->second;
std::string valueAsString = value->str();
std::cout << name << " = " << valueAsString << std::endl;
}
@endcode
If the type of the metadata is not known, use the
@vdblink::Grid::operator[]() index operator@endlink to retrieve
a shared pointer to a generic @vdblink::Metadata Metadata@endlink object,
then query its type:
@code
openvdb::Metadata::Ptr metadata = grid["center"];
// See typenameAsString() in Types.h for a list of strings that can be
// returned by the typeName() method.
std::cout << metadata->typeName() << std::endl; // prints "vec3s"
// One way to process metadata of arbitrary types:
if (metadata->typeName() == openvdb::StringMetadata::staticTypeName()) {
std::string s = static_cast(*metadata).value();
} else if (metadata->typeName() == openvdb::FloatMetadata::staticTypeName()) {
float f = static_cast(*metadata).value();
} else if (metadata->typeName() == openvdb::Vec3SMetadata::staticTypeName()) {
openvdb::Vec3S v = static_cast(*metadata).value();
}
@endcode
@subsection sRemovingMetadata Removing metadata
@vdblink::Grid::removeMeta() Grid::removeMeta()@endlink removes metadata
by name. If the given name is not found, the call has no effect.
@code
grid->removeMeta("vector type");
grid->removeMeta("center");
grid->removeMeta("vector type"); // OK (no effect)
@endcode
@section sIteration Iteration
@subsection sNodeIterator Node Iterator
A @vdblink::tree::Tree::NodeIter Tree::NodeIter@endlink visits each node in
a tree exactly once. In the following example, the tree is known to have a
depth of 4; see the @ref treeNodeIterRef "Overview" for a discussion of
why node iteration can be complicated when the tree depth is not known.
There are techniques (beyond the scope of this Cookbook) for operating
on trees of arbitrary depth.
@code
#include
typedef openvdb::FloatGrid GridType;
typedef GridType::TreeType TreeType;
typedef TreeType::RootNodeType RootType; // level 3 RootNode
assert(RootType::LEVEL == 3);
typedef RootType::ChildNodeType Int1Type; // level 2 InternalNode
typedef Int1Type::ChildNodeType Int2Type; // level 1 InternalNode
typedef TreeType::LeafNodeType LeafType; // level 0 LeafNode
GridType::Ptr grid = ...;
for (TreeType::NodeIter iter = grid->tree().beginNode(); iter; ++iter) {
switch (iter.getDepth()) {
case 0: { RootType* node = NULL; iter.getNode(node); if (node) ...; break; }
case 1: { Int1Type* node = NULL; iter.getNode(node); if (node) ...; break; }
case 2: { Int2Type* node = NULL; iter.getNode(node); if (node) ...; break; }
case 3: { LeafType* node = NULL; iter.getNode(node); if (node) ...; break; }
}
}
@endcode
@subsection sLeafIterator Leaf Node Iterator
A @vdblink::tree::Tree::LeafIter Tree::LeafIter@endlink visits each leaf
node in a tree exactly once.
@code
#include
typedef openvdb::FloatGrid GridType;
typedef GridType::TreeType TreeType;
GridType::Ptr grid = ...;
// Iterate over references to const LeafNodes.
for (TreeType::LeafCIter iter = grid->tree().cbeginLeaf(); iter; ++iter) {
const TreeType::LeafNodeType& leaf = *iter;
...
}
// Iterate over references to non-const LeafNodes.
for (TreeType::LeafIter iter = grid->tree().beginLeaf(); iter; ++iter) {
TreeType::LeafNodeType& leaf = *iter;
...
}
// Iterate over pointers to const LeafNodes.
for (TreeType::LeafCIter iter = grid->tree().cbeginLeaf(); iter; ++iter) {
const TreeType::LeafNodeType* leaf = iter.getLeaf();
...
}
// Iterate over pointers to non-const LeafNodes.
for (TreeType::LeafIter iter = grid->tree().beginLeaf(); iter; ++iter) {
TreeType::LeafNodeType* leaf = iter.getLeaf();
...
}
@endcode
See the @ref treeLeafIterRef "Overview" for more on leaf node iterators.
@subsection sValueIterator Value Iterator
A @vdblink::tree::Tree::ValueAllIter Tree::ValueIter@endlink visits each
@ref subsecValues "value" (both tile and voxel) in a tree exactly once.
Iteration can be unrestricted or can be restricted to only active values
or only inactive values. Note that tree-level value iterators (unlike
the node iterators described above) can be accessed either through a
grid's tree or directly through the grid itself, as in the following example:
@code
#include
typedef openvdb::Vec3SGrid GridType;
typedef GridType::TreeType TreeType;
GridType::Ptr grid = ...;
// Iterate over all active values but don't allow them to be changed.
for (GridType::ValueOnCIter iter = grid->cbeginValueOn(); iter.test(); ++iter) {
const openvdb::Vec3f& value = *iter;
// Print the coordinates of all voxels whose vector value has
// a length greater than 10, and print the bounding box coordinates
// of all tiles whose vector value length is greater than 10.
if (value.length() > 10.0) {
if (iter.isVoxelValue()) {
std::cout << iter.getCoord() << std::endl;
} else {
openvdb::CoordBBox bbox;
iter.getBoundingBox(bbox);
std::cout << bbox << std::endl;
}
}
}
// Iterate over and normalize all inactive values.
for (GridType::ValueOffIter iter = grid->beginValueOff(); iter.test(); ++iter) {
openvdb::Vec3f value = *iter;
value.normalize();
iter.setValue(value);
}
// Normalize the (inactive) background value as well.
grid->setBackground(grid->background().unit());
@endcode
See the @ref treeValueIterRef "Overview" for more on value iterators.
@section sXformTools Transforming grids
@subsection sResamplingTools Geometric transformation
A @vdblink::tools::GridTransformer GridTransformer@endlink applies a
geometric transformation to an input grid using one of several sampling
schemes, and stores the result in an output grid. The operation is
multithreaded by default, though threading can be disabled by calling
@vdblink::tools::GridTransformer::setThreaded() setThreaded(false)@endlink.
A @c GridTransformer object can be reused to apply the same transformation
to multiple input grids, optionally using different sampling schemes.
@code
#include
#include
openvdb::FloatGrid::Ptr
sourceGrid = ...
targetGrid = ...;
// Get the source and target grids' index space to world space transforms.
const openvdb::math::Transform
&sourceXform = sourceGrid->transform(),
&targetXform = targetGrid->transform();
// Compute a source grid to target grid transform.
// (For this example, we assume that both grids' transforms are linear,
// so that they can be represented as 4 x 4 matrices.)
openvdb::Mat4R xform =
sourceXform.baseMap()->getAffineMap()->getMat4() *
targetXform.baseMap()->getAffineMap()->getMat4().inverse();
// Create the transformer.
openvdb::tools::GridTransformer transformer(xform);
// Resample using nearest-neighbor interpolation.
transformer.transformGrid(
*sourceGrid, *targetGrid);
// Resample using trilinear interpolation.
transformer.transformGrid(
*sourceGrid, *targetGrid);
// Resample using triquadratic interpolation.
transformer.transformGrid(
*sourceGrid, *targetGrid);
// Prune the target tree for optimal sparsity.
targetGrid->tree().prune();
@endcode
@subsection sValueXformTools Value transformation
This example uses @vdblink::tools::foreach() tools::foreach()@endlink to
multiply all values (both tile and voxel and both active and inactive)
of a scalar, floating-point grid by two:
@code
#include
#include
// Define a local function that doubles the value to which the given
// value iterator points.
struct Local {
static inline void op(const openvdb::FloatGrid::ValueAllIter& iter) {
iter.setValue(*iter * 2);
}
};
openvdb::FloatGrid::Ptr grid = ...;
// Apply the function to all values.
openvdb::tools::foreach(grid->beginValueAll(), Local::op);
@endcode
This example uses @vdblink::tools::foreach() tools::foreach()@endlink to
rotate all active vectors of a vector-valued grid by 45 degrees about the
@em y axis:
@code
#include
#include
// Define a functor that multiplies the vector to which the given
// value iterator points by a fixed matrix.
struct MatMul {
openvdb::math::Mat3s M;
MatMul(const openvdb::math::Mat3s& mat): M(mat) {}
inline void operator()(const openvdb::Vec3SGrid::ValueOnIter& iter) const {
iter.setValue(M.transform(*iter));
}
};
openvdb::Vec3SGrid::Ptr grid = ...;
// Construct the rotation matrix.
openvdb::math::Mat3s rot45 =
openvdb::math::rotation(openvdb::math::Y_AXIS, M_PI_4);
// Apply the functor to all active values.
openvdb::tools::foreach(grid->beginValueOn(), MatMul(rot45));
@endcode
@vdblink::tools::transformValues() tools::transformValues()@endlink is
similar to @vdblink::tools::foreach() tools::foreach()@endlink, but it populates
an output grid with transformed values from an input grid that may have a
different value type. The following example populates a scalar,
floating-point grid with the lengths of all active vectors from a
vector-valued grid (see also
@vdblink::tools::Magnitude tools::Magnitude@endlink):
@code
#include
#include
// Define a local function that, given an iterator pointing to a vector value
// in an input grid, sets the corresponding tile or voxel in a scalar,
// floating-point output grid to the length of the vector.
struct Local {
static inline void op(
const openvdb::Vec3SGrid::ValueOnCIter& iter,
openvdb::FloatGrid::ValueAccessor& accessor)
{
if (iter.isVoxelValue()) { // set a single voxel
accessor.setValue(iter.getCoord(), iter->length());
} else { // fill an entire tile
openvdb::CoordBBox bbox;
iter.getBoundingBox(bbox);
accessor.getTree().fill(bbox, iter->length());
}
}
};
openvdb::Vec3SGrid::ConstPtr inGrid = ...;
// Create a scalar grid to hold the transformed values.
openvdb::FloatGrid::Ptr outGrid = openvdb::FloatGrid::create();
// Populate the output grid with transformed values.
openvdb::tools::transformValues(inGrid->cbeginValueOn(), *outGrid, Local::op);
@endcode
@section sCombiningGrids Combining grids
The following examples show various ways in which a pair of grids can be
combined in @ref subsecVoxSpace "index space". The assumption is that index
coordinates @ijk in both grids correspond to the same physical, @ref
subsecWorSpace "world space" location. When the grids have different
transforms, it is usually necessary to first @ref sResamplingTools "resample"
one grid into the other grid's @ref subsecVoxSpace "index space".
@subsection sCsgTools Level set CSG operations
The level set CSG functions in tools/Composite.h operate on pairs of grids
of the same type, using sparse traversal for efficiency. These operations
always leave the second grid empty.
@code
#include
#include
// Two grids of the same type containing level set volumes
openvdb::FloatGrid::Ptr gridA(...), gridB(...);
// Save copies of the two grids; CSG operations always modify
// the A grid and leave the B grid empty.
openvdb::FloatGrid::ConstPtr
copyOfGridA = gridA->deepCopy(),
copyOfGridB = gridB->deepCopy();
// Compute the union (A u B) of the two level sets.
openvdb::tools::csgUnion(*gridA, *gridB);
// Restore the original level sets.
gridA = copyOfGridA->deepCopy();
gridB = copyOfGridB->deepCopy();
// Compute the intersection (A n B) of the two level sets.
openvdb::tools::csgIntersection(gridA, gridB);
// Restore the original level sets.
gridA = copyOfGridA->deepCopy();
gridB = copyOfGridB->deepCopy();
// Compute the difference (A / B) of the two level sets.
openvdb::tools::csgDifference(gridA, gridB);
@endcode
@subsection sCompTools Compositing operations
Like the @ref sCsgTools "CSG operations", the compositing functions in
tools/Composite.h operate on pairs of grids of the same type, and they
always leave the second grid empty.
@code
#include
#include
// Two grids of the same type
openvdb::FloatGrid::Ptr gridA = ..., gridB = ...;
// Save copies of the two grids; compositing operations always
// modify the A grid and leave the B grid empty.
openvdb::FloatGrid::ConstPtr
copyOfGridA = gridA->deepCopy(),
copyOfGridB = gridB->deepCopy();
// At each voxel, compute a = max(a, b).
openvdb::tools::compMax(*gridA, *gridB);
// Restore the original grids.
gridA = copyOfGridA->deepCopy();
gridB = copyOfGridB->deepCopy();
// At each voxel, compute a = min(a, b).
openvdb::tools::compMin(*gridA, *gridB);
// Restore the original grids.
gridA = copyOfGridA->deepCopy();
gridB = copyOfGridB->deepCopy();
// At each voxel, compute a = a + b.
openvdb::tools::compSum(*gridA, *gridB);
// Restore the original grids.
gridA = copyOfGridA->deepCopy();
gridB = copyOfGridB->deepCopy();
// At each voxel, compute a = a * b.
openvdb::tools::compMul(*gridA, *gridB);
@endcode
@subsection sCombineTools Generic combination
The @vdblink::tree::Tree::combine() Tree::combine()@endlink family of
methods apply a user-supplied operator to pairs of corresponding values
of two trees. These methods are efficient because they take into account
the sparsity of the trees; they are not multithreaded, however.
This example uses the @vdblink::tree::Tree::combine() Tree::combine()@endlink
method to compute the difference between corresponding voxels of two
floating-point grids:
@code
#include
// Define a local function that subtracts two floating-point values.
struct Local {
static inline void diff(const float& a, const float& b, float& result) {
result = a - b;
}
};
openvdb::FloatGrid::Ptr aGrid = ..., bGrid = ...;
// Compute the difference between corresponding voxels of aGrid and bGrid
// and store the result in aGrid, leaving bGrid empty.
aGrid->tree().combine(bGrid->tree(), Local::diff);
@endcode
Another @vdblink::tree::Tree::combine() Tree::combine()@endlink example,
this time using a functor to preserve state:
@code
#include
// Define a functor that computes f * a + (1 - f) * b for pairs of
// floating-point values a and b.
struct Blend {
Blend(float f): frac(f) {}
inline void operator()(const float& a, const float& b, float& result) const {
result = frac * a + (1.0 - frac) * b;
}
float frac;
};
openvdb::FloatGrid::Ptr aGrid = ..., bGrid = ...;
// Compute a = 0.25 * a + 0.75 * b for all corresponding voxels of
// aGrid and bGrid. Store the result in aGrid and empty bGrid.
aGrid->tree().combine(bGrid->tree(), Blend(0.25));
@endcode
The @vdblink::tree::Tree::combineExtended() Tree::combineExtended()@endlink
method invokes a function of the form void f(CombineArgs\& args),
where the @vdblink::CombineArgs CombineArgs@endlink object encapsulates an
@em a and a @em b value and their active states as well as a result value
and its active state. In the following example, voxel values in
floating-point @c aGrid are replaced with corresponding values from
floating-point @c bGrid (leaving @c bGrid empty) wherever the @em b values
are larger. The active states of any transferred values are preserved.
@code
#include
// Define a local function that retrieves a and b values from a CombineArgs
// struct and then sets the result member to the maximum of a and b.
struct Local {
static inline void max(CombineArgs& args) {
if (args.b() > args.a()) {
// Transfer the B value and its active state.
args.setResult(args.b());
args.setResultIsActive(args.bIsActive());
} else {
// Preserve the A value and its active state.
args.setResult(args.a());
args.setResultIsActive(args.aIsActive());
}
}
};
openvdb::FloatGrid::Ptr aGrid = ..., bGrid = ...;
aGrid->tree().combineExtended(bGrid->tree(), Local::max);
@endcode
Like @c combine(), @vdblink::tree::Tree::combine2() Tree::combine2()@endlink
applies an operation to pairs of corresponding values of two trees.
However, @c combine2() writes the result to a third, output tree and does
not modify either of the two input trees. (As a result, it is less
space-efficient than the @c combine() method.) Here, the voxel differencing
example above is repeated using @c combine2():
@code #include
struct Local {
static inline void diff(const float& a, const float& b, float& result) {
result = a - b;
}
};
openvdb::FloatGrid::ConstPtr aGrid = ..., bGrid = ...;
openvdb::FloatGrid::Ptr resultGrid = openvdb::FloatGrid::create();
// Combine aGrid and bGrid and write the result into resultGrid.
// The input grids are not modified.
resultGrid->tree().combine2(aGrid->tree(), bGrid->tree(), Local::diff);
@endcode
An @vdblink::tree::Tree::combine2Extended() extended combine2()@endlink
is also available.
@section sGenericProg Generic programming
@subsection sTypedGridMethods Calling Grid methods
A common task is to perform some operation on all of the grids in a file,
where the operation involves @vdblink::Grid Grid@endlink method calls
and the grids are of different types.
Only a handful of @c Grid methods, such as
@vdblink::Grid::activeVoxelCount() activeVoxelCount()@endlink,
are virtual and can be called through a @vdblink::GridBase GridBase@endlink
pointer; most are not, because they require knowledge of the Grid's
value type.
For example, one might want to @vdblink::tree::Tree::prune() prune()@endlink
the trees of all of the grids in a file regardless of their type, but
@c Tree::prune() is non-virtual because it accepts an optional pruning
tolerance argument whose type is the grid's value type.
The @c processTypedGrid() function below makes this kind of task easier.
It is called with a @c GridBase pointer and a functor whose call operator
accepts a pointer to a @c Grid of arbitrary type. The call operator should
be templated on the grid type and, if necessary, overloaded for specific
grid types.
@code
template
void processTypedGrid(openvdb::GridBase::Ptr grid, OpType& op)
{
#define CALL_OP(GridType) \
op.template operator()(openvdb::gridPtrCast(grid))
if (grid->isType()) CALL_OP(openvdb::BoolGrid);
else if (grid->isType()) CALL_OP(openvdb::FloatGrid);
else if (grid->isType()) CALL_OP(openvdb::DoubleGrid);
else if (grid->isType()) CALL_OP(openvdb::Int32Grid);
else if (grid->isType()) CALL_OP(openvdb::Int64Grid);
else if (grid->isType()) CALL_OP(openvdb::Vec3IGrid);
else if (grid->isType()) CALL_OP(openvdb::Vec3SGrid);
else if (grid->isType()) CALL_OP(openvdb::Vec3DGrid);
else if (grid->isType()) CALL_OP(openvdb::StringGrid);
#undef CALL_OP
}
@endcode
The following example shows how to use @c processTypedGrid() to implement
a generic pruning operation for grids of all built-in types:
@code
#include
// Define a functor that prunes the trees of grids of arbitrary type
// with a fixed pruning tolerance.
struct PruneOp {
double tolerance;
PruneOp(double t): tolerance(t) {}
template
void operator()(typename GridType::Ptr grid) const
{
grid->tree().prune(typename GridType::ValueType(tolerance));
}
// Overload to handle string-valued grids
void operator()(openvdb::StringGrid::Ptr grid) const
{
grid->tree().prune();
}
};
// Read all grids from a file.
openvdb::io::File file("mygrids.vdb");
file.open();
openvdb::GridPtrVecPtr myGrids = file.getGrids();
file.close();
// Prune each grid with a tolerance of 1%.
const PruneOp pruner(/*tolerance=*/0.01);
for (openvdb::GridPtrVecIter iter = myGrids->begin();
iter != myGrids->end(); ++iter)
{
openvdb::GridBase::Ptr grid = *iter;
processTypedGrid(grid, pruner);
}
@endcode
*/
openvdb/unittest/ 0000755 0000000 0000000 00000000000 12252453157 013076 5 ustar root root openvdb/unittest/TestLeafOrigin.cc 0000644 0000000 0000000 00000011043 12252453157 016263 0 ustar root root ///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2012-2013 DreamWorks Animation LLC
//
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
//
// Redistributions of source code must retain the above copyright
// and license notice and the following restrictions and disclaimer.
//
// * Neither the name of DreamWorks Animation nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
//
///////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
#include
#include
#include
#include
class TestLeafOrigin: public CppUnit::TestCase
{
public:
virtual void setUp() { openvdb::initialize(); }
virtual void tearDown() { openvdb::uninitialize(); }
CPPUNIT_TEST_SUITE(TestLeafOrigin);
CPPUNIT_TEST(test);
CPPUNIT_TEST(test2Values);
CPPUNIT_TEST(testGetValue);
CPPUNIT_TEST_SUITE_END();
void test();
void test2Values();
void testGetValue();
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestLeafOrigin);
////////////////////////////////////////
void
TestLeafOrigin::test()
{
using namespace openvdb;
std::set indices;
indices.insert(Coord( 0, 0, 0));
indices.insert(Coord( 1, 0, 0));
indices.insert(Coord( 0, 100, 8));
indices.insert(Coord(-9, 0, 8));
indices.insert(Coord(32, 0, 16));
indices.insert(Coord(33, -5, 16));
indices.insert(Coord(42, 17, 35));
indices.insert(Coord(43, 17, 64));
FloatTree tree(/*bg=*/256.0);
std::set::iterator iter = indices.begin();
for ( ; iter != indices.end(); ++iter) tree.setValue(*iter, 1.0);
for (FloatTree::LeafCIter leafIter = tree.cbeginLeaf(); leafIter; ++leafIter) {
const Int32 mask = ~((1 << leafIter->log2dim()) - 1);
const Coord leafOrigin = leafIter->origin();
for (FloatTree::LeafNodeType::ValueOnCIter valIter = leafIter->cbeginValueOn();
valIter; ++valIter)
{
Coord xyz = valIter.getCoord();
CPPUNIT_ASSERT_EQUAL(leafOrigin, xyz & mask);
iter = indices.find(xyz);
CPPUNIT_ASSERT(iter != indices.end());
indices.erase(iter);
}
}
CPPUNIT_ASSERT(indices.empty());
}
void
TestLeafOrigin::test2Values()
{
using namespace openvdb;
FloatGrid::Ptr grid = createGrid(/*bg=*/1.0f);
FloatTree& tree = grid->tree();
tree.setValue(Coord(0, 0, 0), 5);
tree.setValue(Coord(100, 0, 0), 6);
grid->setTransform(math::Transform::createLinearTransform(0.1));
FloatTree::LeafCIter iter = tree.cbeginLeaf();
CPPUNIT_ASSERT_EQUAL(Coord(0, 0, 0), iter->origin());
++iter;
CPPUNIT_ASSERT_EQUAL(Coord(96, 0, 0), iter->origin());
}
void
TestLeafOrigin::testGetValue()
{
const openvdb::Coord c0(0,-10,0), c1(100,13,0);
const float v0=5.0f, v1=6.0f, v2=1.0f;
openvdb::FloatTree::Ptr tree(new openvdb::FloatTree(v2));
tree->setValue(c0, v0);
tree->setValue(c1, v1);
openvdb::FloatTree::LeafCIter iter = tree->cbeginLeaf();
CPPUNIT_ASSERT_EQUAL(v0, iter->getValue(c0));
++iter;
CPPUNIT_ASSERT_EQUAL(v1, iter->getValue(c1));
}
// Copyright (c) 2012-2013 DreamWorks Animation LLC
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
openvdb/unittest/TestVec2Metadata.cc 0000644 0000000 0000000 00000010617 12252453157 016512 0 ustar root root ///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2012-2013 DreamWorks Animation LLC
//
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
//
// Redistributions of source code must retain the above copyright
// and license notice and the following restrictions and disclaimer.
//
// * Neither the name of DreamWorks Animation nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
//
///////////////////////////////////////////////////////////////////////////
#include
#include
#include
class TestVec2Metadata : public CppUnit::TestCase
{
public:
CPPUNIT_TEST_SUITE(TestVec2Metadata);
CPPUNIT_TEST(testVec2i);
CPPUNIT_TEST(testVec2s);
CPPUNIT_TEST(testVec2d);
CPPUNIT_TEST_SUITE_END();
void testVec2i();
void testVec2s();
void testVec2d();
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestVec2Metadata);
void
TestVec2Metadata::testVec2i()
{
using namespace openvdb;
Metadata::Ptr m(new Vec2IMetadata(openvdb::Vec2i(1, 1)));
Metadata::Ptr m2 = m->copy();
CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0);
CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0);
CPPUNIT_ASSERT(m->typeName().compare("vec2i") == 0);
CPPUNIT_ASSERT(m2->typeName().compare("vec2i") == 0);
Vec2IMetadata *s = dynamic_cast(m.get());
CPPUNIT_ASSERT(s->value() == openvdb::Vec2i(1, 1));
s->value() = openvdb::Vec2i(2, 2);
CPPUNIT_ASSERT(s->value() == openvdb::Vec2i(2, 2));
m2->copy(*s);
s = dynamic_cast(m2.get());
CPPUNIT_ASSERT(s->value() == openvdb::Vec2i(2, 2));
}
void
TestVec2Metadata::testVec2s()
{
using namespace openvdb;
Metadata::Ptr m(new Vec2SMetadata(openvdb::Vec2s(1, 1)));
Metadata::Ptr m2 = m->copy();
CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0);
CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0);
CPPUNIT_ASSERT(m->typeName().compare("vec2s") == 0);
CPPUNIT_ASSERT(m2->typeName().compare("vec2s") == 0);
Vec2SMetadata *s = dynamic_cast(m.get());
CPPUNIT_ASSERT(s->value() == openvdb::Vec2s(1, 1));
s->value() = openvdb::Vec2s(2, 2);
CPPUNIT_ASSERT(s->value() == openvdb::Vec2s(2, 2));
m2->copy(*s);
s = dynamic_cast(m2.get());
CPPUNIT_ASSERT(s->value() == openvdb::Vec2s(2, 2));
}
void
TestVec2Metadata::testVec2d()
{
using namespace openvdb;
Metadata::Ptr m(new Vec2DMetadata(openvdb::Vec2d(1, 1)));
Metadata::Ptr m2 = m->copy();
CPPUNIT_ASSERT(dynamic_cast(m.get()) != 0);
CPPUNIT_ASSERT(dynamic_cast(m2.get()) != 0);
CPPUNIT_ASSERT(m->typeName().compare("vec2d") == 0);
CPPUNIT_ASSERT(m2->typeName().compare("vec2d") == 0);
Vec2DMetadata *s = dynamic_cast(m.get());
CPPUNIT_ASSERT(s->value() == openvdb::Vec2d(1, 1));
s->value() = openvdb::Vec2d(2, 2);
CPPUNIT_ASSERT(s->value() == openvdb::Vec2d(2, 2));
m2->copy(*s);
s = dynamic_cast(m2.get());
CPPUNIT_ASSERT(s->value() == openvdb::Vec2d(2, 2));
}
// Copyright (c) 2012-2013 DreamWorks Animation LLC
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
openvdb/unittest/TestTreeIterators.cc 0000644 0000000 0000000 00000053331 12252453157 017046 0 ustar root root ///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2012-2013 DreamWorks Animation LLC
//
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
//
// Redistributions of source code must retain the above copyright
// and license notice and the following restrictions and disclaimer.
//
// * Neither the name of DreamWorks Animation nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
//
///////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \
CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0)
class TestTreeIterators: public CppUnit::TestCase
{
public:
virtual void setUp() { openvdb::initialize(); }
virtual void tearDown() { openvdb::uninitialize(); }
CPPUNIT_TEST_SUITE(TestTreeIterators);
CPPUNIT_TEST(testLeafIterator);
CPPUNIT_TEST(testEmptyLeafIterator);
CPPUNIT_TEST(testOnlyNegative);
CPPUNIT_TEST(testValueAllIterator);
CPPUNIT_TEST(testValueOnIterator);
CPPUNIT_TEST(testValueOffIterator);
CPPUNIT_TEST(testModifyValue);
CPPUNIT_TEST(testDepthBounds);
CPPUNIT_TEST_SUITE_END();
void testLeafIterator();
void testEmptyLeafIterator();
void testOnlyNegative();
void testValueAllIterator();
void testValueOnIterator();
void testValueOffIterator();
void testModifyValue();
void testDepthBounds();
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestTreeIterators);
typedef openvdb::FloatTree TreeType;
void
TestTreeIterators::testLeafIterator()
{
const float fillValue = 256.0f;
TreeType tree(fillValue);
tree.setValue(openvdb::Coord(0, 0, 0), 1.0);
tree.setValue(openvdb::Coord(1, 0, 0), 1.5);
tree.setValue(openvdb::Coord(0, 0, 8), 2.0);
tree.setValue(openvdb::Coord(1, 0, 8), 2.5);
tree.setValue(openvdb::Coord(0, 0, 16), 3.0);
tree.setValue(openvdb::Coord(1, 0, 16), 3.5);
tree.setValue(openvdb::Coord(0, 0, 24), 4.0);
tree.setValue(openvdb::Coord(1, 0, 24), 4.5);
float val = 1.0;
for (TreeType::LeafCIter iter = tree.cbeginLeaf(); iter; ++iter) {
const TreeType::LeafNodeType* leaf = iter.getLeaf();
CPPUNIT_ASSERT(leaf != NULL);
ASSERT_DOUBLES_EXACTLY_EQUAL(val, leaf->getValue(openvdb::Coord(0, 0, 0)));
ASSERT_DOUBLES_EXACTLY_EQUAL(val + 0.5, iter->getValue(openvdb::Coord(1, 0, 0)));
ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue, iter->getValue(openvdb::Coord(1, 1, 1)));
val = val + 1.0;
}
}
// Test the leaf iterator over a tree without any leaf nodes.
void
TestTreeIterators::testEmptyLeafIterator()
{
using namespace openvdb;
TreeType tree(/*fillValue=*/256.0);
std::vector dims;
tree.getNodeLog2Dims(dims);
CPPUNIT_ASSERT_EQUAL(4, int(dims.size()));
// Start with an iterator over an empty tree.
TreeType::LeafCIter iter = tree.cbeginLeaf();
CPPUNIT_ASSERT(!iter);
// Using sparse fill, add internal nodes but no leaf nodes to the tree.
// Fill the region subsumed by a level-2 internal node (assuming a four-level tree).
Index log2Sum = dims[1] + dims[2] + dims[3];
CoordBBox bbox(Coord(0), Coord((1 << log2Sum) - 1));
tree.fill(bbox, /*value=*/1.0);
iter = tree.cbeginLeaf();
CPPUNIT_ASSERT(!iter);
// Fill the region subsumed by a level-1 internal node.
log2Sum = dims[2] + dims[3];
bbox.reset(Coord(0), Coord((1 << log2Sum) - 1));
tree.fill(bbox, /*value=*/2.0);
iter = tree.cbeginLeaf();
CPPUNIT_ASSERT(!iter);
}
void
TestTreeIterators::testOnlyNegative()
{
using openvdb::Index64;
const float fillValue = 5.0f;
TreeType tree(fillValue);
CPPUNIT_ASSERT(tree.empty());
ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue, tree.getValue(openvdb::Coord(5, -10, 20)));
ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue, tree.getValue(openvdb::Coord(-500, 200, 300)));
tree.setValue(openvdb::Coord(-5, 10, 20), 0.1f);
tree.setValue(openvdb::Coord( 5, -10, 20), 0.2f);
tree.setValue(openvdb::Coord( 5, 10, -20), 0.3f);
tree.setValue(openvdb::Coord(-5, -10, 20), 0.4f);
tree.setValue(openvdb::Coord(-5, 10, -20), 0.5f);
tree.setValue(openvdb::Coord( 5, -10, -20), 0.6f);
tree.setValue(openvdb::Coord(-5, -10, -20), 0.7f);
tree.setValue(openvdb::Coord(-500, 200, -300), 4.5678f);
tree.setValue(openvdb::Coord( 500, -200, -300), 4.5678f);
tree.setValue(openvdb::Coord(-500, -200, 300), 4.5678f);
ASSERT_DOUBLES_EXACTLY_EQUAL(0.1f, tree.getValue(openvdb::Coord(-5, 10, 20)));
ASSERT_DOUBLES_EXACTLY_EQUAL(0.2f, tree.getValue(openvdb::Coord( 5, -10, 20)));
ASSERT_DOUBLES_EXACTLY_EQUAL(0.3f, tree.getValue(openvdb::Coord( 5, 10, -20)));
ASSERT_DOUBLES_EXACTLY_EQUAL(0.4f, tree.getValue(openvdb::Coord(-5, -10, 20)));
ASSERT_DOUBLES_EXACTLY_EQUAL(0.5f, tree.getValue(openvdb::Coord(-5, 10, -20)));
ASSERT_DOUBLES_EXACTLY_EQUAL(0.6f, tree.getValue(openvdb::Coord( 5, -10, -20)));
ASSERT_DOUBLES_EXACTLY_EQUAL(0.7f, tree.getValue(openvdb::Coord(-5, -10, -20)));
ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord(-500, 200, -300)));
ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord( 500, -200, -300)));
ASSERT_DOUBLES_EXACTLY_EQUAL(4.5678f, tree.getValue(openvdb::Coord(-500, -200, 300)));
int count = 0;
for (int i = -25; i < 25; ++i) {
for (int j = -25; j < 25; ++j) {
for (int k = -25; k < 25; ++k) {
if (tree.getValue(openvdb::Coord(i, j, k)) < 1.0f) {
//fprintf(stderr, "(%i, %i, %i) = %f\n",
// i, j, k, tree.getValue(openvdb::Coord(i, j, k)));
++count;
}
}
}
}
CPPUNIT_ASSERT_EQUAL(7, count);
openvdb::Coord xyz;
int count2 = 0;
for (TreeType::ValueOnCIter iter = tree.cbeginValueOn();iter; ++iter) {
++count2;
xyz = iter.getCoord();
//std::cerr << xyz << " = " << *iter << "\n";
}
CPPUNIT_ASSERT_EQUAL(10, count2);
CPPUNIT_ASSERT_EQUAL(Index64(10), tree.activeVoxelCount());
}
void
TestTreeIterators::testValueAllIterator()
{
const openvdb::Index DIM0 = 3, DIM1 = 2, DIM2 = 3;
typedef openvdb::tree::Tree4::Type Tree323f;
typedef Tree323f::RootNodeType RootT;
typedef RootT::ChildNodeType Int1T;
typedef Int1T::ChildNodeType Int2T;
typedef Int2T::ChildNodeType LeafT;
Tree323f tree(/*fillValue=*/256.0f);
tree.setValue(openvdb::Coord(4), 0.0f);
tree.setValue(openvdb::Coord(-4), -1.0f);
const size_t expectedNumOff =
2 * ((1 << (3 * DIM2)) - 1) // 2 8x8x8 InternalNodes - 1 child pointer each
+ 2 * ((1 << (3 * DIM1)) - 1) // 2 4x4x4 InternalNodes - 1 child pointer each
+ 2 * ((1 << (3 * DIM0)) - 1); // 2 8x8x8 LeafNodes - 1 active value each
{
Tree323f::ValueAllIter iter = tree.beginValueAll();
CPPUNIT_ASSERT(iter.test());
// Read all tile and voxel values through a non-const value iterator.
size_t numOn = 0, numOff = 0;
for ( ; iter; ++iter) {
CPPUNIT_ASSERT(iter.getLevel() <= 3);
const openvdb::Index iterLevel = iter.getLevel();
for (openvdb::Index lvl = 0; lvl <= 3; ++lvl) {
RootT* root; Int1T* int1; Int2T* int2; LeafT* leaf;
iter.getNode(root); CPPUNIT_ASSERT(root != NULL);
iter.getNode(int1); CPPUNIT_ASSERT(iterLevel < 3 ? int1 != NULL: int1 == NULL);
iter.getNode(int2); CPPUNIT_ASSERT(iterLevel < 2 ? int2 != NULL: int2 == NULL);
iter.getNode(leaf); CPPUNIT_ASSERT(iterLevel < 1 ? leaf != NULL: leaf == NULL);
}
if (iter.isValueOn()) {
++numOn;
const float f = iter.getValue();
if (openvdb::math::isZero(f)) {
CPPUNIT_ASSERT(iter.getCoord() == openvdb::Coord(4));
CPPUNIT_ASSERT(iter.isVoxelValue());
} else {
ASSERT_DOUBLES_EXACTLY_EQUAL(-1.0f, f);
CPPUNIT_ASSERT(iter.getCoord() == openvdb::Coord(-4));
CPPUNIT_ASSERT(iter.isVoxelValue());
}
} else {
++numOff;
// For every tenth inactive value, check that the size of
// the tile or voxel is as expected.
if (numOff % 10 == 0) {
const int dim[4] = {
1, 1 << DIM0, 1 << (DIM1 + DIM0), 1 << (DIM2 + DIM1 + DIM0)
};
const int lvl = iter.getLevel();
CPPUNIT_ASSERT(lvl < 4);
openvdb::CoordBBox bbox;
iter.getBoundingBox(bbox);
CPPUNIT_ASSERT_EQUAL(
bbox.extents(), openvdb::Coord(dim[lvl], dim[lvl], dim[lvl]));
}
}
}
CPPUNIT_ASSERT_EQUAL(2, int(numOn));
CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff);
}
{
Tree323f::ValueAllCIter iter = tree.cbeginValueAll();
CPPUNIT_ASSERT(iter.test());
// Read all tile and voxel values through a const value iterator.
size_t numOn = 0, numOff = 0;
for ( ; iter.test(); iter.next()) {
if (iter.isValueOn()) ++numOn; else ++numOff;
}
CPPUNIT_ASSERT_EQUAL(2, int(numOn));
CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff);
}
{
Tree323f::ValueAllIter iter = tree.beginValueAll();
CPPUNIT_ASSERT(iter.test());
// Read all tile and voxel values through a non-const value iterator
// and overwrite all active values.
size_t numOn = 0, numOff = 0;
for ( ; iter; ++iter) {
if (iter.isValueOn()) {
iter.setValue(iter.getValue() - 5);
++numOn;
} else {
++numOff;
}
}
CPPUNIT_ASSERT_EQUAL(2, int(numOn));
CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff);
}
}
void
TestTreeIterators::testValueOnIterator()
{
typedef openvdb::tree::Tree4::Type Tree323f;
Tree323f tree(/*fillValue=*/256.0f);
{
Tree323f::ValueOnIter iter = tree.beginValueOn();
CPPUNIT_ASSERT(!iter.test()); // empty tree
}
const int STEP = 8/*100*/, NUM_STEPS = 10;
for (int i = 0; i < NUM_STEPS; ++i) {
tree.setValue(openvdb::Coord(STEP * i), 0.0f);
}
{
Tree323f::ValueOnIter iter = tree.beginValueOn();
CPPUNIT_ASSERT(iter.test());
// Read all active tile and voxel values through a non-const value iterator.
size_t numOn = 0;
for ( ; iter; ++iter) {
CPPUNIT_ASSERT(iter.isVoxelValue());
CPPUNIT_ASSERT(iter.isValueOn());
ASSERT_DOUBLES_EXACTLY_EQUAL(0.0f, iter.getValue());
CPPUNIT_ASSERT_EQUAL(openvdb::Coord(STEP * numOn), iter.getCoord());
++numOn;
}
CPPUNIT_ASSERT_EQUAL(NUM_STEPS, int(numOn));
}
{
Tree323f::ValueOnCIter iter = tree.cbeginValueOn();
CPPUNIT_ASSERT(iter.test());
// Read all active tile and voxel values through a const value iterator.
size_t numOn = 0;
for ( ; iter.test(); iter.next()) {
CPPUNIT_ASSERT(iter.isVoxelValue());
CPPUNIT_ASSERT(iter.isValueOn());
ASSERT_DOUBLES_EXACTLY_EQUAL(0.0f, iter.getValue());
CPPUNIT_ASSERT_EQUAL(openvdb::Coord(STEP * numOn), iter.getCoord());
++numOn;
}
CPPUNIT_ASSERT_EQUAL(NUM_STEPS, int(numOn));
}
{
Tree323f::ValueOnIter iter = tree.beginValueOn();
CPPUNIT_ASSERT(iter.test());
// Read all active tile and voxel values through a non-const value iterator
// and overwrite the values.
size_t numOn = 0;
for ( ; iter; ++iter) {
CPPUNIT_ASSERT(iter.isVoxelValue());
CPPUNIT_ASSERT(iter.isValueOn());
ASSERT_DOUBLES_EXACTLY_EQUAL(0.0f, iter.getValue());
iter.setValue(5.0f);
ASSERT_DOUBLES_EXACTLY_EQUAL(5.0f, iter.getValue());
CPPUNIT_ASSERT_EQUAL(openvdb::Coord(STEP * numOn), iter.getCoord());
++numOn;
}
CPPUNIT_ASSERT_EQUAL(NUM_STEPS, int(numOn));
}
}
void
TestTreeIterators::testValueOffIterator()
{
const openvdb::Index DIM0 = 3, DIM1 = 2, DIM2 = 3;
typedef openvdb::tree::Tree4::Type Tree323f;
Tree323f tree(/*fillValue=*/256.0f);
tree.setValue(openvdb::Coord(4), 0.0f);
tree.setValue(openvdb::Coord(-4), -1.0f);
const size_t expectedNumOff =
2 * ((1 << (3 * DIM2)) - 1) // 2 8x8x8 InternalNodes - 1 child pointer each
+ 2 * ((1 << (3 * DIM1)) - 1) // 2 4x4x4 InternalNodes - 1 child pointer each
+ 2 * ((1 << (3 * DIM0)) - 1); // 2 8x8x8 LeafNodes - 1 active value each
{
Tree323f::ValueOffIter iter = tree.beginValueOff();
CPPUNIT_ASSERT(iter.test());
// Read all inactive tile and voxel values through a non-const value iterator.
size_t numOff = 0;
for ( ; iter; ++iter) {
CPPUNIT_ASSERT(!iter.isValueOn());
++numOff;
// For every tenth inactive value, check that the size of
// the tile or voxel is as expected.
if (numOff % 10 == 0) {
const int dim[4] = {
1, 1 << DIM0, 1 << (DIM1 + DIM0), 1 << (DIM2 + DIM1 + DIM0)
};
const int lvl = iter.getLevel();
CPPUNIT_ASSERT(lvl < 4);
openvdb::CoordBBox bbox;
iter.getBoundingBox(bbox);
CPPUNIT_ASSERT_EQUAL(bbox.extents(), openvdb::Coord(dim[lvl], dim[lvl], dim[lvl]));
}
}
CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff);
}
{
Tree323f::ValueOffCIter iter = tree.cbeginValueOff();
CPPUNIT_ASSERT(iter.test());
// Read all inactive tile and voxel values through a const value iterator.
size_t numOff = 0;
for ( ; iter.test(); iter.next(), ++numOff) {
CPPUNIT_ASSERT(!iter.isValueOn());
}
CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff);
}
{
Tree323f::ValueOffIter iter = tree.beginValueOff();
CPPUNIT_ASSERT(iter.test());
// Read all inactive tile and voxel values through a non-const value iterator
// and overwrite the values.
size_t numOff = 0;
for ( ; iter; ++iter, ++numOff) {
iter.setValue(iter.getValue() - 5);
iter.setValueOff();
}
for (iter = tree.beginValueOff(); iter; ++iter, --numOff);
CPPUNIT_ASSERT_EQUAL(size_t(0), numOff);
}
}
void
TestTreeIterators::testModifyValue()
{
using openvdb::Coord;
const openvdb::Index DIM0 = 3, DIM1 = 2, DIM2 = 3;
{
typedef openvdb::tree::Tree4::Type IntTree323f;
IntTree323f tree(/*background=*/256);
tree.addTile(/*level=*/3, Coord(-1), /*value=*/ 4, /*active=*/true);
tree.addTile(/*level=*/2, Coord(1 << (DIM0 + DIM1)), /*value=*/-3, /*active=*/true);
tree.addTile(/*level=*/1, Coord(1 << DIM0), /*value=*/ 2, /*active=*/true);
tree.addTile(/*level=*/0, Coord(0), /*value=*/-1, /*active=*/true);
struct Local { static inline void negate(int32_t& n) { n = -n; } };
for (IntTree323f::ValueAllIter iter = tree.beginValueAll(); iter; ++iter) {
iter.modifyValue(Local::negate);
}
for (IntTree323f::ValueAllCIter iter = tree.cbeginValueAll(); iter; ++iter) {
const int32_t val = *iter;
if (val < 0) CPPUNIT_ASSERT((-val) % 2 == 0); // negative values are even
else CPPUNIT_ASSERT(val % 2 == 1); // positive values are odd
}
// Because modifying values through a const iterator is not allowed,
// uncommenting the following line should result in a static assertion failure:
//tree.cbeginValueOn().modifyValue(Local::negate);
}
{
typedef openvdb::tree::Tree4::Type BoolTree323f;
BoolTree323f tree;
tree.addTile(/*level=*/3, Coord(-1), /*value=*/false, /*active=*/true);
tree.addTile(/*level=*/2, Coord(1 << (DIM0 + DIM1)), /*value=*/ true, /*active=*/true);
tree.addTile(/*level=*/1, Coord(1 << DIM0), /*value=*/false, /*active=*/true);
tree.addTile(/*level=*/0, Coord(0), /*value=*/ true, /*active=*/true);
struct Local { static inline void negate(bool& b) { b = !b; } };
for (BoolTree323f::ValueAllIter iter = tree.beginValueAll(); iter; ++iter) {
iter.modifyValue(Local::negate);
}
CPPUNIT_ASSERT(!tree.getValue(Coord(0)));
CPPUNIT_ASSERT( tree.getValue(Coord(1 << DIM0)));
CPPUNIT_ASSERT(!tree.getValue(Coord(1 << (DIM0 + DIM1))));
CPPUNIT_ASSERT( tree.getValue(Coord(-1)));
// Because modifying values through a const iterator is not allowed,
// uncommenting the following line should result in a static assertion failure:
//tree.cbeginValueOn().modifyValue(Local::negate);
}
{
typedef openvdb::tree::Tree4::Type StringTree323f;
StringTree323f tree(/*background=*/"");
tree.addTile(/*level=*/3, Coord(-1), /*value=*/"abc", /*active=*/true);
tree.addTile(/*level=*/2, Coord(1 << (DIM0 + DIM1)), /*value=*/"abc", /*active=*/true);
tree.addTile(/*level=*/1, Coord(1 << DIM0), /*value=*/"abc", /*active=*/true);
tree.addTile(/*level=*/0, Coord(0), /*value=*/"abc", /*active=*/true);
struct Local { static inline void op(std::string& s) { s.append("def"); } };
for (StringTree323f::ValueOnIter iter = tree.beginValueOn(); iter; ++iter) {
iter.modifyValue(Local::op);
}
const std::string expectedVal("abcdef");
for (StringTree323f::ValueOnCIter iter = tree.cbeginValueOn(); iter; ++iter) {
CPPUNIT_ASSERT_EQUAL(expectedVal, *iter);
}
for (StringTree323f::ValueOffCIter iter = tree.cbeginValueOff(); iter; ++iter) {
CPPUNIT_ASSERT((*iter).empty());
}
}
}
void
TestTreeIterators::testDepthBounds()
{
const openvdb::Index DIM0 = 3, DIM1 = 2, DIM2 = 3;
typedef openvdb::tree::Tree4::Type Tree323f;
Tree323f tree(/*fillValue=*/256.0f);
tree.setValue(openvdb::Coord(4), 0.0f);
tree.setValue(openvdb::Coord(-4), -1.0f);
const size_t
numDepth1 = 2 * ((1 << (3 * DIM2)) - 1), // 2 8x8x8 InternalNodes - 1 child pointer each
numDepth2 = 2 * ((1 << (3 * DIM1)) - 1), // 2 4x4x4 InternalNodes - 1 child pointer each
numDepth3 = 2 * ((1 << (3 * DIM0)) - 1), // 2 8x8x8 LeafNodes - 1 active value each
expectedNumOff = numDepth1 + numDepth2 + numDepth3;
{
Tree323f::ValueOffCIter iter = tree.cbeginValueOff();
CPPUNIT_ASSERT(iter.test());
// Read all inactive tile and voxel values through a non-const value iterator.
size_t numOff = 0;
for ( ; iter; ++iter) {
CPPUNIT_ASSERT(!iter.isValueOn());
++numOff;
}
CPPUNIT_ASSERT_EQUAL(expectedNumOff, numOff);
}
{
// Repeat, setting the minimum iterator depth to 2.
Tree323f::ValueOffCIter iter = tree.cbeginValueOff();
CPPUNIT_ASSERT(iter.test());
iter.setMinDepth(2);
CPPUNIT_ASSERT(iter.test());
size_t numOff = 0;
for ( ; iter; ++iter) {
CPPUNIT_ASSERT(!iter.isValueOn());
++numOff;
const int depth = iter.getDepth();
CPPUNIT_ASSERT(depth > 1);
}
CPPUNIT_ASSERT_EQUAL(expectedNumOff - numDepth1, numOff);
}
{
// Repeat, setting the minimum and maximum depths to 2.
Tree323f::ValueOffCIter iter = tree.cbeginValueOff();
CPPUNIT_ASSERT(iter.test());
iter.setMinDepth(2);
CPPUNIT_ASSERT(iter.test());
iter.setMaxDepth(2);
CPPUNIT_ASSERT(iter.test());
size_t numOff = 0;
for ( ; iter; ++iter) {
CPPUNIT_ASSERT(!iter.isValueOn());
++numOff;
const int depth = iter.getDepth();
CPPUNIT_ASSERT_EQUAL(2, depth);
}
CPPUNIT_ASSERT_EQUAL(expectedNumOff - numDepth1 - numDepth3, numOff);
}
}
// Copyright (c) 2012-2013 DreamWorks Animation LLC
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
openvdb/unittest/TestMeanCurvature.cc 0000644 0000000 0000000 00000057203 12252453157 017035 0 ustar root root ///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2012-2013 DreamWorks Animation LLC
//
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
//
// Redistributions of source code must retain the above copyright
// and license notice and the following restrictions and disclaimer.
//
// * Neither the name of DreamWorks Animation nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
//
///////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
#include
#include
#include
#include "util.h" // for unittest_util::makeSphere()
#define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \
CPPUNIT_ASSERT_DOUBLES_EQUAL((expected), (actual), /*tolerance=*/0.0);
class TestMeanCurvature: public CppUnit::TestFixture
{
public:
virtual void setUp() { openvdb::initialize(); }
virtual void tearDown() { openvdb::uninitialize(); }
CPPUNIT_TEST_SUITE(TestMeanCurvature);
CPPUNIT_TEST(testISMeanCurvature); // MeanCurvature in Index Space
CPPUNIT_TEST(testISMeanCurvatureStencil);
CPPUNIT_TEST(testWSMeanCurvature); // MeanCurvature in World Space
CPPUNIT_TEST(testWSMeanCurvatureStencil);
CPPUNIT_TEST(testMeanCurvatureTool); // MeanCurvature tool
CPPUNIT_TEST(testMeanCurvatureMaskedTool); // MeanCurvature tool
CPPUNIT_TEST(testOldStyleStencils); // old stencil impl - deprecate
CPPUNIT_TEST_SUITE_END();
void testISMeanCurvature();
void testISMeanCurvatureStencil();
void testWSMeanCurvature();
void testWSMeanCurvatureStencil();
void testMeanCurvatureTool();
void testMeanCurvatureMaskedTool();
void testOldStyleStencils();
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestMeanCurvature);
void
TestMeanCurvature::testISMeanCurvature()
{
using namespace openvdb;
typedef FloatGrid::ConstAccessor AccessorType;
FloatGrid::Ptr grid = createGrid(/*background=*/5.0);
FloatTree& tree = grid->tree();
const openvdb::Coord dim(64,64,64);
const openvdb::Vec3f center(35.0f ,30.0f, 40.0f);
const float radius=0.0f;
unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
CPPUNIT_ASSERT(!tree.empty());
Coord xyz(35,30,30);
AccessorType inAccessor = grid->getConstAccessor();
AccessorType::ValueType alpha, beta;
math::ISMeanCurvature::result(inAccessor, xyz, alpha, beta);
AccessorType::ValueType meancurv = alpha/(2*math::Pow3(beta) );
AccessorType::ValueType normGrad = alpha/(2*math::Pow2(beta) );
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
math::ISMeanCurvature::result(inAccessor, xyz, alpha, beta);
meancurv = alpha/(2*math::Pow3(beta) );
normGrad = alpha/(2*math::Pow2(beta) );
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
math::ISMeanCurvature::result(inAccessor, xyz, alpha, beta);
meancurv = alpha/(2*math::Pow3(beta) );
normGrad = alpha/(2*math::Pow2(beta) );
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
xyz.reset(35,10,40);
math::ISMeanCurvature::result(inAccessor, xyz, alpha, beta);
meancurv = alpha/(2*math::Pow3(beta) );
normGrad = alpha/(2*math::Pow2(beta) );
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, normGrad, 0.001);
}
void
TestMeanCurvature::testISMeanCurvatureStencil()
{
using namespace openvdb;
typedef FloatGrid::ConstAccessor AccessorType;
FloatGrid::Ptr grid = createGrid(/*background=*/5.0);
FloatTree& tree = grid->tree();
const openvdb::Coord dim(64,64,64);
const openvdb::Vec3f center(35.0f ,30.0f, 40.0f);
const float radius=0.0f;
unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
CPPUNIT_ASSERT(!tree.empty());
Coord xyz(35,30,30);
math::SecondOrderDenseStencil dense_2nd(*grid);
math::FourthOrderDenseStencil dense_4th(*grid);
math::SixthOrderDenseStencil dense_6th(*grid);
dense_2nd.moveTo(xyz);
dense_4th.moveTo(xyz);
dense_6th.moveTo(xyz);
AccessorType::ValueType alpha, beta;
math::ISMeanCurvature::result(dense_2nd, alpha, beta);
AccessorType::ValueType meancurv = alpha/(2*math::Pow3(beta) );
AccessorType::ValueType normGrad = alpha/(2*math::Pow2(beta) );
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
math::ISMeanCurvature::result(dense_4th, alpha, beta);
meancurv = alpha/(2*math::Pow3(beta) );
normGrad = alpha/(2*math::Pow2(beta) );
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
math::ISMeanCurvature::result(dense_6th, alpha, beta);
meancurv = alpha/(2*math::Pow3(beta) );
normGrad = alpha/(2*math::Pow2(beta) );
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
xyz.reset(35,10,40);
dense_2nd.moveTo(xyz);
math::ISMeanCurvature::result(dense_2nd, alpha, beta);
meancurv = alpha/(2*math::Pow3(beta) );
normGrad = alpha/(2*math::Pow2(beta) );
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, normGrad, 0.001);
}
void
TestMeanCurvature::testWSMeanCurvature()
{
using namespace openvdb;
using math::AffineMap;
using math::TranslationMap;
using math::UniformScaleMap;
typedef FloatGrid::ConstAccessor AccessorType;
{ // unit size voxel test
FloatGrid::Ptr grid = createGrid(/*background=*/5.0);
FloatTree& tree = grid->tree();
const openvdb::Coord dim(64,64,64);
const openvdb::Vec3f center(35.0f ,30.0f, 40.0f);
const float radius=0.0f;
unittest_util::makeSphere(
dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
CPPUNIT_ASSERT(!tree.empty());
Coord xyz(35,30,30);
AccessorType inAccessor = grid->getConstAccessor();
AccessorType::ValueType meancurv;
AccessorType::ValueType normGrad;
AffineMap affine;
meancurv = math::MeanCurvature::result(
affine, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
affine, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
meancurv = math::MeanCurvature::result(
affine, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
affine, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
UniformScaleMap uniform;
meancurv = math::MeanCurvature::result(
uniform, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
uniform, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
xyz.reset(35,10,40);
TranslationMap trans;
meancurv = math::MeanCurvature::result(
trans, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
trans, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, normGrad, 0.001);
}
{ // non-unit sized voxel
double voxel_size = 0.5;
FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0);
grid->setTransform(math::Transform::createLinearTransform(voxel_size));
CPPUNIT_ASSERT(grid->empty());
const openvdb::Coord dim(32,32,32);
const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space
const float radius=10.0f;
unittest_util::makeSphere(
dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
AccessorType inAccessor = grid->getConstAccessor();
AccessorType::ValueType meancurv;
AccessorType::ValueType normGrad;
Coord xyz(20,16,20);
AffineMap affine(voxel_size*math::Mat3d::identity());
meancurv = math::MeanCurvature::result(
affine, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
affine, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001);
meancurv = math::MeanCurvature::result(
affine, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
affine, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001);
UniformScaleMap uniform(voxel_size);
meancurv = math::MeanCurvature::result(
uniform, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
uniform, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001);
}
{ // NON-UNIFORM SCALING AND ROTATION
Vec3d voxel_sizes(0.25, 0.45, 0.75);
FloatGrid::Ptr grid = FloatGrid::create();
math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes));
// apply rotation
math::MapBase::Ptr rotated_map = base_map->preRotate(1.5, math::X_AXIS);
grid->setTransform(math::Transform::Ptr(new math::Transform(rotated_map)));
CPPUNIT_ASSERT(grid->empty());
const openvdb::Coord dim(32,32,32);
const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space
const float radius=10.0f;
unittest_util::makeSphere(
dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
AccessorType inAccessor = grid->getConstAccessor();
AccessorType::ValueType meancurv;
AccessorType::ValueType normGrad;
Coord xyz(20,16,20);
Vec3d location = grid->indexToWorld(xyz);
double dist = (center - location).length();
AffineMap::ConstPtr affine = grid->transform().map();
meancurv = math::MeanCurvature::result(
*affine, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
*affine, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, normGrad, 0.001);
meancurv = math::MeanCurvature::result(
*affine, inAccessor, xyz);
normGrad = math::MeanCurvature::normGrad(
*affine, inAccessor, xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, normGrad, 0.001);
}
}
void
TestMeanCurvature::testWSMeanCurvatureStencil()
{
using namespace openvdb;
using math::AffineMap;
using math::TranslationMap;
using math::UniformScaleMap;
typedef FloatGrid::ConstAccessor AccessorType;
{ // unit-sized voxels
FloatGrid::Ptr grid = createGrid(/*background=*/5.0);
FloatTree& tree = grid->tree();
const openvdb::Coord dim(64,64,64);
const openvdb::Vec3f center(35.0f, 30.0f, 40.0f);//i.e. (35,30,40) in index space
const float radius=0.0f;
unittest_util::makeSphere(
dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
CPPUNIT_ASSERT(!tree.empty());
Coord xyz(35,30,30);
math::SecondOrderDenseStencil dense_2nd(*grid);
math::FourthOrderDenseStencil dense_4th(*grid);
math::SixthOrderDenseStencil dense_6th(*grid);
dense_2nd.moveTo(xyz);
dense_4th.moveTo(xyz);
dense_6th.moveTo(xyz);
AccessorType::ValueType meancurv;
AccessorType::ValueType normGrad;
AffineMap affine;
meancurv = math::MeanCurvature::result(
affine, dense_2nd);
normGrad = math::MeanCurvature::normGrad(
affine, dense_2nd);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
meancurv = math::MeanCurvature::result(
affine, dense_4th);
normGrad = math::MeanCurvature::normGrad(
affine, dense_4th);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
UniformScaleMap uniform;
meancurv = math::MeanCurvature::result(
uniform, dense_6th);
normGrad = math::MeanCurvature::normGrad(
uniform, dense_6th);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, normGrad, 0.001);
xyz.reset(35,10,40);
dense_6th.moveTo(xyz);
TranslationMap trans;
meancurv = math::MeanCurvature::result(
trans, dense_6th);
normGrad = math::MeanCurvature::normGrad(
trans, dense_6th);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, normGrad, 0.001);
}
{ // non-unit sized voxel
double voxel_size = 0.5;
FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0);
grid->setTransform(math::Transform::createLinearTransform(voxel_size));
CPPUNIT_ASSERT(grid->empty());
const openvdb::Coord dim(32,32,32);
const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space
const float radius=10.0f;
unittest_util::makeSphere(
dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
AccessorType::ValueType meancurv;
AccessorType::ValueType normGrad;
Coord xyz(20,16,20);
math::SecondOrderDenseStencil dense_2nd(*grid);
math::FourthOrderDenseStencil dense_4th(*grid);
math::SixthOrderDenseStencil dense_6th(*grid);
dense_2nd.moveTo(xyz);
dense_4th.moveTo(xyz);
dense_6th.moveTo(xyz);
AffineMap affine(voxel_size*math::Mat3d::identity());
meancurv = math::MeanCurvature::result(
affine, dense_2nd);
normGrad = math::MeanCurvature::normGrad(
affine, dense_2nd);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001);
meancurv = math::MeanCurvature::result(
affine, dense_4th);
normGrad = math::MeanCurvature::normGrad(
affine, dense_4th);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001);
UniformScaleMap uniform(voxel_size);
meancurv = math::MeanCurvature::result(
uniform, dense_6th);
normGrad = math::MeanCurvature::normGrad(
uniform, dense_6th);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, normGrad, 0.001);
}
{ // NON-UNIFORM SCALING AND ROTATION
Vec3d voxel_sizes(0.25, 0.45, 0.75);
FloatGrid::Ptr grid = FloatGrid::create();
math::MapBase::Ptr base_map( new math::ScaleMap(voxel_sizes));
// apply rotation
math::MapBase::Ptr rotated_map = base_map->preRotate(1.5, math::X_AXIS);
grid->setTransform(math::Transform::Ptr(new math::Transform(rotated_map)));
CPPUNIT_ASSERT(grid->empty());
const openvdb::Coord dim(32,32,32);
const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space
const float radius=10.0f;
unittest_util::makeSphere(
dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
AccessorType::ValueType meancurv;
AccessorType::ValueType normGrad;
Coord xyz(20,16,20);
math::SecondOrderDenseStencil dense_2nd(*grid);
math::FourthOrderDenseStencil dense_4th(*grid);
dense_2nd.moveTo(xyz);
dense_4th.moveTo(xyz);
Vec3d location = grid->indexToWorld(xyz);
double dist = (center - location).length();
AffineMap::ConstPtr affine = grid->transform().map();
meancurv = math::MeanCurvature::result(
*affine, dense_2nd);
normGrad = math::MeanCurvature::normGrad(
*affine, dense_2nd);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, normGrad, 0.001);
meancurv = math::MeanCurvature::result(
*affine, dense_4th);
normGrad = math::MeanCurvature::normGrad(
*affine, dense_4th);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, meancurv, 0.001);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/dist, normGrad, 0.001);
}
}
void
TestMeanCurvature::testMeanCurvatureTool()
{
using namespace openvdb;
FloatGrid::Ptr grid = createGrid(/*background=*/5.0);
FloatTree& tree = grid->tree();
const openvdb::Coord dim(64,64,64);
const openvdb::Vec3f center(35.0f, 30.0f, 40.0f);//i.e. (35,30,40) in index space
const float radius=0.0f;
unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
CPPUNIT_ASSERT(!tree.empty());
FloatGrid::Ptr curv = tools::meanCurvature(*grid);
FloatGrid::ConstAccessor accessor = curv->getConstAccessor();
Coord xyz(35,30,30);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, accessor.getValue(xyz), 0.001);
xyz.reset(35,10,40);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/20.0, accessor.getValue(xyz), 0.001);
}
void
TestMeanCurvature::testMeanCurvatureMaskedTool()
{
using namespace openvdb;
FloatGrid::Ptr grid = createGrid(/*background=*/5.0);
FloatTree& tree = grid->tree();
const openvdb::Coord dim(64,64,64);
const openvdb::Vec3f center(35.0f, 30.0f, 40.0f);//i.e. (35,30,40) in index space
const float radius=0.0f;
unittest_util::makeSphere(dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
CPPUNIT_ASSERT(!tree.empty());
const openvdb::CoordBBox maskbbox(openvdb::Coord(35, 30, 30), openvdb::Coord(41, 41, 41));
BoolGrid::Ptr maskGrid = BoolGrid::create(false);
maskGrid->fill(maskbbox, true/*value*/, true/*activate*/);
FloatGrid::Ptr curv = tools::meanCurvature(*grid, *maskGrid);
FloatGrid::ConstAccessor accessor = curv->getConstAccessor();
// test inside
Coord xyz(35,30,30);
CPPUNIT_ASSERT(maskbbox.isInside(xyz));
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/10.0, accessor.getValue(xyz), 0.001);
// test outside
xyz.reset(35,10,40);
CPPUNIT_ASSERT(!maskbbox.isInside(xyz));
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, accessor.getValue(xyz), 0.001);
}
void
TestMeanCurvature::testOldStyleStencils()
{
using namespace openvdb;
{// test of level set to sphere at (6,8,10) with R=10 and dx=0.5
FloatGrid::Ptr grid = FloatGrid::create(/*backgroundValue=*/5.0);
grid->setTransform(math::Transform::createLinearTransform(/*voxel size=*/0.5));
CPPUNIT_ASSERT(grid->empty());
const openvdb::Coord dim(32,32,32);
const openvdb::Vec3f center(6.0f, 8.0f, 10.0f);//i.e. (12,16,20) in index space
const float radius=10.0f;
unittest_util::makeSphere(
dim, center, radius, *grid, unittest_util::SPHERE_DENSE);
CPPUNIT_ASSERT(!grid->empty());
CPPUNIT_ASSERT_EQUAL(dim[0]*dim[1]*dim[2], int(grid->activeVoxelCount()));
math::CurvatureStencil cs(*grid);
Coord xyz(20,16,20);//i.e. 8 voxel or 4 world units away from the center
cs.moveTo(xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/4.0, cs.meanCurvature(), 0.01);// 1/distance from center
CPPUNIT_ASSERT_DOUBLES_EQUAL(
1.0/4.0, cs.meanCurvatureNormGrad(), 0.01);// 1/distance from center
xyz.reset(12,16,10);//i.e. 10 voxel or 5 world units away from the center
cs.moveTo(xyz);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0/5.0, cs.meanCurvature(), 0.01);// 1/distance from center
CPPUNIT_ASSERT_DOUBLES_EQUAL(
1.0/5.0, cs.meanCurvatureNormGrad(), 0.01);// 1/distance from center
}
}
// Copyright (c) 2012-2013 DreamWorks Animation LLC
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
openvdb/unittest/TestFile.cc 0000644 0000000 0000000 00000222674 12252453157 015141 0 ustar root root ///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2012-2013 DreamWorks Animation LLC
//
// All rights reserved. This software is distributed under the
// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
//
// Redistributions of source code must retain the above copyright
// and license notice and the following restrictions and disclaimer.
//
// * Neither the name of DreamWorks Animation nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
//
///////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include // for tbb::this_tbb_thread::sleep()
#include
#include
#include
#include
#include
#include
#include // for tools::sdfToFogVolume()
#include
#include
#include "util.h" // for unittest_util::makeSphere()
#include