mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-14 04:50:05 +00:00
c399655dcc
Sync'ing the 'dev' branch with the 'feature_3.0dev' branch at commit 68c6d11fc36761ae1a5e6cdc3457be16f2e9704a The branch 'feature_3.0dev' is now locked and preserved for historical purposes.
256 lines
16 KiB
Plaintext
256 lines
16 KiB
Plaintext
This text file is written to accompany the opensubdiv/vtr source during review. It is intended to
|
|
give a high level overview of the organization of the source, the rationale for various decisions,
|
|
as well as highlight open issues or situations where the choices made thus far warrant further
|
|
deliberation.
|
|
|
|
Vtr is the newly proposed layer to support the "vectorized topological representation" of a mesh
|
|
and its associated refinement. Vtr is intended for internal use and is currently accessed through
|
|
the public FarTopologyRefiner class and its Factory class that internally transforms an instance of
|
|
a client's mesh into Vtr form. FarTopologyRefiner is intended to be one of a set of modular, table-
|
|
oriented classes made available for public use in the Far layer. As such, its debatable whether
|
|
the Vtr classes that support FarTopologyRefiner should be part of Far or the new Vtr layer. So we
|
|
are prepared to rename all Vtr classes as Far in future if necessary.
|
|
|
|
Currently there are three header files containing the following:
|
|
|
|
1) Common Vtr utilities
|
|
2) Level - a class representing complete topology for a level
|
|
3) Refinement - a class mapping a parent to a child level
|
|
|
|
which will be reviewed below. A synopsis of each provides some of the motivation behind the
|
|
contents along with current issues being addressed. More details may be provided in the header
|
|
itself.
|
|
|
|
Vtr is very much a work in progress. There is still considerable functionality to be added and
|
|
potentially more performance tuning to be done. Based on current and significant performance
|
|
improvements of the refinement process over Hbr though, completing the desired functionality is
|
|
currently the priority. Work to be done includes:
|
|
|
|
- support for sparse (feature-adaptive) refinement
|
|
- addition of more per-component tags to propogate with refinement (e.g. holes, etc.)
|
|
- support for tri-split refinement required by Loop subdivision
|
|
- specializations for regular levels and refinements
|
|
|
|
Since intended for internal use, we can continue to take liberties with the interfaces for the near
|
|
term, e.g. allowing direct member access as needed ala FarSubdivisionTables. The FarTopologyRefiner
|
|
class and its Factory class will be the public interface to the functionality provided here, and so
|
|
they warrant far more attention (and will be discussed as part of proposed Far changes/additions).
|
|
|
|
|
|
1) Common Vtr utilities:
|
|
|
|
Header:
|
|
vtr/types.h
|
|
|
|
Synopsis:
|
|
The style of the existing OpenSubdiv code is to nest the definitions of utility types and
|
|
classes in other classes. There are several in Vtr that are used equally by multiple classes, and
|
|
its debatable if they belong in one or another. So currently <vtr/types.h> includes a number of
|
|
such declarations for public use within Vtr (or Far). These include:
|
|
|
|
- declarations of types used for component indices, sharpness, etc.
|
|
- constant and inline method for testing index validity
|
|
- declarations of commonly used array types
|
|
|
|
These could arguably be moved into the next class discussed (i.e. Level), but as with the choice
|
|
of whether functionality belongs in Vtr or Far or not, but bigger issue at hand is the nature of
|
|
the functionality itself.
|
|
|
|
With data in Vtr, Far and elsewhere stored as collections of arrays aggregated into much larger
|
|
arrays, access to these sets of arrays within a larger context is frequent, and so making it both
|
|
convenient and efficient is of value. A generic "array interface" class was made available to
|
|
support this (and, if ultimately warranted, its debatable whether it should be in Sdc or Vtr).
|
|
This class is nothing more than a pair that defines the extent of an array along with a subset of
|
|
the interface of std::vector for accessing its entries -- it does not own the data that it refers
|
|
to but simply allows access to its elements. Arguably we could pass pairs of values around --
|
|
either begin/end or begin/size -- but the frequency of such occurrences is considerable and it
|
|
doubles the number of function parameters in many cases (particularly those requiring more than
|
|
one array) so declaring a simple class for this purpose makes a big difference to code readability.
|
|
|
|
The term "Vector" is used for types defined in terms of std::vector and "Array" (or "Accessor" and
|
|
"Modifier" for const/non-const interfaces -- a personal style I adopted out of habit and am happy
|
|
to drop) for types defined in terms of these array interfaces. The two main Vtr classes described
|
|
below make heavy use of these.
|
|
|
|
Questions:
|
|
- what definitions really need to be separate from other Vtr classes?
|
|
- use of "int" versus "unsigned" int for Index (unsigned allows twice the size)?
|
|
- usefulness and location of "array interface" types?
|
|
|
|
|
|
2) Level:
|
|
|
|
Header:
|
|
vtr/level.h
|
|
|
|
Synopsis:
|
|
Level is a complete topological description of a subdivision level, with the topological
|
|
relations, sharpness values and component tags all stored in vectors (literally std::vectors,
|
|
but easily changed via typedefs). There are no classes or objects for the mesh component types
|
|
(i.e. faces, edges and vertices) but simply an integer index to identify each. It can be viewed
|
|
as a structure-of-arrays representation of the topology -- any property related to a particular
|
|
component is stored in an array and accessible using the index identifying that component. So
|
|
with no classes the for the components, its difficult to say what constitutes a "vertex" or a
|
|
"face" -- they are each the sum of all the fields scattered amongst the many vectors included.
|
|
|
|
Level represents a single level of a potential hierarchy and is capable of representing the
|
|
complete base mesh. There are no members that relate data in one level to any other, either below
|
|
or above. As such, any Level can be used as the base level for a new subdivision hierarchy
|
|
(potentially more than one). All relationships between separate levels are maintained in the
|
|
Refinement class that follows.
|
|
|
|
Level requires the definition of and associations between a fixed set of indexable components
|
|
for all three component types, i.e. an explicit edge list in addition to the expected set of
|
|
vertices and faces. There are no explicit component objects in the representation -- only an
|
|
integer index (Index) identifying each component within the set and data associated with that
|
|
component in the various vectors. The topology is stored as six sets of incident relations
|
|
between the components -- two each for the two other component types incident each component
|
|
type, i.e.:
|
|
|
|
- for each face, its incident vertices and incident edges
|
|
- for each edge, its incident vertices and incident faces
|
|
- for each vertex, its incident edges and incident faces
|
|
|
|
I am loose with my terminology here and use "incident" when "adjacent" may be more technically
|
|
correct. The collection of incidence relations is a vectorized variation of AIF (the "Adjacency
|
|
and Incidence Framework"). The set of these six incidence relations is not minimal (only four
|
|
are required, but that set excludes the most desired face-vertex relation) but all six are kept
|
|
and maintained to facilitate faster refinement. While the sizes of several vectors are directly
|
|
proportional to the number of vertices, edges or faces to which the data is associated, the
|
|
sizes of some of the vectors for these relations is more cumulative and so additional vectors of
|
|
offsets is required (typical of the face-vertex list commonly used as the minimal definition of
|
|
mesh topology). The sizes and offsets of each set of incident components in these larger arrays
|
|
is a pattern we also see in the FarSubdivisionTables.
|
|
|
|
Vectors for the sharpness values associated with crease edges and corner vertices are included
|
|
(and so sized according to the number of edges and vertices), along with additional tags for the
|
|
components that may be helpful to refinement (i.e. the type of subdivision Rule associated with
|
|
each vertex).
|
|
|
|
In general, this is not that much different from what the FarSubdivisionTables includes. It does
|
|
differ from FarSubdivisionTables in that:
|
|
|
|
- it includes topology and associated data only for a single level
|
|
- the face-edge relation (edges incident each face) is included to support refinement
|
|
- incidence relations are ordered based on the component in a level, not child vertex
|
|
- there is no sorting or other permutation based on any computation kernels required
|
|
|
|
It was mentioned that there is no data relating a level to any other, but that is not yet entirely
|
|
true. The only suggestion that an instance of Level is part of any greater collection is an
|
|
integer "_depth" member. This was included to indicate when the topology of a level might be
|
|
regular or not wrt some subdivision scheme (knowing that all faces in a level are quads or tris
|
|
can allow us to specialize and reduce memory). While useful, there are other ways to indicate
|
|
this and so "_depth" is likely to be replaced with other members that serve the same purpose.
|
|
|
|
A Level is really just a container for data in a subdivision level, and so its public methods
|
|
are primarily to access that data. Modification of the data is protected and only made available
|
|
to classes that are intended to construct Levels -- currently the Far factor class that is
|
|
responsible for building the base level, and the Refinement class that constructs subsequent
|
|
levels during refinement.
|
|
|
|
One of the advantages in storing data in what is essentially a structure-of-arrays, rather than
|
|
the array-of-structures more typical of topological representations, is that we can be more
|
|
selective about memory usage in some cases. Particularly in the case of uniform refinement, when
|
|
the data in subsequent levels is typically 4x its predecessor, we can minimize what we either
|
|
generate or keep around at each level. For instance, if only a face-list is required at the
|
|
finest level, we only need to generate one of the six topological relations -- the vertices
|
|
incident each face. So when we do keep Levels around in memory (as is the case with the
|
|
FarTopologyRefiner) we do have do have the opportunity to prune what is not strictly necessary
|
|
after the refinement. Just as with construction, whatever classes are privileged to construct
|
|
a Level are likely those that will be privileged to prune its contents when needed.
|
|
|
|
Since Level is purely topological, there is no template parameter required as with the case
|
|
of HbrMesh<T>. With no template instantiation required, the source is defined in a .cpp file
|
|
corresponding to the header, rather than in the header itself. Other than the utility types
|
|
defined in a separate header, this is true of all Vtr source.
|
|
|
|
Upcoming work:
|
|
- the addition of more tags (packed into bitfields) for each component to help support
|
|
sparse refinement and any needs that may arise
|
|
- identifying opportunities to prune unused members after refinement
|
|
|
|
Questions:
|
|
- is it worth aggregating the many vector members into a smaller set at this point (and
|
|
so using the array interface for the many members sharing the same vector)?
|
|
|
|
|
|
3) Refinement:
|
|
|
|
Associated headers:
|
|
vtr/refinement.h:
|
|
|
|
Synopsis:
|
|
While Level contains the topology for a subdivision level, Refinement is responsible
|
|
for creating a new level via refinement of an existing one, and for maintaining the relationships
|
|
between the components in the parent and child levels. So a simplified view of a subdivision
|
|
hierarchy with Vtr is a set of Levels with a Refinement between each successive pair.
|
|
|
|
Refinement is a friend of Level and will populate a child level from a parent given a set of
|
|
refinement parameters (eventually). Currently only uniform refinement is implemented, but work to
|
|
support more general "selective" refinement is underway. Feature-adaptive refinement is just one
|
|
form of selective refinement -- the criteria being the topological features of interest (creases
|
|
and extra-ordinary vertices). The intent is to provide greater generality to facilitate the
|
|
refinement of particular regions of interest or more dynamic/adaptive needs.
|
|
|
|
The topology of the refinement, or rather the topology of both the refinement and the Level
|
|
level that it populates, is determined by a trait of the desired subdivision scheme applied --
|
|
currently only quad-splitting is supported for Catmark and Bilinear, but tri-splitting will
|
|
eventually need to be added for Loop. So there is no specializing the refinement in terms of
|
|
subdivision scheme, only in terms of the topological splitting associated with it. There are
|
|
opportunities to reduce both the storage of topology in Level and the expense of refinement
|
|
in VtrRefinment for cases when the mesh has become regular wrt the subdivided topology (i.e. all
|
|
quads or all tris) but these are not yet being explored. Subclassing Refinement for the two
|
|
current topological splitting types is currently under consideration, but more immediate needs
|
|
for feature-adaptive support for Catmark will likely defer any efforts in support of Loop.
|
|
|
|
While Refinement populates a new child Level as part of its refinement operation, it also
|
|
accumulates the relationships between the parent and child level (and as with Level, this data
|
|
is stored in vectors indexable by the components). Currently the associations between components
|
|
in the two levels is uni-directional -- child components are associated with incident components
|
|
of a parent component based on the parent components topology, so we have a parent-to-child
|
|
mapping (one to many). We tried to avoid having a complementary child-to-parent mapping to
|
|
reduce memory (particularly in the case of uniform refinement) as it often was not necessary,
|
|
but there is a growing need for it in some cases once the hierarchy is constructed, so it is
|
|
likely it will be added.
|
|
|
|
One of the advantages of the structure-of-arrays representation in both Level and Refinement
|
|
is that we can make more dynamic choices about what type of data we choose to allocate and use
|
|
based on needs. For instance, we can choose between maintaining the parent-child or child-parent
|
|
mapping in Refinement, or both if needed, and we can remove one if no longer necessary. An
|
|
active example of this is uniform refinement -- if we only require the face-vertex list at the
|
|
finest subdivision level, there is no need to generate a complete topological description of that
|
|
level (as would be required of more traditional representations), and given that level is 4x the
|
|
magnitude of its parent, the savings are considerable.
|
|
|
|
Currently there is nothing specific to a subdivision scheme in the refinement other than the
|
|
type of topological splitting to apply. The refinement does subdivide sharpness values for
|
|
creasing, but that too is independent of scheme. That may not last forever though -- at least
|
|
two areas where specialization according to scheme may be necessary are:
|
|
|
|
- computation (but not application) of stencil weights for refined vertices
|
|
- identification of extra-ordinary feature as part of feature-adaptive analysis
|
|
|
|
In both cases though, these specializations may end up external to Vtr. Stencil weights may only
|
|
be needed once for application to a single set of data, and that may be managed by a separate
|
|
class in Far. (Currently I optionally compute and store them in Refinement as part of an
|
|
experiment for Manuel, but they are not intended to persist, and so are hard-coded to be for
|
|
Catmark only for the duration of the experiment.) If stencil weights are to be computed and
|
|
stored -- as with FarSubdivisionTables -- that may also be done where needed. Similarly, the
|
|
feature-adaptive analysis is also likely to be part of the FarTopologyRefiner, where the refinement
|
|
parameters are publicly specified and internally translated into Vtr operations to be applied.
|
|
|
|
Given that Vtr and Far might eventually be merged, whether these specializations for scheme occur
|
|
in Vtr or Far is less important -- the point here is that Refinement should not require them.
|
|
|
|
Like Level, Refinement is also purely topological, free of any template parameter for a
|
|
vertex type, and so its source is defined in a .cpp file corresponding to its header.
|
|
|
|
Upcoming work:
|
|
- support sparse refinement (marking/selecting components, required incident components, etc.)
|
|
- provide member vectors for the inverse mapping of components (i.e. child-to-parent)
|
|
- redefine component traversal from parent to child where necessary
|
|
- support tri-split topology in addition to current quad-split for Catmark and Bilinear
|
|
|
|
Questions:
|
|
- ?
|