mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-25 21:10:08 +00:00
24b63c951f
- new section for SurfaceFactory caching and thread-safety - new section for deriving subclasses of SurfaceFactory
1107 lines
56 KiB
ReStructuredText
1107 lines
56 KiB
ReStructuredText
..
|
|
Copyright 2022 Pixar
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
with the following modification; you may not use this file except in
|
|
compliance with the Apache License and the following modification to it:
|
|
Section 6. Trademarks. is deleted and replaced with:
|
|
|
|
6. Trademarks. This License does not grant permission to use the trade
|
|
names, trademarks, service marks, or product names of the Licensor
|
|
and its affiliates, except as required to comply with Section 4(c) of
|
|
the License and to reproduce the content of the NOTICE file.
|
|
|
|
You may obtain a copy of the Apache License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the Apache License with the above modification is
|
|
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
KIND, either express or implied. See the Apache License for the specific
|
|
language governing permissions and limitations under the Apache License.
|
|
|
|
|
|
BFR Overview
|
|
------------
|
|
|
|
.. contents::
|
|
:local:
|
|
:backlinks: none
|
|
|
|
Base Face Representation (Bfr)
|
|
==============================
|
|
|
|
*Bfr* is an alternate API layer that treats a subdivision mesh provided
|
|
by a client as a `piecewise parameteric surface primitive
|
|
<subdivision_surfaces.html#piecewise-parametric-surfaces>`__.
|
|
|
|
The name *Bfr* derives from the fact that the concepts and classes of
|
|
this interface all relate to the "base faces" of a mesh. Concepts such
|
|
as *parameterization*, *evaluation* and *tessellation* all refer to and
|
|
are embodied by classes that deal with a specific face of the original
|
|
unrefined mesh.
|
|
|
|
The *Bfr* interfaces allow the limit surface for a single face to be
|
|
identified and evaluated independently of all other faces without any
|
|
global pre-processing. While concepts and utilities from the *Far*
|
|
interface are used internally, the details of their usage is hidden.
|
|
There is no need to coordinate adaptive refinement with tables of
|
|
patches, stencils, Ptex indices, patch maps, etc.
|
|
|
|
The resulting evaluation interface is much simpler, more flexible and
|
|
more scalable than those assembled with the *Far* classes -- providing
|
|
a preferable alternative for many CPU-based use cases.
|
|
|
|
The main classes in *Bfr* include:
|
|
|
|
+------------------+----------------------------------------------------------+
|
|
| SurfaceFactory | A light-weight interface to a mesh that constructs |
|
|
| | pieces of limit surface for specified faces of a mesh |
|
|
| | in the form of Surfaces. |
|
|
+------------------+----------------------------------------------------------+
|
|
| Surface | A class encapsulating the limit surface of a face with |
|
|
| | methods for complete parametric evaluation. |
|
|
+------------------+----------------------------------------------------------+
|
|
| Parameterization | A simple class defining the available parameterizations |
|
|
| | of faces and for identifying that of a particular face. |
|
|
+------------------+----------------------------------------------------------+
|
|
| Tessellation | A simple class providing information about a specified |
|
|
| | tessellation pattern for a given Parameterization. |
|
|
+------------------+----------------------------------------------------------+
|
|
|
|
*Bfr* is well suited to cases where evaluation of the mesh may be sparse,
|
|
dynamically determined or iterative (Newton, gradient descent, etc).
|
|
It is not intended to replace the cases for which *Far* has been designed
|
|
(i.e. repeated evaluation of a fixed set of points) but is intended to
|
|
complement them. While simplicity, flexibility and reasonable performance
|
|
were the main goals of *Bfr*, its current implementation often outperforms
|
|
the table-based solutions of *Far* for many common use cases -- both in terms
|
|
of execution time and memory use.
|
|
|
|
An area that *Bfr* does not address, and where *Far* remains more suited,
|
|
is capturing a specific representation of the limit surface for external
|
|
use. *Bfr* intentionally keeps internal implementation details private to
|
|
allow future improvements or extensions. Those representation details may
|
|
be publicly exposed in future releases, but until then, use of *Far* is
|
|
required for such purposes.
|
|
|
|
----
|
|
|
|
.. _bfr-navlink-evaluation:
|
|
|
|
Evaluation
|
|
==========
|
|
|
|
Since subdivision surfaces are piecewise parametric surfaces, the main
|
|
operation of interest is evaluation.
|
|
|
|
*Bfr* deals with the limit surface of a mesh as a whole by associating
|
|
pieces of surface with each face of the mesh. These pieces of surface
|
|
are referred to in the context of *Bfr* simply as "surfaces" and
|
|
represented by Bfr::Surface.
|
|
|
|
Each face of the mesh has an implicit local 2D parameterization and
|
|
individual coordinates of that parameterization are used to evaluate its
|
|
corresponding Surface. In general, 3- and 4-sided faces use the same
|
|
parameterizations for quad and triangular patches used elsewhere in
|
|
OpenSubdiv:
|
|
|
|
+--------------------------------------+--------------------------------------+
|
|
| .. image:: images/param_uv.png | .. image:: images/param_uv2xyz.png |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/param_uv.png | :target: images/param_uv2xyz.png |
|
|
+--------------------------------------+--------------------------------------+
|
|
|
|
Parameterizations are defined for other faces (more details to follow), so
|
|
Surfaces for all faces can be evaluated given any 2D parametric coordinate
|
|
of its face.
|
|
|
|
Given an instance of a mesh, usage first requires the creation of a
|
|
Bfr::SurfaceFactory corresponding to that mesh -- from which Surfaces
|
|
can then be created for evaluation. Construction of the SurfaceFactory
|
|
involves no pre-processing and Surfaces can be created and discarded
|
|
as needed. The processes of constructing and evaluating Surfaces are
|
|
described in more detail below.
|
|
|
|
Bfr::SurfaceFactory
|
|
*******************
|
|
|
|
Construction of Bfr::Surfaces requires an instance of Bfr::SurfaceFactory.
|
|
|
|
An instance of SurfaceFactory is a light-weight interface to an instance
|
|
of a mesh that requires little to no construction cost or memory. The
|
|
SurfaceFactory does no work until a Surface is requested for a particular
|
|
face -- at which point the factory inspects the mesh topology around that
|
|
face to assemble the Surface.
|
|
|
|
.. image:: images/bfr_eval_surfacefactory.png
|
|
:align: center
|
|
|
|
SurfaceFactory is actually a base class that is inherited to provide a
|
|
consistent construction interface for Surfaces. Subclasses are derived
|
|
to support a particular class of connected mesh -- to implement the
|
|
topology inspection around each face required to construct the Surface.
|
|
Use of these subclasses is very simple given the public interface of
|
|
SurfaceFactory, but defining such a subclass is not. That more complex
|
|
use case of SurfaceFactory will be described in detail later with other
|
|
more advanced topics.
|
|
|
|
In many cases, it is not necessary to explicitly define a subclass of
|
|
SurfaceFactory, as the tutorials for *Bfr* illustrate.
|
|
If already using OpenSubdiv for other reasons, a Far::TopologyRefiner
|
|
will have been constructed to represent the initial base mesh before
|
|
refinement. *Bfr* provides a subclass of SurfaceFactory using
|
|
Far::TopologyRefiner as the base mesh (ignoring any levels of
|
|
refinement) for immediate use in such cases.
|
|
|
|
For those cases when no connected mesh representation is available at
|
|
all (i.e. only raw, unconnected mesh data exists) construction of a
|
|
Far::TopologyRefiner provides a reasonably efficient connected mesh
|
|
representation (see the *Far* tutorials for construction details),
|
|
whose provided subclass for SurfaceFactory is then readily available.
|
|
|
|
Given the different interpolation types for mesh data (i.e. "vertex",
|
|
"varying" and "face-varying"), the common interface for SurfaceFactory
|
|
provides methods to construct Surfaces explicitly for all data types.
|
|
So for positions, the methods for "vertex" data must be used to obtain
|
|
the desired Surface, while for texture coordinates the methods for
|
|
"face-varying" are usually required, e.g.:
|
|
|
|
.. code:: c++
|
|
|
|
Surface * CreateVertexSurface( Index faceIndex) const;
|
|
Surface * CreateVaryingSurface( Index faceIndex) const;
|
|
Surface * CreateFaceVaryingSurface(Index faceIndex) const;
|
|
|
|
The Surfaces created by these construction methods may all be
|
|
distinct as the underlying representations of the Surfaces and the
|
|
indices of the data that define them will often differ. For
|
|
example, the position data may require a bicubic patch while the
|
|
face-varying texture data may be linear or a different type of
|
|
bicubic patch (given the different interpolation rules for
|
|
face-varying and the possibility of seams).
|
|
|
|
While the internal representations of the Surfaces constructed for
|
|
different data interpolation types may differ, since they are all
|
|
constructed as Surfaces, the functionality used to evaluate them is
|
|
identical.
|
|
|
|
Bfr::Surface
|
|
************
|
|
|
|
The Surface class encapsulates the piece of limit surface associated
|
|
with a particular face of the mesh. The term "surface" is used rather
|
|
than "patch" to emphasize that the Surface may itself be a piecewise
|
|
parametric surface composed of more than one patch (potentially
|
|
even a complex set of patches).
|
|
|
|
Surface is also a class template selected by floating point precision,
|
|
and so typically declared as Bfr::Surface<float>. Just as a simpler
|
|
type name is likely to be declared when used, the simple name Surface
|
|
will be used to refer to it here. And where code fragments may be
|
|
warranted, "float" will be substituted for the template parameter for
|
|
clarity.
|
|
|
|
Once created, there are two steps required to evaluate a Surface:
|
|
|
|
* preparation of associated data points from the mesh
|
|
* the actual calls to evaluation methods using these data points
|
|
|
|
The latter is straight-forward, but the former warrants a little more
|
|
explanation.
|
|
|
|
The shape of a Surface for a base face is influenced by the set of data
|
|
points associated with both the vertices of the face and a subset of
|
|
those in its immediate neighborhood. These "control points" are
|
|
identified when the Surface is initialized and are publicly available
|
|
for inspection if desired. The control points are sufficient to define
|
|
the Surface if the face and its neighborhood are regular, but any
|
|
irregularity (an extra-ordinary vertex, crease, etc.) usually requires
|
|
additional, intermediate points to be computed from those control points
|
|
in order to evaluate the Surface efficiently.
|
|
|
|
Having previously avoided use of the term "patch" in favor of "surface",
|
|
the term "patch points" is now used to refer to these intermediate points.
|
|
Patch points always include the control points as a subset and may be
|
|
followed by points needed for any additional patches required to represent
|
|
a more complex Surface. While the patch points are assembled in a local
|
|
array for direct use by the Surface, the control points can either be
|
|
gathered and accessed locally or indexed from buffers associated with the
|
|
mesh for other purposes (e.g. computing a bounding box of the Surface):
|
|
|
|
.. image:: images/bfr_eval_surface.png
|
|
:align: center
|
|
|
|
Once the patch points for a Surface are prepared, they can be passed to
|
|
the main evaluation methods with the desired parametric coordinates.
|
|
As previously noted, since the Surface class is a template for floating
|
|
point precision, evaluation is supported in single or double precision
|
|
by constructing a Surface for the desired precision. Evaluation methods
|
|
are overloaded to obtain simply position or including all first or second
|
|
derivatives. So preparation and evaluation can be achieved with the
|
|
following:
|
|
|
|
.. code:: c++
|
|
|
|
// Preparing patch points:
|
|
void PreparePatchPoints(
|
|
float const * meshPoints, PointDescriptor meshPointDescriptor,
|
|
float * patchPoints, PointDescriptor patchPointDescriptor) const;
|
|
|
|
// Evaluating position and 1st derivatives:
|
|
void Evaluate(float const uv[2],
|
|
float const * patchPoints, PointDescriptor patchPointDescriptor,
|
|
float * P, float * dPdu, float * dPdv) const;
|
|
|
|
The PointDescriptor class here is a simple struct defining the size and
|
|
stride of the associated array of points. Any use of mesh points, control
|
|
points or patch points generally requires an accompanying descriptor.
|
|
|
|
Depending on the complexity of the limit surface, this preparation of
|
|
patch points can be costly -- especially if only evaluating the Surface
|
|
once or twice. In such cases, it is worth considering evaluating
|
|
"limit stencils", i.e. sets of coefficients that combine the original
|
|
control vertices of the mesh without requiring the computation of
|
|
intermediate values.
|
|
The cost of evaluating stencils is considerably higher than direct
|
|
evaluation, but that added overhead is often offset by avoiding the
|
|
use of patch points.
|
|
|
|
Surfaces should be considered a class for transient use as retaining
|
|
them for longer term usage can reduce their benefits. The relatively
|
|
high cost of initialization of irregular Surfaces can be a deterrent
|
|
and often motivates their retention despite increased memory costs.
|
|
Retaining all Surfaces of a mesh for random sampling is a situation
|
|
that should be undertaken with caution and will be discussed in more
|
|
detail later with other advanced topics.
|
|
|
|
----
|
|
|
|
.. _bfr-navlink-parameterization:
|
|
|
|
Parameterization
|
|
================
|
|
|
|
Each face of a mesh has an implicit local 2D parameterization whose 2D
|
|
coordinates are used to evaluate the Surface for that face.
|
|
|
|
*Bfr* adopts the parameterizations defined elsewhere in OpenSubdiv for
|
|
quadrilateral and triangular patches, for use quadrilateral and
|
|
triangular faces:
|
|
|
|
+----------------------------------------------+----------------------------------------------+
|
|
| .. image:: images/bfr_param_patch_quad.png | .. image:: images/bfr_param_patch_tri.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_param_patch_quad.png | :target: images/bfr_param_patch_tri.png |
|
|
+----------------------------------------------+----------------------------------------------+
|
|
|
|
But the parameterization of a face is also dependent on the subdivision
|
|
scheme applied to it.
|
|
|
|
Subdivision schemes that divide faces into quads are ultimately represented
|
|
by quadrilateral patches. So a face that is a quad can be parameterized as
|
|
a single quad, but other non-quad faces are parameterized as a set of quad
|
|
"sub-faces", i.e. faces resulting from subdivision:
|
|
|
|
+-------------------------------------------+
|
|
| .. image:: images/bfr_param_subfaces.png |
|
|
| :align: center |
|
|
| :width: 100% |
|
|
| :target: images/bfr_param_subfaces.png |
|
|
+-------------------------------------------+
|
|
|
|
A triangle subdivided with a quad-based scheme (e.g. Catmull-Clark) will
|
|
therefore not have the parameterization of the triangular patch indicated
|
|
previously, but another defined by its quad sub-faces illustrated above
|
|
(to be described in more detail below).
|
|
|
|
Subdivision schemes that divide faces into triangles are currently restricted
|
|
to triangles only, so all faces are parameterized as single triangles. (If
|
|
Loop subdivision is extended to non-triangles in future, a parameterization
|
|
involving triangular sub-faces will be necessary.)
|
|
|
|
Note that triangles are often parameterized elsewhere in terms of barycentric
|
|
coordinates (u,v,w) where *w = 1 - u - v*. As is the case elsewhere in
|
|
OpenSubdiv, *Bfr* considers parametric coordinates as 2D (u,v) pairs for all
|
|
purposes. All faces have an implicit 2D local parameterization and all
|
|
interfaces requiring parametric coordinates consider only the (u,v) pair.
|
|
If interaction with some other tool set requiring barycentric coordinates
|
|
for triangles is necessary, it is left to users to compute the implicit *w*
|
|
accordingly.
|
|
|
|
Bfr::Parameterization
|
|
*********************
|
|
|
|
Bfr::Parameterization is a simple class that fully defines the parameterization
|
|
for a particular face.
|
|
|
|
An instance of Parameterization is fully defined on construction given the
|
|
"size" of a face and the subdivision scheme applied to it (where the face
|
|
"size" is its number of vertices/edges). Since any parameterization of
|
|
*N*-sided faces requires *N* in some form, the face size is stored as a member
|
|
and made publicly available.
|
|
|
|
Each Surface has the Parameterization of its face assigned internally as part
|
|
of its construction, and that is used internally by the Surface in many of its
|
|
methods. The need to deal directly with the explicit details of the
|
|
Parameterization class is not generally necessary. Often it is sufficient
|
|
to retrieve the Parameterization from a Surface for use in some other context
|
|
(e.g. passed to Bfr::Tessellation).
|
|
|
|
The enumerated type Parameterization::Type currently defines three kinds of
|
|
parameterizations -- one of which is assigned to each instance on construction
|
|
according to the properties of the face:
|
|
|
|
+---------------+--------------------------------------------------------------+
|
|
| QUAD | Applied to quadrilateral faces with a quad-based |
|
|
| | subdivision scheme (e.g. Catmark or Bilinear). |
|
|
+---------------+--------------------------------------------------------------+
|
|
| TRI | Applied to triangular faces with a triangle-based |
|
|
| | subdivision scheme (e.g. Loop). |
|
|
+---------------+--------------------------------------------------------------+
|
|
| QUAD_SUBFACES | Applied to non-quad faces with a quad-based subdivision |
|
|
| | scheme -- dividing the face into quadrilateral sub-faces. |
|
|
+---------------+--------------------------------------------------------------+
|
|
|
|
Parameterizations that involve subdivision into sub-faces, e.g. QUAD_SUBFACES,
|
|
may warrant some care as they are not continuous. Depending on how they are
|
|
defined, the sub-faces may be disjoint (e.g. *Bfr*) or overlap in parametric
|
|
space (e.g. Ptex). To help these situations, methods to detect the presence
|
|
of sub-faces and deal with their local parameterizations are made available.
|
|
|
|
Discontinuous Parameterizations
|
|
*******************************
|
|
|
|
When a face does not have a regular parameterization, the division of the
|
|
parameterization into sub-faces can create complications -- as noted and
|
|
addressed elsewhere in OpenSubdiv.
|
|
|
|
Bfr::Parameterization defines a quadrangulated sub-face parameterization
|
|
differently from the *Far* and *Osd* interfaces. For an *N*-sided face,
|
|
*Far* uses a parameterization adopted by Ptex. In this case, all quad
|
|
sub-faces are parameterized over the unit square and require an additional
|
|
index of the sub-face to identify them. So Ptex coordinates require three
|
|
values: the index and (u,v) of the sub-face.
|
|
|
|
To embed sub-face coordinates in a single (u,v) pair, *Bfr* tiles the
|
|
sub-faces in disjoint regions in parameter space. This tiling is similar
|
|
to the Udim convention for textures, where a UDim on the order of *sqrt(N)*
|
|
is used to preserve accuracy for increasing *N*:
|
|
|
|
+---------------------------------------------+------------------------------------------------------------+
|
|
| .. image:: images/bfr_param_subfaces_5.png | .. image:: images/bfr_param_subfaces_5_uv.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_param_subfaces_5.png | :target: images/bfr_param_subfaces_5_uv.png |
|
|
+---------------------------------------------+------------------------------------------------------------+
|
|
|
|
|
|
|
|
|
+--------------------------------------------------+--------------------------------------------------+
|
|
| .. image:: images/bfr_param_subfaces_3.png | .. image:: images/bfr_param_subfaces_3_uv.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_param_subfaces_3.png | :target: images/bfr_param_subfaces_3_uv.png |
|
|
+--------------------------------------------------+--------------------------------------------------+
|
|
|
|
Note also that the edges of each sub-face are of parametric length 0.5,
|
|
which results in a total parametric length of 1.0 for all base edges.
|
|
This differs again from Ptex, which parameterizes sub-faces with edge
|
|
lengths of 1.0, and so can lead to inconsistencies in parametric scale
|
|
(typically with derivatives) across edges of the mesh if not careful.
|
|
|
|
As previously mentioned, care may be necessary when dealing with the
|
|
discontinuities that exist in parameterizations with sub-faces. This is
|
|
particularly true if evaluating data at sampled locations of the face
|
|
and needing to evaluate at other locations interpolated from these.
|
|
|
|
+--------------------------------------------------+--------------------------------------------------+
|
|
| .. image:: images/bfr_param_subfaces_abc.png | .. image:: images/bfr_param_subfaces_abc_uv.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_param_subfaces_abc.png | :target: images/bfr_param_subfaces_abc_uv.png |
|
|
+--------------------------------------------------+--------------------------------------------------+
|
|
| Interpolation between parametric locations, e.g. A, B and C, should be avoided when discontinuous. |
|
|
+-----------------------------------------------------------------------------------------------------+
|
|
|
|
In many cases, dealing directly with coordinates of the sub-faces
|
|
is unavoidable, e.g. interpolating Ptex coordinates for sampling of
|
|
textures assigned explicitly to the sub-faces. Methods are provided
|
|
to convert from *Bfr*'s tiled parameterization to and from other
|
|
representations that use a local parameterization for each sub-face.
|
|
|
|
----
|
|
|
|
.. _bfr-navlink-tessellation:
|
|
|
|
Tessellation
|
|
============
|
|
|
|
Once a Surface can be evaluated it can be tessellated. Given a 2D
|
|
parameterization, a tessellation consists of two parts:
|
|
|
|
* a set of parametric coordinates sampling the Parameterization
|
|
* a set of faces connecting these coordinates that covers the
|
|
entire Parameterization
|
|
|
|
Once evaluated, the resulting set of sample points and the faces
|
|
connecting them effectively define a mesh for that parameterization.
|
|
|
|
For the sake of brevity both here and in the programming interface,
|
|
the parametric coordinates or sample points are referred to simply as
|
|
"coords" or "Coords" -- avoiding the term "points", which is already
|
|
a heavily overloaded term. Similarly the faces connecting the coords
|
|
are referred to as "facets" or "Facets" -- avoiding the term "face" to
|
|
avoid confusion with the base face of the mesh being tessellated.
|
|
|
|
*Bfr* provides a simple class to support a variety of tessellation patterns
|
|
for the different Parameterization types and methods for retrieving its
|
|
associated coords and facets. In many cases the patterns they define are
|
|
similar to those of GPU hardware tessellation -- which may be more familiar
|
|
to many -- but they do differ in several ways, as noted below.
|
|
|
|
Bfr::Tessellation
|
|
*****************
|
|
|
|
In *Bfr* a Tessellation is a simple class defined by a Parameterization and
|
|
a given set of tessellation rates (and a few additional options). These two
|
|
elements define a specific tessellation pattern for all faces sharing that
|
|
Parameterization. An instance of Tessellation can then be inspected to
|
|
identify all or subsets of its coords or facets.
|
|
|
|
The process of tessellation in other contexts usually generates triangular
|
|
facets, but that is not the case with *Bfr*. While producing triangular
|
|
facets is the default, options are available to have Tessellation include
|
|
quads in patterns for parameterizations associated with quad-based
|
|
subdivision schemes. For simple uniform patterns, these produce patterns
|
|
that are similar in topology to those resulting from subdivision:
|
|
|
|
+--------------------------------------------+--------------------------------------------+
|
|
| .. image:: images/bfr_tess_quad_quads.png | .. image:: images/bfr_tess_quad_tris.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_quad_quads.png | :target: images/bfr_tess_quad_tris.png |
|
|
+--------------------------------------------+--------------------------------------------+
|
|
| .. image:: images/bfr_tess_pent_quads.png | .. image:: images/bfr_tess_pent_tris.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_pent_quads.png | :target: images/bfr_tess_pent_tris.png |
|
|
+--------------------------------------------+--------------------------------------------+
|
|
| Tessellation of 4- and 5-sided faces of a quad-based scheme using quadrilateral facets |
|
|
| (left) and triangular (right) |
|
|
+-----------------------------------------------------------------------------------------+
|
|
|
|
The name "Tessellation" was chosen rather than "Tessellator" as it is a
|
|
passive class that simply holds information define its pattern. It doesn't
|
|
do much other than providing information about the pattern when requested.
|
|
A few general properties about the pattern are determined and retained on
|
|
construction, after which an instance is immutable. So it does not maintain
|
|
any additional state between queries.
|
|
|
|
In order to provide flexibility when dealing with tessellations of adjacent
|
|
faces, the coords arising from an instance of Tessellation are ordered and
|
|
are retrievable in ways to help identify points along edges that may be
|
|
shared between two or more faces. The coords of a Tessellation are generated
|
|
in concentric rings, beginning with the outer ring and starting with the first
|
|
vertex:
|
|
|
|
+---------------------------------------------+---------------------------------------------+
|
|
| .. image:: images/bfr_tess_quad_order.png | .. image:: images/bfr_tess_tri_order.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_quad_order.png | :target: images/bfr_tess_tri_order.png |
|
|
+---------------------------------------------+---------------------------------------------+
|
|
| Ordering of coords around boundary for quad and tri parameterizations. |
|
|
+-------------------------------------------------------------------------------------------+
|
|
|
|
Methods of the Tessellation class allow the coords associated with specific
|
|
vertices or edges to be identified, as well as providing the coords for the
|
|
entire ring around the boundary separately from those of the interior if
|
|
desired. While the ordering of coords in the interior is not defined (and
|
|
so not to be relied upon), the ordering of the boundary coords is
|
|
specifically fixed to support the correlation of potentially shared coords
|
|
between faces.
|
|
|
|
The Tessellation class is completely independent of the Surface class.
|
|
Tessellation simply takes a Parameterization and tessellation rates and
|
|
provides the coords and facets that define its pattern. So Tessellation can
|
|
be used in any other evaluation context where the Parameterizations are
|
|
appropriate.
|
|
|
|
Tessellation Rates
|
|
******************
|
|
|
|
For a particular Parameterization, the various tessellation patterns are
|
|
determined by one or more tessellation rates.
|
|
|
|
The simplest set of patterns uses a single tessellation rate and is said
|
|
to be "uniform", i.e. all edges and the interior of the face are split to
|
|
a similar degree:
|
|
|
|
+---------------------------------------------+---------------------------------------------+
|
|
| .. image:: images/bfr_tess_uni_quad_5.png | .. image:: images/bfr_tess_uni_quad_8.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_uni_quad_5.png | :target: images/bfr_tess_uni_quad_8.png |
|
|
+---------------------------------------------+---------------------------------------------+
|
|
| .. image:: images/bfr_tess_uni_tri_5.png | .. image:: images/bfr_tess_uni_tri_8.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_uni_tri_5.png | :target: images/bfr_tess_uni_tri_8.png |
|
|
+---------------------------------------------+---------------------------------------------+
|
|
| Uniform tessellation of a quadrilateral and triangle with rates of 5 and 8. |
|
|
+-------------------------------------------------------------------------------------------+
|
|
|
|
More complex non-uniform patterns allow the edges of the face to be split
|
|
independently from the interior of the face. Given rates for each edge, a
|
|
suitable uniform rate for the interior can be either inferred or specified
|
|
explicitly. These are typically referred to as the "outer rates" and the
|
|
"inner rate". (The single rate specified for a simple uniform tessellation
|
|
is essentially the specification of a single inner rate while the outer
|
|
rates for all edges are inferred as the same.)
|
|
|
|
+------------------------------------------------+------------------------------------------------+
|
|
| .. image:: images/bfr_tess_nonuni_quad_A.png | .. image:: images/bfr_tess_nonuni_quad_B.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_nonuni_quad_A.png | :target: images/bfr_tess_nonuni_quad_B.png |
|
|
+------------------------------------------------+------------------------------------------------+
|
|
| .. image:: images/bfr_tess_nonuni_tri_A.png | .. image:: images/bfr_tess_nonuni_tri_B.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_nonuni_tri_A.png | :target: images/bfr_tess_nonuni_tri_B.png |
|
|
+------------------------------------------------+------------------------------------------------+
|
|
| .. image:: images/bfr_tess_nonuni_pent_A.png | .. image:: images/bfr_tess_nonuni_pent_B.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_nonuni_pent_A.png | :target: images/bfr_tess_nonuni_pent_B.png |
|
|
+------------------------------------------------+------------------------------------------------+
|
|
| Non-uniform tessellation of a quadrilateral, triangle and 5-sided face |
|
|
| with various outer and inner rates. |
|
|
+-------------------------------------------------------------------------------------------------+
|
|
|
|
In the case of Parameterizations for quads, it is common elsewhere to
|
|
associate two inner rates with the opposing edges. So two separate
|
|
inner rates are available for quad parameterizations -- to be specified
|
|
or otherwise inferred:
|
|
|
|
+---------------------------------------------+---------------------------------------------+
|
|
| .. image:: images/bfr_tess_mXn_quad_A.png | .. image:: images/bfr_tess_mXn_quad_B.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_mXn_quad_A.png | :target: images/bfr_tess_mXn_quad_B.png |
|
|
+---------------------------------------------+---------------------------------------------+
|
|
| Quad tessellations with differing inner rates with matching (left) and varying outer |
|
|
| rates (right). |
|
|
+-------------------------------------------------------------------------------------------+
|
|
|
|
Differences from Hardware Tessellation
|
|
**************************************
|
|
|
|
Since the specifications for hardware tessellation often leave some details
|
|
of the patterns as implementation dependent, no two hardware implementations
|
|
are necessarily the same. Typically there may be subtle differences in the
|
|
non-uniform tessellation patterns along boundaries, and that is to be executed
|
|
here.
|
|
|
|
*Bfr* does provide some obvious additional functionality not present in
|
|
hardware tessellation and vice versa, e.g. *Bfr* provides the following (not
|
|
supported by hardware tessellation):
|
|
|
|
* patterns for parameterizations other than quads and tris (e.g. N-sided)
|
|
* preservation of quad facets of quad-based parameterizations
|
|
|
|
while hardware tessellation provides the following (not supported by *Bfr*):
|
|
|
|
* patterns for so-called "fractional" tessellation (non-integer rates)
|
|
|
|
The lack of fractional tessellation in *Bfr* is something that may be
|
|
addressed in a future release.
|
|
|
|
Where the functionality of *Bfr* and hardware tessellation overlap, a few
|
|
other differences are worth noting:
|
|
|
|
* indexing of edges and their associated outer tessellation rates
|
|
* uniform tessellation patterns for triangles differ significantly
|
|
|
|
For the indexing of edges and rates, when specifying an outer rate associated
|
|
with an edge, the array index for rate *i* is expected to correspond to edge
|
|
*i*. *Bfr* follows the convention established elsewhere in OpenSubdiv of
|
|
labeling/indexing edges 0, 1, etc. between vertex pairs [0,1], [1,2], etc.
|
|
So outer rate [0] corresponds to the edge between vertices [0,1]. In contrast,
|
|
hardware tessellation associates the rate for the edge between vertices [0,1]
|
|
as outer rate [1] -- its outer rate [0] is between vertices [N-1,0]. So an
|
|
offset of 1 is warranted when comparing the two.
|
|
|
|
+------------------------------------------------+------------------------------------------------+
|
|
| .. image:: images/bfr_tess_diff_edges_osd.png | .. image:: images/bfr_tess_diff_edges_gpu.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_diff_edges_osd.png | :target: images/bfr_tess_diff_edges_gpu.png |
|
|
+------------------------------------------------+------------------------------------------------+
|
|
| Outer edge tessellation rates of {1,3,5,7} applied to a quad with *Bfr* (left) and GPU |
|
|
| tessellation (right). |
|
|
+-------------------------------------------------------------------------------------------------+
|
|
|
|
For the uniform tessellation of triangles, its well known that the needs of
|
|
hardware implementation led designers to factor the patterns for triangles
|
|
to make use of the same hardware necessary for quads. As a result, many edges
|
|
are introduced into a simple tessellation of a triangle that are not parallel
|
|
to one of its three edges.
|
|
|
|
*Bfr* uses patterns more consistent with those resulting from the subdivision
|
|
of triangles. Only edges parallel to the edges of the triangle are introduced,
|
|
which creates more uniform facets (both edge lengths and area) and reduces
|
|
their number (by one third). This can reduce artifacts that sometimes arise
|
|
with use of the hardware patterns at lower tessellation rates:
|
|
|
|
+----------------------------------------------+----------------------------------------------+
|
|
| .. image:: images/bfr_tess_diff_tri_osd.png | .. image:: images/bfr_tess_diff_tri_gpu.png |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/bfr_tess_diff_tri_osd.png | :target: images/bfr_tess_diff_tri_gpu.png |
|
|
+----------------------------------------------+----------------------------------------------+
|
|
| Uniform tessellation of a triangle with *Bfr* (left) and GPU tessellation (right). |
|
|
+---------------------------------------------------------------------------------------------+
|
|
|
|
These triangular patterns have been referred to as "integer spacing"
|
|
for triangular patches in early work on hardware tessellation. But use of
|
|
these patterns was generally discarded in favor of techniques that split
|
|
the triangle into three quads -- allowing the hardware solution for quad
|
|
tessellation to be reused.
|
|
|
|
----
|
|
|
|
.. _bfr-navlink-surfacefactory:
|
|
|
|
More on Bfr::SurfaceFactory
|
|
===========================
|
|
The primary function of Bfr::SurfaceFactory is to identify and construct
|
|
a representation of the limit surface for a given face of a mesh. It achieves
|
|
this by inspecting the topology around the given face and constructing a
|
|
suitable representation encapsulated in a Surface.
|
|
|
|
The regions around a face can be divided into two categories based on their
|
|
topology: those that are "regular" and those that are not, i.e. those that
|
|
are "irregular". Recalling the illustration from `Irregular versus Irregular
|
|
Features <subdivision_surfaces.html#regular-versus-irregular-features>`__:
|
|
|
|
+-----------------------------------------+-----------------------------------------+
|
|
| .. image:: images/val6_regular.jpg | .. image:: images/val6_irregular.jpg |
|
|
| :align: center | :align: center |
|
|
| :width: 100% | :width: 100% |
|
|
| :target: images/val6_regular.jpg | :target: images/val6_irregular.jpg |
|
|
+-----------------------------------------+-----------------------------------------+
|
|
| Patches of regular Surfaces | Potential patches of irregular Surfaces |
|
|
+-----------------------------------------+-----------------------------------------+
|
|
|
|
The representation of the limit surface for regular regions is trivial --
|
|
it is a single parametric patch whose basis is determined by the subdivision
|
|
scheme (e.g. uniform bicubic B-spline for Catmull-Clark). In contrast, the
|
|
representation of the limit surface for an irregular region cannot be
|
|
accurately represented so simply. It can be far more complex depending on the
|
|
features present (extra-ordinary vertices, creasing of edges, etc.). It may
|
|
be as simple as a different kind of parametric patch whose points are
|
|
derived from those of the mesh, or it may often be a set of patches in a
|
|
hierarchy resulting from local subdivision. (*Bfr* intentionally hides the
|
|
details of these representations to allow future improvement.)
|
|
|
|
The cost of determining and assembling the representations of irregular
|
|
Surfaces is therefore often significant. Some of the performance benefits of
|
|
the SurfaceFactory are achieved by having it cache the complexities of the
|
|
irregular surfaces that it encounters.
|
|
|
|
In many common use cases, awareness and management of this caching is not
|
|
necessary (as illustrated by the tutorials). But the thread-safe construction
|
|
of Surfaces is one area where some awareness is required. Other use cases
|
|
that share the cache between meshes are also worth exploring as they can
|
|
further reduce potentially significant costs.
|
|
|
|
|
|
Bfr::SurfaceFactoryCache
|
|
************************
|
|
The SurfaceFactoryCache is the class used by SurfaceFactory to cache the
|
|
topological information that it can reuse for other similarly irregular
|
|
faces of the mesh. Though it is a publicly accessible class, the
|
|
SurfaceFactoryCache has little to no public interface other than construction
|
|
(made available to support more advanced cases covered later) and in most
|
|
cases it can be completely ignored.
|
|
|
|
Typically an instance of SurfaceFactory has an internal SurfaceFactoryCache
|
|
member which is used by that factory for its lifetime. Since that cache member
|
|
is mutable -- potentially updated when an irregular Surface is created -- it
|
|
does need to be thread-safe if the SurfaceFactory is to be used in a threaded
|
|
context.
|
|
|
|
To accommodate this need, SurfaceFactoryCache is defined as a base class with
|
|
an accompanying class template to allow the trivial declaration of thread-safe
|
|
subclasses:
|
|
|
|
.. code:: c++
|
|
|
|
template <typename MUTEX_TYPE,
|
|
typename READ_LOCK_GUARD_TYPE,
|
|
typename WRITE_LOCK_GUARD_TYPE >
|
|
class SurfaceFactoryCacheThreaded : public SurfaceFactoryCache {
|
|
...
|
|
};
|
|
|
|
For example, a local type for a thread-safe cache using std::shared_mutex
|
|
from C++17 could be simply declared as follows:
|
|
|
|
.. code:: c++
|
|
|
|
#include <shared_mutex>
|
|
|
|
typedef Bfr::SurfaceFactoryCacheThreaded<
|
|
std::shared_mutex,
|
|
std::shared_lock<std::shared_mutex>,
|
|
std::unique_lock<std::shared_mutex> >
|
|
ThreadSafeCache;
|
|
|
|
Such thread-safe cache types are essential when distributing the work of a
|
|
single SurfaceFactory across multiple threads. They can be encapsulated in
|
|
the definitions of subclasses of SurfaceFactory or used to define external
|
|
cache instances for use with any subclass of SurfaceFactory.
|
|
|
|
Defining a Thread-Safe SurfaceFactory
|
|
*************************************
|
|
|
|
The thread-safety of a SurfaceFactory is purely dependent on the
|
|
thread-safety of the SurfaceFactoryCache that it uses. With caching
|
|
disabled, any SurfaceFactory is thread-safe but will be far less
|
|
efficient in dealing with irregular Surfaces.
|
|
|
|
When a subclass of SurfaceFactory is defined (discussed in more detail
|
|
later), one of its responsibilities is to identify and manage an instance of
|
|
SurfaceFactoryCache for its internal use. Defining such a subclass is a
|
|
simple matter of declaring a thread-safe SurfaceFactoryCache type (as noted
|
|
above) along with a local member of that type to be used by each instance.
|
|
|
|
Given the widespread use of the Far::TopologyRefiner in OpenSubdiv, and
|
|
the lack of a connected mesh representation in many contexts, a subclass of
|
|
SurfaceFactory is made available to use a TopologyRefiner as a mesh, i.e.
|
|
the Bfr::RefinerSurfaceFactory subclass.
|
|
|
|
Since many OpenSubdiv users may make use of the RefinerSurfaceFactory
|
|
subclass, and they may have different preferences of threading model,
|
|
the RefinerSurfaceFactory subclass is similarly defined as a class
|
|
template to enable threading flexibility. In this case, the template
|
|
is parameterized by the desired type of SurfaceFactoryCache, which
|
|
embodies the threading specifications as noted above, i.e.:
|
|
|
|
.. code:: c++
|
|
|
|
template <class CACHE_TYPE = SurfaceFactoryCache>
|
|
class RefinerSurfaceFactory : public ... {
|
|
...
|
|
};
|
|
|
|
The default template is the base SurfaceFactoryCache which is not thread-safe,
|
|
but a simple declaration of a thread-safe cache type is sufficient to declare
|
|
a similarly thread-safe RefinerSurfaceFactory type:
|
|
|
|
.. code:: c++
|
|
|
|
#include <opensubdiv/bfr/surfaceFactoryCache.h>
|
|
|
|
// Declare thread-safe cache type (see std::shared_mutex example above):
|
|
typedef Bfr::SurfaceFactoryCacheThreaded< ... > ThreadSafeCache;
|
|
|
|
// Declare thread-safe factory type:
|
|
typedef Bfr::RefinerSurfaceFactory<ThreadSafeCache> ThreadSafeFactory;
|
|
|
|
The resulting factory type safely allows the construction of Surfaces
|
|
(and their subsequent evaluation and tessellation) to be distributed over
|
|
multiple threads.
|
|
|
|
|
|
Internal versus External SurfaceFactoryCache
|
|
********************************************
|
|
|
|
Typical usage of the SurfaceFactoryCache by the SurfaceFactory is to have
|
|
the factory create an internal cache member to be used for the lifetime of
|
|
the factory associated with a mesh. But the data stored in the cache is not
|
|
in any way dependent on the factory or mesh used to create it. So a cache
|
|
can potentially be shared by multiple factories.
|
|
|
|
While such sharing is possible -- and the *Bfr* interfaces intentionally
|
|
permit it -- any exploration should proceed with caution. Greater public
|
|
knowledge and control of the cache is ultimately necessary to manage its
|
|
potentially unbounded memory increase, and support in the public interface
|
|
is currently limited.
|
|
|
|
A cache stored as a member varialbe and managed exclusively by the factory
|
|
is said to be "internal" while one managed exclusively by its client is
|
|
said to be "external". In both cases, the factory deals with retrieving
|
|
data from or adding data to the cache -- only management of the cache's
|
|
ownership differs, and that ownership is never transferred.
|
|
|
|
A subset of the methods of SurfaceFactory::Options provide the means of
|
|
specifying the use of an internal or external cache, or no caching at all:
|
|
|
|
.. code:: c++
|
|
|
|
// Assign an external cache to override the internal
|
|
Options & SetExternalCache(SurfaceFactoryCache * cache);
|
|
|
|
// Enable or disable caching (default is true):
|
|
Options & EnableCaching(bool on);
|
|
|
|
As noted here, specifying an external cache will override use of a
|
|
factory's internal cache. Disabling caching takes precedence over both,
|
|
but is generally not practical and exists mainly to aide debugging.
|
|
|
|
The common use of the internal cache is to create a SurfaceFactory and
|
|
distribute processing of the Surfaces of its faces over multiple threads,
|
|
or to construct Surfaces for the mesh for any other purpose while the
|
|
mesh remains in scope. There is no need to deal explicitly with the
|
|
SurfaceFactoryCache in these cases.
|
|
|
|
Use cases for an external cache are more varied and explicit, including:
|
|
|
|
* creating a single external cache to process a sequence of meshes
|
|
on a single thread (cache thread-safety not required)
|
|
* creating a separate external cache on each thread to process a set
|
|
of meshes distributed over multiple threads (cache thread-safety
|
|
not required)
|
|
* creating a single external cache for multiple meshes distributed
|
|
over multiple threads (cache thread-safety required, and beware of
|
|
unbounded memory growth here)
|
|
|
|
Future extensions to the public interface of SurfaceFactoryCache may be
|
|
made to support common use cases as their common needs are made clearer.
|
|
|
|
----
|
|
|
|
.. _bfr-navlink-customizing:
|
|
|
|
Customizing a Bfr::SurfaceFactory
|
|
=================================
|
|
|
|
One of the goals of *Bfr* is to provide a lightweight interface for the
|
|
evaluation of Surfaces from any connected mesh representation. In order to
|
|
do so, the factory needs to gather topological information from that mesh
|
|
representation. That information is provide to the factory through
|
|
inheritance: a subclass of SurfaceFactory is defined that fulfills all
|
|
requirements of the factory.
|
|
|
|
It must be made clear that a subclass can only be created from a *connected*
|
|
mesh representation, i.e. a representation that includes connectivity or
|
|
adjacency relationships between its components (vertices, faces and edges).
|
|
|
|
Classes for simple containers of mesh topology used for external formats
|
|
(e.g. USD, Alembic, etc.) are generally not *connected*. Many applications
|
|
construct a connected mesh representation for internal use when loading such
|
|
mesh data -- using a variety of techniques including half-edges, winged-edges
|
|
or table-based relationships. There are many choices here that offer a variety
|
|
of trade-offs depending on usage (e.g. fixed vs dynamic topology) and so no
|
|
"best" solution. Once constructed and available within an application, *Bfr*
|
|
strives to take advantage of that representation.
|
|
|
|
As a minimum requirement for supporting a subclass of SurfaceFactory, a
|
|
connected mesh representation must be able to efficiently identify the
|
|
incident faces of any given vertex. As noted earlier, when no such
|
|
representation is available, users can construct a Far::TopologyRefiner for
|
|
their connected mesh and use Bfr::RefinerSurfaceFactory.
|
|
|
|
There are three requirements of a subclass of SurfaceFactory:
|
|
|
|
* fulfill the interface required to adapt the connected mesh to the factory
|
|
* provide an internal cache for the factory of the preferred type
|
|
* extend the existing SurfaceFactory interface for the connected mesh type
|
|
|
|
The first of these is the most significant and is the focus here. The second
|
|
was mentioned previously with the SurfaceFactoryCache and is trivial. The last
|
|
should also be trivial and is generally optional (at minimum the subclass will
|
|
need a constructor to create an instance of the factory from a given mesh, but
|
|
anything more is not strictly essential).
|
|
|
|
It is important to note that anyone attempting to write such a subclass must
|
|
have an intimate understanding of the topological capabilities and limitations
|
|
of the mesh representation involved. The SurfaceFactory is topologically
|
|
robust in that it will support meshes with a wide range of degenerate or
|
|
non-manifold features, but in order to process topology efficiently, a
|
|
subclass needs to indicate when and where those degeneracies may occur.
|
|
|
|
A simplified implementation of the Bfr::RefinerSurfaceFactory is provided in
|
|
the tutorials for illustration purposes.
|
|
|
|
The Bfr::SurfaceFactoryMeshAdapter Interface
|
|
********************************************
|
|
|
|
The SurfaceFactoryMeshAdapter class defines the interface used to satisfy the
|
|
topological requirements of the SurfaceFactory. An implementation for a
|
|
particular mesh class provides the base factory with everything needed to
|
|
identify the limit surface of a given face from its surrounding topology.
|
|
The SurfaceFactory actually inherits the SurfaceFactoryMeshAdapter interface
|
|
but does not implement it -- deferring that to its subclasses -- since
|
|
separate subclasses of SurfaceFactoryMeshAdapter serve no other purpose.
|
|
|
|
The limit surface for a face is fully defined by the complete set of incident
|
|
vertices, faces and edges surrounding the face. But it is difficult to
|
|
accurately and efficiently assemble and represent all of that required
|
|
information in a single class or query for all possible cases. So the mesh
|
|
adapter interface provides a suite of methods to allow the factory to gather
|
|
only what it needs for the Surface required -- which may differ considerably
|
|
according to whether the Surface is for vertex or face-varying data, linear or
|
|
non-linear, etc.
|
|
|
|
The virtual methods required can be organized into small groups devoted to
|
|
particular aspects of construction. A description of the methods and purposes
|
|
for each group follows, with more details and exact signatures available in
|
|
the accompanying Doxygen for the SurfaceFactoryMeshAdapter class.
|
|
|
|
**Basic Properties of a Face**
|
|
|
|
A small set of simple methods indicate whether the SurfaceFactory needs to
|
|
create a Surface for a face, and if so, how:
|
|
|
|
.. code:: c++
|
|
|
|
virtual bool isFaceHole(Index faceIndex) const = 0;
|
|
|
|
virtual int getFaceSize(Index faceIndex) const = 0;
|
|
|
|
These are trivial and self-explanatory.
|
|
|
|
**Identifying Indices for an Entire Face**
|
|
|
|
If the Surface requested turns out to be linearly interpolated (e.g. for
|
|
varying or linear face-varying data) indices for the control point data
|
|
are all assigned to the face and can be trivially identified:
|
|
|
|
.. code:: c++
|
|
|
|
virtual int getFaceVertexIndices(Index faceIndex,
|
|
Index vertexIndices[]) const = 0;
|
|
|
|
virtual int getFaceFVarValueIndices(Index faceIndex,
|
|
FVarID faceVaryingID,
|
|
Index faceVaryingIndices[]) const = 0;
|
|
|
|
Since multiple sets of face-varying data with different topology may be
|
|
assigned to the mesh, an identifier needs to be specified both in the
|
|
public interface when requesting a Surface and here when the factory
|
|
assembles it. How a face-varying identifier is interpreted is completely
|
|
determined by the subclass through the implementation of the methods
|
|
that require it.
|
|
|
|
**Specifying the Neighborhood Around a Vertex**
|
|
|
|
When the Surface requested is not linear, the entire neighborhood around
|
|
the face must be determined. This is achieved by specifying the
|
|
neighborhoods around each of the vertices of the face, which the factory
|
|
then assembles.
|
|
|
|
For the neighborhood of each face-vertex, the factory obtains a complete
|
|
specification in a simple VertexDescriptor class. An instance of
|
|
VertexDescriptor is provided and populated with the following method:
|
|
|
|
.. code:: c++
|
|
|
|
virtual int populateFaceVertexDescriptor(
|
|
Index faceIndex, int faceVertex,
|
|
VertexDescriptor * vertexDescriptor) const = 0;
|
|
|
|
Within this method, the given VertexDescriptor instance is initialized
|
|
using a small suite of VertexDescriptor methods that specify the following
|
|
information about the vertex and its neighborhood:
|
|
|
|
* whether the neighborhood is manifold (ordered counter-clockwise)
|
|
* whether the vertex is on a boundary
|
|
* the sizes of all or each incident face
|
|
* the sharpness of the vertex
|
|
* the sharpness of edges of incident faces
|
|
|
|
These methods are specified between Initialize() and Finalize() methods, so
|
|
an interior vertex of valence 4 with three incident quads and one incident
|
|
triangle might be specified as follows:
|
|
|
|
.. code:: c++
|
|
|
|
int vertexValence = 4;
|
|
vertexDescriptor.Initialize(vertexValence);
|
|
vertexDescriptor.SetManifold(true);
|
|
vertexDescriptor.SetBoundary(false);
|
|
|
|
vertexDescriptor.SetIncidentFaceSize(0, 4);
|
|
vertexDescriptor.SetIncidentFaceSize(1, 4);
|
|
vertexDescriptor.SetIncidentFaceSize(2, 3);
|
|
vertexDescriptor.SetIncidentFaceSize(3, 4);
|
|
vertexDescriptor.Finalize();
|
|
|
|
Specifying the vertex neighborhood as manifold is critical to allowing the
|
|
factory to inspect the neighborhood efficiently. A manifold vertex has its
|
|
incident faces and edges ordered in a counter-clockwise orientation and is
|
|
free of degeneracies. If it is not clear that a vertex is manifold, it
|
|
should not be set as such or the factory's inspection of associated data
|
|
will not be correct.
|
|
|
|
**Identifying Indices Around a Vertex**
|
|
|
|
When the Surface requested is not linear, the indices of control point data
|
|
for the entire neighborhood of the face are ultimately required, and that
|
|
entire set is similarly determined by identifying the indices for each of
|
|
the neighborhoods of the face-vertices:
|
|
|
|
.. code:: c++
|
|
|
|
virtual int getFaceVertexIncidentFaceVertexIndices(
|
|
Index faceIndex, int faceVertex,
|
|
Index vertexIndices[]) const = 0;
|
|
|
|
virtual int getFaceVertexIncidentFaceFVarValueIndices(
|
|
Index faceIndex, int faceVertex,
|
|
FVarID faceVaryingID,
|
|
Index faceVaryingIndices[]) const = 0;
|
|
|
|
As was the case with the methods retrieving indices for the entire face, one
|
|
exists for identifying indices vertex data while another exists to identify
|
|
indices for a specified set of face-varying data.
|
|
|
|
Customizing the Subclass Interface
|
|
**********************************
|
|
|
|
Once the topological requirements of a subclass have been satisfied for its
|
|
mesh representation, minor customizations of the inherited interface of
|
|
SurfaceFactory may be useful.
|
|
|
|
Consider a class called Mesh and its associated subclass of SurfaceFactory
|
|
called MeshSurfaceFactory.
|
|
|
|
At minimum, a constructor of MeshSurfaceFactory is necessary to construct
|
|
an instance for a particular instance of mesh. This is typically achieved
|
|
as follows:
|
|
|
|
.. code:: c++
|
|
|
|
MeshSurfaceFactory(Mesh const & mesh,
|
|
Options const & options);
|
|
|
|
In addition to the Mesh instance, such a constructor passes a set of
|
|
Options (i.e. SurfaceFactory::Options) to the base SurfaceFactory. Any
|
|
additional arguments are possible here, e.g. perhaps only a single
|
|
face-varying UV set is supported, and that might be specified by
|
|
identifying it on construction.
|
|
|
|
Given that mesh representations often have their own associated classes that
|
|
internally contain the actual data, it may be useful to provide a few other
|
|
conveniences to simplify working with a Mesh. For example, if mesh data is
|
|
stored in a class called MeshPrimvar, a method to construct a Surface from
|
|
a given MeshPrimvar may be useful, e.g.:
|
|
|
|
.. code:: c++
|
|
|
|
bool InitPrimvarSurface(int faceIndex,
|
|
MeshPrimvar const & meshPrimvar,
|
|
Surface<float> * surface);
|
|
|
|
which would then determine the nature of the MeshPrimvar data (interpolated
|
|
as vertex, varying or face-varying) and act accordingly. It may also be
|
|
worth simplifying the template complexity here if only one precision is
|
|
ever required.
|
|
|