Merge branch 'dev' into dev-mac-subproject

This commit is contained in:
David G Yu 2022-08-07 17:12:10 -07:00 committed by GitHub
commit 7b8728a3f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
139 changed files with 25468 additions and 202 deletions

View File

@ -22,10 +22,10 @@
# language governing permissions and limitations under the Apache License.
#
project(OpenSubdiv)
cmake_minimum_required(VERSION 2.8.6)
project(OpenSubdiv)
# Turn on folder support
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

View File

@ -30,10 +30,10 @@ jobs:
- job: macOS
pool:
vmImage: 'macOS-10.14'
vmImage: 'macOS-10.15'
steps:
- script: |
/bin/bash -c "sudo xcode-select -s /Applications/Xcode_10.1.app/Contents/Developer"
/bin/bash -c "sudo xcode-select -s /Applications/Xcode_12.4.app/Contents/Developer"
python build_scripts/build_osd.py --tests --tbb --generator Xcode --build $HOME/OSDgen/build --src $HOME/OSDgen/src $HOME/OSDinst
displayName: 'Building OpenSubdiv'
- script: |

View File

@ -66,6 +66,7 @@ if (DOCUTILS_FOUND AND PYTHONINTERP_FOUND)
set(RST_FILES
additional_resources.rst
api_overview.rst
bfr_overview.rst
cmake_build.rst
code_examples.rst
compatibility.rst
@ -183,6 +184,13 @@ if (DOCUTILS_FOUND AND PYTHONINTERP_FOUND)
far/tutorial_5_1/far_tutorial_5_1.cpp
far/tutorial_5_2/far_tutorial_5_2.cpp
far/tutorial_5_3/far_tutorial_5_3.cpp
bfr/tutorial_1_1/bfr_tutorial_1_1.cpp
bfr/tutorial_1_2/bfr_tutorial_1_2.cpp
bfr/tutorial_1_3/bfr_tutorial_1_3.cpp
bfr/tutorial_1_4/bfr_tutorial_1_4.cpp
bfr/tutorial_2_1/bfr_tutorial_2_1.cpp
bfr/tutorial_2_2/bfr_tutorial_2_2.cpp
bfr/tutorial_3_1/bfr_tutorial_3_1.cpp
osd/tutorial_0/osd_tutorial_0.cpp
)

View File

@ -51,7 +51,7 @@ DOXYFILE_ENCODING = UTF-8
# identify the project. Note that if you do not use Doxywizard you need
# to put quotes around the project name if it contains spaces.
PROJECT_NAME =
PROJECT_NAME = ""
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
@ -750,7 +750,9 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = osd/nonCopyable.h
EXCLUDE = opensubdiv/bfr/irregularPatchType.h \
opensubdiv/bfr/surfaceData.h \
opensubdiv/osd/nonCopyable.h
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@ -772,7 +774,7 @@ EXCLUDE_PATTERNS =
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
EXCLUDE_SYMBOLS =
EXCLUDE_SYMBOLS = internal
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
@ -1546,14 +1548,14 @@ XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
#
#XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
#
#XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
# dump the program listings (including syntax highlighting

View File

@ -46,53 +46,42 @@ Videos
Feature Adaptive GPU Rendering of Catmull-Clark Subdivision Surfaces (2012)
***************************************************************************
.. raw:: html
<center>
<iframe width="640" height="360" src="https://www.youtube.com/embed/uogAzQoVdNU" frameborder="0" allowfullscreen></iframe>
</center>
.. image:: https://graphics.pixar.com/opensubdiv/videothumbnails/feature_adaptive_siggraph2012.png
:align: center
:width: 75%
:target: https://www.youtube.com/embed/uogAzQoVdNU
Open Subdivision Technology Review (2012)
*****************************************
.. raw:: html
<center>
<iframe width="640" height="360" src="https://www.youtube.com/embed/Y-3L9BOTEtw" frameborder="0" allowfullscreen></iframe>
</center>
First public demonstration of OpenSubdiv at Siggraph 2012.
.. image:: https://graphics.pixar.com/opensubdiv/videothumbnails/opensubdiv_intro_autodesk2012.png
:align: center
:width: 75%
:target: https://www.youtube.com/embed/Y-3L9BOTEtw
Autodesk User Group At Anaheim (2013)
*************************************
.. raw:: html
<center>
<iframe width="640" height="360" src="https://www.youtube.com/embed/9lFwFoCxysI" frameborder="0" allowfullscreen></iframe>
</center>
Bill Polson, Director of Industry Strategy at Pixar Animation Studios
.. image:: https://graphics.pixar.com/opensubdiv/videothumbnails/opensubdiv_demo_autodesk2013.png
:align: center
:width: 75%
:target: https://www.youtube.com/embed/9lFwFoCxysI
Why Model with Subdivisions (2013)
**********************************
.. raw:: html
<center>
<iframe src="https://player.vimeo.com/video/70600180" width="640" height="360" frameborder="0" allowfullscreen></iframe>
</center>
Ivo Kos, Modelling Technical Director at Pixar Animation Studios
.. image:: https://graphics.pixar.com/opensubdiv/videothumbnails/subdiv_modeling_pixar2013.png
:align: center
:width: 75%
:target: https://player.vimeo.com/video/70600180
Meet the Experts: The OpenSubdiv Project (2013)
***********************************************
.. raw:: html
<center>
<iframe width="640" height="360" src="https://www.youtube.com/embed/xFZazwvYc5o" frameborder="0" allowfullscreen></iframe>
</center>
.. image:: https://graphics.pixar.com/opensubdiv/videothumbnails/meet_the_experts_autodesk2013.png
:align: center
:width: 75%
:target: https://www.youtube.com/embed/xFZazwvYc5o
Join special guests, Bill Polson, Dirk Van Gelder, Manuel Kraemer,
Takahito Tejima, David G. Yu and Dale Ruffolo, from Pixar Animation

View File

@ -61,6 +61,10 @@ Layers list:
| | *Far* also provides a fully-featured single-threaded |
| | implementation of subdivision interpolation algorithms. |
+-----------------------------------------+--------------------------------------------------------------------------------+
| | **Bfr** | A suite of classes to provide parameterization, evaluation |
| | Bace Face Representation | and tessellation on the CPU. *Bfr* is more flexible and more |
| | `Bfr Overview <bfr_overview.html>`__ | scalable than *Osd* but potentially less efficient. |
+-----------------------------------------+--------------------------------------------------------------------------------+
| | **Osd** | |
| | OpenSubdiv cross platform | A suite of classes to provide parallel subdivision |
| | `Osd Overview <osd_overview.html>`__ | kernels and drawing utilities on a variety of platforms |

View File

@ -0,0 +1,710 @@
..
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
===========================
Work in progress -- topics to include:
* Bfr::RefinerSurfaceFactory as an example
* Bfr::SurfaceFactoryCache and its thread-safety
* thread-safe declaration and usage of SurfaceFactory
* using an external SurfaceFactoryCache with multiple meshes
* serial
* parallel
----
.. _bfr-navlink-customizing:
Customizing a Bfr::SurfaceFactory
=================================
Work in progress -- topics to include:
* SurfaceFactory and Bfr::SurfaceFactoryMeshAdapter
* fulfilling the SurfaceFactoryMeshAdapter interface
* retrieving simple properties of a face
* retrieving indices at all face-vertices
* retrieving indices for the neighborhood around a face-vertex
* accelerated retrieval for regular face neighborhoods
* customizing a subclass of SurfaceFactory

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 KiB

View File

@ -196,11 +196,10 @@ An excellent short tutorial from the Guerrilla CG Project that illustrates many
of the common pitfalls of subdivision modeling, and the strategies to overcome
them:
.. raw:: html
<center>
<iframe width="640" height="360" src="https://www.youtube.com/embed/k_S1INdEmdI" frameborder="0" allowfullscreen></iframe>
</center>
.. image:: https://graphics.pixar.com/opensubdiv/videothumbnails/subdiv_topology_guerrillacg.png
:align: center
:width: 75%
:target: https://www.youtube.com/embed/k_S1INdEmdI
----
@ -208,9 +207,8 @@ Ivo Kos, Modelling Technical Director at Pixar Animation Studios, shows some of
the modeling techniques he uses when modeling props and architecture sets for
feature films.
.. raw:: html
<center>
<iframe src="https://player.vimeo.com/video/70600180" width="640" height="360" frameborder="0" allowfullscreen></iframe>
</center>
.. image:: https://graphics.pixar.com/opensubdiv/videothumbnails/subdiv_modeling_pixar2013.png
:align: center
:width: 75%
:target: https://player.vimeo.com/video/70600180

View File

@ -67,6 +67,14 @@
<li><a href="far_overview.html#far-patchtable">Patch Table</a></li>
<li><a href="far_overview.html#far-stenciltable">Stencil Table</a></li>
</ul>
<li><a href="bfr_overview.html">Bfr</a></li>
<ul>
<li><a href="bfr_overview.html#bfr-navlink-evaluation">Evaluation</a></li>
<li><a href="bfr_overview.html#bfr-navlink-parameterization">Parameterization</a></li>
<li><a href="bfr_overview.html#bfr-navlink-tessellation">Tessellation</a></li>
<li><a href="bfr_overview.html#bfr-navlink-surfacefactory">More on Surface Factory</a></li>
<li><a href="bfr_overview.html#bfr-navlink-customizing">Custom Surface Factory</a></li>
</ul>
<li><a href="osd_overview.html">Osd</a></li>
<ul>
<li><a href="osd_shader_interface.html">Shader Interface</a></li>

View File

@ -69,7 +69,8 @@ def Process(srcfile, title):
rest += ("\n"
"----\n"
"\n"
".. code:: c\n")
".. code:: c++\n"
"\n")
code = ReadFile(srcfile)

View File

@ -72,7 +72,7 @@ Major Improvements to Introductory Documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A significant rewrite of the `Subdivision Surfaces <subdivision_surfaces.html>`__
page is included in this release. The new documentation emphasizes the *piecewise
parameteric surface* nature of subdivision surfaces and the implications of
parametric surface* nature of subdivision surfaces and the implications of
supporting *arbitary topology*.
+---------------------------------------+---------------------------------------+

View File

@ -35,6 +35,86 @@ The tutorial source code can be found in the `github.com repository
<https://github.com/PixarAnimationStudios/OpenSubdiv/tree/release/tutorials>`__
or in your local ``<repository root>/tutorials``.
Bfr Tutorials
=============
1. Basic Evaluation and Tessellation
************************************
Tutorial 1.1
^^^^^^^^^^^^
This tutorial illustrates the use of Bfr::SurfaceFactory and Bfr::Surface
to evaluate points on the limit of each face. The limit positions at all
corners of the face are evaluated and connected to the limit position in
the center of the face -- creating a simple triangular tessellation.
`[code] <bfr_tutorial_1_1.html>`__
Tutorial 1.2
^^^^^^^^^^^^
This tutorial shows the added use of Bfr::Tessellation to identify the
set of points and connectivity for a uniform tessellation. Both a Surface
and Tessellation is identified for each face, with the Tessellation
indicating which points are to be evaluated by the Surface.
`[code] <bfr_tutorial_1_2.html>`__
.. image:: images/bfr_tutorial_1_2.png
:align: center
:height: 100px
:target: images/bfr_tutorial_1_2.png
Tutorial 1.3
^^^^^^^^^^^^
This tutorial extends the previous tutorial on uniform Tessellation by
adding face-varying Surfaces to compute corresponding UVs for each
evaluated position. `[code] <bfr_tutorial_1_3.html>`__
Tutorial 1.4
^^^^^^^^^^^^
This tutorial extends the previous tutorial on uniform tessellation of
position and UV by illustrating how additional mesh data interleaved with
the position and UV data is easily handled. `[code] <bfr_tutorial_1_4.html>`__
2. More on Bfr::Tessellation
****************************
Tutorial 2.1
^^^^^^^^^^^^
This tutorial extends the use of Tessellation to illustrate the use of
non-uniform tessellation rates per edge. A simple edge-length metric is
used to determine the tessellation rate for each edge of a face.
`[code] <bfr_tutorial_2_1.html>`__
.. image:: images/bfr_tutorial_2_1.png
:align: center
:height: 100px
:target: images/bfr_tutorial_2_1.png
Tutorial 2.2
^^^^^^^^^^^^
This tutorial is a more complex extension of the use of Tessellation
that illustrates how the separation and association of tessellation data
with the boundary and interior of the face can be used. Limit points
evaluated on the vertices and edges of a face (the boundary of the
Tessellation) are computed once and shared with adjacent faces --
creating a topologically watertight tessellation of the mesh.
`[code] <bfr_tutorial_2_2.html>`__
3. Creating a Custom Bfr::SurfaceFactory
****************************************
Tutorial 3.1
^^^^^^^^^^^^
This tutorial shows a basic example of the more advanced topic of creating
a subclass of SurfaceFactory adapted to a connected mesh representation --
requiring an implementation of the SurfaceFactoryMeshAdapter interface for
that mesh. A simplified version of the implementation of Far::TopologyRefiner
is provided. (Note that the `[code] <bfr_tutorial_3_1.html>`__ imported
here is that of the main program, not the separate header and source files
of the custom subclass illustrated -- which current documentation scripts
cannot import.)
----
Far Tutorials
=============
@ -49,7 +129,7 @@ Tutorial 1.1
.. image:: images/far_tutorial_1_1.0.png
:align: center
:width: 100px
:height: 100px
:target: images/far_tutorial_1_1.0.png
Tutorial 1.2
@ -70,7 +150,7 @@ Tutorial 2.1
.. image:: images/far_tutorial_2_1.0.png
:align: center
:width: 100px
:height: 100px
:target: images/far_tutorial_2_1.0.png
Tutorial 2.2
@ -82,7 +162,7 @@ Tutorial 2.2
.. image:: images/far_tutorial_2_2.0.png
:align: center
:width: 100px
:height: 100px
:target: images/far_tutorial_2_2.0.png
Tutorial 2.3
@ -133,7 +213,7 @@ Tutorial 5.1
.. image:: images/far_tutorial_5_1.0.png
:align: center
:width: 100px
:height: 100px
:target: images/far_tutorial_5_1.0.png
Tutorial 5.2
@ -191,6 +271,6 @@ Tutorial 2
.. image:: images/hbr_tutorial_2.0.png
:align: center
:width: 100px
:height: 100px
:target: images/hbr_tutorial_2.0.png

View File

@ -1705,15 +1705,15 @@ initD3D11(HWND hWnd) {
float specular[4];
} lightSource[2];
} lightingData = {
0.5, 0.2f, 1.0f, 0.0f,
0.1f, 0.1f, 0.1f, 1.0f,
0.7f, 0.7f, 0.7f, 1.0f,
0.8f, 0.8f, 0.8f, 1.0f,
{{ { 0.5, 0.2f, 1.0f, 0.0f },
{ 0.1f, 0.1f, 0.1f, 1.0f },
{ 0.7f, 0.7f, 0.7f, 1.0f },
{ 0.8f, 0.8f, 0.8f, 1.0f } },
-0.8f, 0.4f, -1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f,
0.8f, 0.8f, 0.8f, 1.0f,
{ { -0.8f, 0.4f, -1.0f, 0.0f },
{ 0.0f, 0.0f, 0.0f, 1.0f },
{ 0.5f, 0.5f, 0.5f, 1.0f },
{ 0.8f, 0.8f, 0.8f, 1.0f } }},
};
D3D11_BUFFER_DESC cbDesc;

View File

@ -1377,15 +1377,15 @@ initD3D11(HWND hWnd) {
float specular[4];
} lightSource[2];
} lightingData = {
0.5, 0.2f, 1.0f, 0.0f,
0.1f, 0.1f, 0.1f, 1.0f,
0.7f, 0.7f, 0.7f, 1.0f,
0.8f, 0.8f, 0.8f, 1.0f,
{{ { 0.5, 0.2f, 1.0f, 0.0f },
{ 0.1f, 0.1f, 0.1f, 1.0f },
{ 0.7f, 0.7f, 0.7f, 1.0f },
{ 0.8f, 0.8f, 0.8f, 1.0f } },
-0.8f, 0.4f, -1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f,
0.8f, 0.8f, 0.8f, 1.0f,
{ { -0.8f, 0.4f, -1.0f, 0.0f },
{ 0.0f, 0.0f, 0.0f, 1.0f },
{ 0.5f, 0.5f, 0.5f, 1.0f },
{ 0.8f, 0.8f, 0.8f, 1.0f } }},
};
D3D11_BUFFER_DESC cbDesc;
ZeroMemory(&cbDesc, sizeof(cbDesc));

View File

@ -110,6 +110,8 @@ if (NOT NO_LIB)
add_subdirectory(far)
add_subdirectory(bfr)
add_subdirectory(osd)
#---------------------------------------------------------------------------
@ -131,6 +133,7 @@ if (NOT NO_LIB)
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:bfr_obj>
$<TARGET_OBJECTS:osd_cpu_obj>
)
@ -179,6 +182,7 @@ if (NOT NO_LIB)
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:bfr_obj>
$<TARGET_OBJECTS:osd_cpu_obj>
)
@ -245,6 +249,7 @@ if (NOT NO_LIB)
get_directory_property(SDC_HEADER_FILES DIRECTORY ${PROJECT_SOURCE_DIR}/opensubdiv/sdc DEFINITION PUBLIC_HEADER_FILES)
get_directory_property(HBR_HEADER_FILES DIRECTORY ${PROJECT_SOURCE_DIR}/opensubdiv/hbr DEFINITION PUBLIC_HEADER_FILES)
get_directory_property(VTR_HEADER_FILES DIRECTORY ${PROJECT_SOURCE_DIR}/opensubdiv/vtr DEFINITION PUBLIC_HEADER_FILES)
get_directory_property(BFR_HEADER_FILES DIRECTORY ${PROJECT_SOURCE_DIR}/opensubdiv/bfr DEFINITION PUBLIC_HEADER_FILES)
foreach(file ${OSD_HEADER_FILES})
@ -267,6 +272,10 @@ if (NOT NO_LIB)
list(APPEND PUBLIC_HEADER_FILES "vtr/${file}")
endforeach(file)
foreach(file ${BFR_HEADER_FILES})
list(APPEND PUBLIC_HEADER_FILES "bfr/${file}")
endforeach(file)
list(APPEND PUBLIC_HEADER_FILES "version.h")
#static framework
@ -276,6 +285,7 @@ if (NOT NO_LIB)
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:bfr_obj>
$<TARGET_OBJECTS:osd_cpu_obj>
$<TARGET_OBJECTS:osd_gpu_obj>
${OPENGL_LOADER_OBJS}
@ -323,6 +333,7 @@ if (NOT NO_LIB)
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:bfr_obj>
$<TARGET_OBJECTS:osd_cpu_obj>
$<TARGET_OBJECTS:osd_gpu_obj>
${OPENGL_LOADER_OBJS}

View File

@ -0,0 +1,110 @@
#
# Copyright 2021 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.
#
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# source & headers
set(SOURCE_FILES
faceSurface.cpp
faceTopology.cpp
faceVertex.cpp
hash.cpp
irregularPatchBuilder.cpp
parameterization.cpp
patchTree.cpp
patchTreeBuilder.cpp
refinerSurfaceFactory.cpp
regularPatchBuilder.cpp
surface.cpp
surfaceData.cpp
surfaceFactory.cpp
surfaceFactoryCache.cpp
tessellation.cpp
vertexDescriptor.cpp
)
set(PRIVATE_HEADER_FILES
faceSurface.h
faceTopology.h
faceVertex.h
faceVertexSubset.h
hash.h
irregularPatchBuilder.h
patchTree.h
patchTreeBuilder.h
pointOperations.h
regularPatchBuilder.h
vertexTag.h
)
set(PUBLIC_HEADER_FILES
irregularPatchType.h
limits.h
parameterization.h
refinerSurfaceFactory.h
surface.h
surfaceData.h
surfaceFactory.h
surfaceFactoryMeshAdapter.h
surfaceFactoryCache.h
tessellation.h
vertexDescriptor.h
)
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
if (NOT NO_LIB)
# Compile objs first for both the CPU and GPU libs -----
add_library(bfr_obj
OBJECT
${SOURCE_FILES}
${PRIVATE_HEADER_FILES}
${PUBLIC_HEADER_FILES}
)
set_target_properties(bfr_obj
PROPERTIES
FOLDER "opensubdiv"
)
endif()
#-------------------------------------------------------------------------------
osd_add_doxy_headers( "${DOXY_HEADER_FILES}" )
install(
FILES
${PUBLIC_HEADER_FILES}
DESTINATION
"${CMAKE_INCDIR_BASE}/bfr"
PERMISSIONS
OWNER_READ
GROUP_READ
WORLD_READ )
#-------------------------------------------------------------------------------

View File

@ -0,0 +1,557 @@
//
// Copyright 2021 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.
//
#include "../bfr/faceSurface.h"
#include <algorithm>
#include <cstring>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Initialization utilities for both vertex and face-varying surfaces:
//
void
FaceSurface::preInitialize(FaceTopology const & faceTopology,
Index const faceIndices[]) {
//
// Initialize members, allocate subsets for the corners and clear
// tags combining features of all corners:
//
_topology = &faceTopology;
_indices = faceIndices;
_isFaceVarying = false;
_matchesVertex = false;
_corners.SetSize(GetFaceSize());
_combinedTag.Clear();
}
void
FaceSurface::postInitialize() {
//
// Determine if the surface is regular and if not, filter options
// that are not being used (to avoid them falsely indicating that
// two similar surfaces are different):
//
_isRegular = isRegular();
_optionsInEffect = GetSdcOptionsAsAssigned();
if (!_isRegular) {
reviseSdcOptionsInEffect();
}
}
//
// Initializers for vertex and face-varying surfaces:
//
void
FaceSurface::Initialize(FaceTopology const & vtxTopology,
Index const vtxIndices[]) {
preInitialize(vtxTopology, vtxIndices);
_isFaceVarying = false;
// WIP - we could reduce the subset by seeking delimiting inf-sharp
// edges, but not in the presence of a dart
bool useInfSharpSubsets = _topology->GetTag().HasInfSharpEdges() &&
!_topology->GetTag().HasInfSharpDarts();
//
// For each corner, identify the manifold subset containing the face
// and sharpen according to the vertex boundary interpolation option
// if warranted. Meanwhile, accumulate the combined set of tags for
// all corners:
//
for (int corner = 0; corner < GetFaceSize(); ++corner) {
FaceVertex const & vtxTop = GetCornerTopology(corner);
FaceVertexSubset & vtxSub = _corners[corner];
vtxTop.GetVertexSubset(&vtxSub);
if (vtxSub.IsBoundary() && !vtxSub.IsSharp()) {
sharpenBySdcVtxBoundaryInterpolation(&vtxSub, vtxTop);
}
if (useInfSharpSubsets && vtxTop.GetTag().HasInfSharpEdges()) {
// WIP - potentially reduce to a smaller subset here
}
_combinedTag.Combine(vtxSub.GetTag());
}
postInitialize();
}
void
FaceSurface::Initialize(FaceSurface const & vtxSurface,
Index const fvarIndices[]) {
preInitialize(*vtxSurface._topology, fvarIndices);
_isFaceVarying = true;
//
// For each corner, find the face-varying subset of the vertex subset
// and sharpen according to the face-varying interpolation option if
// warranted. Meanwhile, accumulate the combined set of tags for all
// corners, and whether the face-varying topology matches the vertex
// for all corners:
//
for (int corner = 0; corner < GetFaceSize(); ++corner) {
FaceVertex const & vtxTop = GetCornerTopology(corner);
FaceVertexSubset const & vtxSub = vtxSurface.GetCornerSubset(corner);
FaceVertexSubset & fvarSub = _corners[corner];
vtxTop.FindFaceVaryingSubset(&fvarSub, fvarIndices, vtxSub);
if (fvarSub.IsBoundary() && !fvarSub.IsSharp()) {
sharpenBySdcFVarLinearInterpolation(&fvarSub, fvarIndices,
vtxSub, vtxTop);
}
_combinedTag.Combine(fvarSub.GetTag());
_matchesVertex = _matchesVertex && fvarSub.ShapeMatchesSuperset(vtxSub);
fvarIndices += vtxTop.GetNumFaceVertices();
}
postInitialize();
}
//
// Minor methods supporting initialization:
//
bool
FaceSurface::isRegular() const {
//
// Immediate reject features from the combined tags (semi-sharp
// vertices, any sharp edges, any irregular face sizes) before
// testing valence and topology at each corner:
//
if (_combinedTag.HasSharpEdges() ||
_combinedTag.HasSemiSharpVertices() ||
_combinedTag.HasIrregularFaceSizes()) {
return false;
}
//
// If no boundaries, the interior case can be quickly determined:
//
if (!_combinedTag.HasBoundaryVertices()) {
if (_combinedTag.HasInfSharpVertices()) return false;
if (GetRegFaceSize() == 4) {
// Can use bitwise-OR here for reg valence of 4:
return (_corners[0].GetNumFaces() |
_corners[1].GetNumFaces() |
_corners[2].GetNumFaces() |
_corners[3].GetNumFaces()) == 4;
} else {
return (_corners[0].GetNumFaces() == 6) &&
(_corners[1].GetNumFaces() == 6) &&
(_corners[2].GetNumFaces() == 6);
}
}
//
// Test all corners for appropriate interior or boundary valence:
//
int regInteriorValence = (GetRegFaceSize() == 4) ? 4 : 6;
int regBoundaryValence = (regInteriorValence / 2);
for (int i = 0; i < GetFaceSize(); ++i) {
FaceVertexSubset const & corner = _corners[i];
if (corner.IsSharp()) {
if (corner.GetNumFaces() != 1) return false;
} else if (corner.IsBoundary()) {
if (corner.GetNumFaces() != regBoundaryValence) return false;
} else {
if (corner.GetNumFaces() != regInteriorValence) return false;
}
}
return true;
}
void
FaceSurface::reviseSdcOptionsInEffect() {
//
// "Override" (ignore, set to default) any options not affecting
// the shape of the limit surface. The boundary and face-varying
// interpolation options are fixed/ignored for all cases. Whether
// other options have an effect depends on the topology present.
//
// This is done, in part, to make accurate comparisons between
// the topologies of two surfaces. For example, the presence of
// differing creasing methods should not lead to two topologically
// identical surfaces with no creasing being considered different.
//
// This is to be used on construction on irregular surfaces AFTER
// the combined tags have been determined.
//
assert(!_isRegular);
MultiVertexTag const & tags = _combinedTag;
Sdc::Options & options = _optionsInEffect;
// Boundary and face-varying interpolation fixed/ignored for all:
options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
options.SetFVarLinearInterpolation( Sdc::Options::FVAR_LINEAR_ALL);
// Crease-method ignored when no semi-sharp creasing:
if (options.GetCreasingMethod() != Sdc::Options::CREASE_UNIFORM) {
if (!tags.HasSemiSharpEdges() && !tags.HasSemiSharpVertices()) {
options.SetCreasingMethod(Sdc::Options::CREASE_UNIFORM);
}
}
// Catmark triangle smoothing ignored if not Catmark with triangles:
if (options.GetTriangleSubdivision() != Sdc::Options::TRI_SUB_CATMARK) {
// This is slightly stronger than necessary -- will keep the
// tri-smooth setting if Catmark and any non-quads:
if ((GetSdcScheme() != Sdc::SCHEME_CATMARK) ||
!tags.HasIrregularFaceSizes()) {
options.SetTriangleSubdivision(Sdc::Options::TRI_SUB_CATMARK);
}
}
// Non-default values of any future options will warrant attention
}
//
// Internal methods to apply the Sdc boundary interpolation options for
// vertex and face-varying topology:
//
void
FaceSurface::sharpenBySdcVtxBoundaryInterpolation(FaceVertexSubset * vtxSub,
FaceVertex const & vtxTop) const {
assert(vtxSub->IsBoundary() && !vtxSub->IsSharp());
//
// Sharpen according to Sdc::Options::VtxBoundaryInterpolation:
//
// Remember vertex boundary interpolation is applied based on the
// full topology of the vertex not a particular subset (e.g. we can
// have a smooth corner in a subset delimited by inf-sharp edges).
// And edges are all implicitly sharpened -- leaving only corners to
// be sharpened -- making the EDGE_ONLY and EDGE_AND_CORNER names
// somewhat misleading.
//
bool isSharp = false;
switch (_topology->_schemeOptions.GetVtxBoundaryInterpolation()) {
case Sdc::Options::VTX_BOUNDARY_NONE:
// Nothing to do, as the name suggests
break;
case Sdc::Options::VTX_BOUNDARY_EDGE_ONLY:
// Edges are implicitly sharpened -- nothing more to do
break;
case Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER:
// Edges are implicitly sharpened -- sharpen any corners
isSharp = (vtxTop.GetNumFaces() == 1);
break;
default:
assert("Unknown value for Sdc::Options::VtxBoundaryInterpolation" == 0);
break;
}
if (isSharp) {
vtxTop.SharpenSubset(vtxSub);
}
}
namespace fvar_plus {
//
// This local namespace includes a few utilities for dealing solely
// with the CORNERS_PLUS1 and PLUS2 face-varying interpolation options.
//
// These "plus" options differ from the others in that the behavior
// within a face-varying subset is influenced by factors outside the
// subset, i.e. the presence of external face-varying indices or sharp
// edges.
//
typedef FaceSurface::Index Index;
//
// If more than two distinct face-varying subsets are present, the
// corner is sharpened regardless of any other conditions -- leaving
// cases of only one or two subsets to be dealt with.
//
bool
hasMoreThanTwoFVarSubsets(FaceVertex const & top,
Index const fvarIndices[]) {
Index indexCorner = top.GetFaceIndexAtCorner(fvarIndices);
Index indexOther = -1;
int numOtherEdgesDiscts = 1;
//
// Iterate through the faces and return if more than two unique
// fvar indices encountered, or more than two discts edges are
// found in the only other subset:
//
int numFaces = top.GetNumFaces();
for (int face = 0; face < numFaces; ++face) {
Index index = top.GetFaceIndexAtCorner(face, fvarIndices);
// Matches the corner's subset -- skip:
if (index == indexCorner) continue;
// Does not match corner's subset or the other subset -- done:
if ((indexOther >= 0) && (index != indexOther)) return true;
// Matches the "other" subset -- check for discontinuity
// between this face and the next:
indexOther = index;
int faceNext = top.GetFaceNext(face);
numOtherEdgesDiscts += (faceNext < 0) ||
!top.FaceIndicesMatchAcrossEdge(face, faceNext, fvarIndices);
if (numOtherEdgesDiscts > 2) return true;
}
return false;
}
//
// Two face-varying subsets are said to have "dependent sharpness"
// when the sharpness of one influences the other. This is applied
// when one subset has no sharp interior edges while the other does.
//
// NOTE that while these match the behavior of Far, it is unclear if
// Far's conditions are what was intended (need to compare to Hbr).
// If both subsets have a semi-sharp interior edge, the largest of
// the two should probably influence the other -- as is the case as
// one of those semi-sharp edges becomes inf-sharp.
//
bool
hasDependentSharpness(FaceVertex const & topology,
FaceVertexSubset const & subset) {
return ((topology.GetNumFaces() - subset.GetNumFaces()) > 1) &&
topology.GetTag().HasSharpEdges() &&
!subset.GetTag().HasSharpEdges();
}
//
// After the conditions for dependent sharpness have been confirmed,
// retrieve the desired value. The result is the maximum sharpness
// of interior edges that are outside the subset -- and do not lie
// on the seams between the two subsets.
//
float
getDependentSharpness(FaceVertex const & top,
FaceVertexSubset const & subset) {
// Identify the first and last faces of the subset -- to be
// skipped when searching for the largest interior sharp edge:
int firstFace = top.GetFaceFirst(subset);
int lastFace = top.GetFaceLast(subset);
// Skip the face or its neighbor with the shared leading edge:
int firstFacePrev = top.GetFacePrevious(firstFace);
int lastFaceNext = top.GetFaceNext(lastFace);
firstFace = (firstFacePrev < 0) ? -1 : firstFace;
lastFace = (lastFaceNext < 0) ? -1 : lastFaceNext;
// Search for largest interior sharp edge using leading edges:
float sharp = 0.0f;
for (int i = 0; i < top.GetNumFaces(); ++i) {
if (top.GetFacePrevious(i) >= 0) {
if ((i != firstFace) && (i != lastFace)) {
sharp = std::max(sharp, top.GetFaceEdgeSharpness(2*i));
}
}
}
// Must exceed vert sharpness to have any effect, otherwise ignore:
return (sharp > top.GetVertexSharpness()) ? sharp : 0.0f;
}
}
//
// The main method for affecting face-varying subsets according to the
// face-varying interpolation options. Most of these are trivial, with
// only the LINEAR_CORNERS_PLUS* cases requiring much effort.
//
void
FaceSurface::sharpenBySdcFVarLinearInterpolation(FaceVertexSubset * fvarSub,
Index const fvarIndices[],
FaceVertexSubset const & vtxSub,
FaceVertex const & vtxTop) const {
assert(fvarSub->IsBoundary() && !fvarSub->IsSharp());
// Each option applies rules to make the corner "linear", i.e. sharp:
bool isSharp = false;
switch (_topology->_schemeOptions.GetFVarLinearInterpolation()) {
case Sdc::Options::FVAR_LINEAR_NONE:
// Nothing to do, as the name suggests
break;
case Sdc::Options::FVAR_LINEAR_CORNERS_ONLY:
// Sharpen corners only:
isSharp = (fvarSub->GetNumFaces() == 1);
break;
case Sdc::Options::FVAR_LINEAR_CORNERS_PLUS1:
//
// Sharpen corners with more than two disjoint face-varying subsets
// and apply "dependent sharpness" (see above) when necessary:
//
isSharp = (fvarSub->GetNumFaces() == 1) ||
fvar_plus::hasMoreThanTwoFVarSubsets(vtxTop, fvarIndices);
if (!isSharp && fvar_plus::hasDependentSharpness(vtxTop, *fvarSub)) {
// Sharpen if sharp edges of other subset affects this one
vtxTop.SharpenSubset(fvarSub,
fvar_plus::getDependentSharpness(vtxTop, *fvarSub));
}
break;
case Sdc::Options::FVAR_LINEAR_CORNERS_PLUS2:
//
// Sharpen as with "plus1" above, in addition to sharpening both
// concave corners and darts.
//
// In other words, the only situations unsharpened are when either
// the face-varying and vertex subsets exactly match, or there
// are two fvar subsets that both have two or more faces (and no
// dependent sharpness between them).
//
isSharp = (fvarSub->GetNumFaces() == 1) ||
fvar_plus::hasMoreThanTwoFVarSubsets(vtxTop, fvarIndices);
if (!isSharp) {
// Distinguish by the number of faces outside the subset:
int numOtherFaces = vtxSub.GetNumFaces() - fvarSub->GetNumFaces();
if (numOtherFaces == 0) {
// Sharpen if a dart was created from a periodic vertex
isSharp = !vtxSub.IsBoundary();
} else if (numOtherFaces == 1) {
// Sharpen this concave corner since other subset is a corner
isSharp = true;
} else {
// Sharpen if sharp edges of other subset affects this one
if (fvar_plus::hasDependentSharpness(vtxTop, *fvarSub)) {
vtxTop.SharpenSubset(fvarSub,
fvar_plus::getDependentSharpness(vtxTop, *fvarSub));
}
}
}
break;
case Sdc::Options::FVAR_LINEAR_BOUNDARIES:
// Sharpen all boundaries:
isSharp = true;
break;
case Sdc::Options::FVAR_LINEAR_ALL:
assert("Unexpected FVarLinearInterpolation == FVAR_LINEAR_ALL" == 0);
break;
default:
assert("Unknown value for Sdc::Options::FVarLinearInterpolation" == 0);
break;
}
if (isSharp) {
vtxTop.SharpenSubset(fvarSub);
}
}
//
// Miscellaneous methods for debugging:
//
void
FaceSurface::print(bool printVerts) const {
MultiVertexTag const & tag = _combinedTag;
printf(" FaceTopology:\n");
printf(" face size = %d\n", _topology->GetFaceSize());
printf(" num-face-verts = %d\n", _topology->GetNumFaceVertices());
printf(" Properties:\n");
printf(" is regular = %d\n", IsRegular());
printf(" Combined tags:\n");
printf(" inf-sharp verts = %d\n", tag.HasInfSharpVertices());
printf(" semi-sharp verts = %d\n", tag.HasSemiSharpVertices());
printf(" inf-sharp edges = %d\n", tag.HasInfSharpEdges());
printf(" semi-sharp edges = %d\n", tag.HasSemiSharpEdges());
printf(" inf-sharp darts = %d\n", tag.HasInfSharpDarts());
printf(" unsharp boundary = %d\n", tag.HasNonSharpBoundary());
printf(" irregular faces = %d\n", tag.HasIrregularFaceSizes());
printf(" unordered verts = %d\n", tag.HasUnOrderedVertices());
if (printVerts) {
Index const * indices = _indices;
for (int i = 0; i < GetFaceSize(); ++i) {
FaceVertex const & top = GetCornerTopology(i);
FaceVertexSubset const & sub = GetCornerSubset(i);
printf(" corner %d:\n", i);
printf(" topology: num faces = %d, boundary = %d\n",
top.GetNumFaces(), top.GetTag().IsBoundary());
printf(" subset: num faces = %d, boundary = %d\n",
sub.GetNumFaces(), sub.IsBoundary());
printf(" num before = %d, num after = %d\n",
sub._numFacesBefore, sub._numFacesAfter);
printf(" face-vert indices:\n");
for (int j = 0, n = 0; j < top.GetNumFaces(); ++j) {
printf(" face %d: ", j);
int S = top.GetFaceSize(j);
for (int k = 0; k < S; ++k, ++n) {
printf("%3d", indices[n]);
}
printf("\n");
}
indices += top.GetNumFaceVertices();
}
}
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,215 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_FACE_SURFACE_H
#define OPENSUBDIV3_BFR_FACE_SURFACE_H
#include "../version.h"
#include "../bfr/faceTopology.h"
#include "../bfr/faceVertex.h"
#include "../vtr/stackBuffer.h"
#include "../vtr/types.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// The FaceSurface class combines references to several other classes and
// data to provide a complete description of the limit surface of a face.
//
// It is a simple aggregate of four sets of data:
//
// - an instance of FaceTopology with all topological information
// - a set of FaceVertexSubsets for topological extent of each corner
// - a set of indices associated with all vertices of FaceTopology
// - a subset of the Sdc::Options that actually affects the surface
//
// with a few additional members summarizing features of these. The full
// set of topology and corresponding indices are provided on construction
// and the rest are initialized as member variables.
//
// FaceSurfaces are constructed/initialized in two ways:
//
// - for the vertex topology of a face, initialization requires:
// - an instance of FaceTopology
// - vertex indices associated with the FaceTopology (though in
// some cases the vertex indices are not necessary)
//
// - for the face-varying topology of a face:
// - an instance of FaceSurface capturing the vertex topology
// - face-varying indices associated with the vertex topology
//
// Once initialized, other than a few simple queries, it serves solely
// as a container to be passed to other classes to assemble into regular
// or irregular surfaces.
//
class FaceSurface {
public:
typedef FaceTopology::Index Index;
public:
// Constructors for vertex and face-varying surfaces:
FaceSurface();
FaceSurface(FaceTopology const & vtxTopology, Index const vtxIndices[]);
FaceSurface(FaceSurface const & vtxSurface, Index const fvarIndices[]);
~FaceSurface() { }
bool IsInitialized() const;
void Initialize(FaceTopology const & vtxTopology, Index const vtxInds[]);
void Initialize(FaceSurface const & vtxSurface, Index const fvarInds[]);
// Main public methods to distinguish surface and topology:
bool IsRegular() const { return _isRegular; }
bool FVarTopologyMatchesVertex() const { return _matchesVertex; }
// Debugging:
void print(bool printVerts = false) const;
public:
// Public access to the main members:
FaceTopology const & GetTopology() const { return *_topology; }
FaceVertexSubset const * GetSubsets() const { return _corners; }
Index const * GetIndices() const { return _indices; }
MultiVertexTag GetTag() const { return _combinedTag; }
public:
// Additional public access to data used by builder classes:
int GetFaceSize() const;
int GetRegFaceSize() const;
Sdc::SchemeType GetSdcScheme() const;
Sdc::Options GetSdcOptionsInEffect() const;
Sdc::Options GetSdcOptionsAsAssigned() const;
FaceVertex const & GetCornerTopology(int corner) const;
FaceVertexSubset const & GetCornerSubset(int corner) const;
int GetNumIndices() const;
private:
// Internal methods:
void preInitialize(FaceTopology const & topology, Index const indices[]);
void postInitialize();
bool isRegular() const;
void reviseSdcOptionsInEffect();
// Methods to apply specified interpolation options to the corners:
void sharpenBySdcVtxBoundaryInterpolation(
FaceVertexSubset * vtxSubsetPtr,
FaceVertex const & cornerTopology) const;
void sharpenBySdcFVarLinearInterpolation(
FaceVertexSubset * fvarSubsetPtr,
Index const fvarIndices[],
FaceVertexSubset const & vtxSubset,
FaceVertex const & cornerTopology) const;
private:
typedef Vtr::internal::StackBuffer<FaceVertexSubset,8,true> CornerArray;
FaceTopology const * _topology;
Index const * _indices;
CornerArray _corners;
// Members reflecting the effective subset of topology and options:
MultiVertexTag _combinedTag;
Sdc::Options _optionsInEffect;
unsigned int _isFaceVarying : 1;
unsigned int _matchesVertex : 1;
unsigned int _isRegular : 1;
};
//
// Inline constructors:
//
inline
FaceSurface::FaceSurface() : _topology(0), _indices(0) {
}
inline
FaceSurface::FaceSurface(FaceTopology const & vtxTop, Index const vIndices[]) {
Initialize(vtxTop, vIndices);
}
inline
FaceSurface::FaceSurface(FaceSurface const & vtxSurf, Index const fvIndices[]) {
Initialize(vtxSurf, fvIndices);
}
//
// Inline accessors:
//
inline bool
FaceSurface::IsInitialized() const {
return _topology != 0;
}
inline int
FaceSurface::GetFaceSize() const {
return _topology->GetFaceSize();
}
inline int
FaceSurface::GetRegFaceSize() const {
return _topology->GetRegFaceSize();
}
inline Sdc::SchemeType
FaceSurface::GetSdcScheme() const {
return _topology->_schemeType;
}
inline Sdc::Options
FaceSurface::GetSdcOptionsInEffect() const {
return _optionsInEffect;
}
inline Sdc::Options
FaceSurface::GetSdcOptionsAsAssigned() const {
return _topology->_schemeOptions;
}
inline FaceVertex const &
FaceSurface::GetCornerTopology(int corner) const {
return _topology->GetTopology(corner);
}
inline FaceVertexSubset const &
FaceSurface::GetCornerSubset(int corner) const {
return _corners[corner];
}
inline int
FaceSurface::GetNumIndices() const {
return _topology->GetNumFaceVertices();
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_FACE_SURFACE_H */

View File

@ -0,0 +1,172 @@
//
// Copyright 2021 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.
//
#include "../bfr/faceTopology.h"
#include "../sdc/crease.h"
#include <cstring>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Constructor needs the same Sdc scheme/options as the SurfaceFactory
// to support internal work -- may need to figure another way to assign
// these if we later need a default constructor for some purpose...
//
FaceTopology::FaceTopology(Sdc::SchemeType schemeType,
Sdc::Options schemeOptions) :
_schemeType(schemeType),
_schemeOptions(schemeOptions),
_regFaceSize(Sdc::SchemeTypeTraits::GetRegularFaceSize(schemeType)),
_isInitialized(false) {
}
//
// Main initialize/finalize used by base factory to delimit assignment:
//
void
FaceTopology::Initialize(int faceSize) {
_faceSize = faceSize;
_numFaceVertsTotal = 0;
_combinedTag.Clear();
_isInitialized = true;
_isFinalized = false;
_corner.SetSize(faceSize);
}
void
FaceTopology::Finalize() {
//
// Inspect all corner vertex topologies -- accumulating the presence
// of irregular features for the face and assigning other internal
// members used to assemble the limit surface:
//
// WIP - potentially want to identify presence of degenerate faces
// below too, i.e. face size < 3. A subclass may specify these in
// an ordered set and that would mess up some of the topological
// traversals. In such case, we can initialize the vertex subset
// to excludes such faces -- treating their edges as non-manifold.
//
// Probably need to add yet another bit per vertex here to know when
// to process an otherwise simple manifold ring, i.e. hasDegenFaces
//
assert(_isInitialized);
for (int i = 0; i < _faceSize; ++i) {
FaceVertex & cTop = GetTopology(i);
_combinedTag.Combine(cTop.GetTag());
_numFaceVertsTotal += cTop.GetNumFaceVertices();
}
_isFinalized = true;
}
void
FaceTopology::ResolveUnOrderedCorners(Index const fvIndices[]) {
//
// Inspect and deal with any corner that did not have its incident
// faces specified in counter-clockwise order (and so which may be
// non-manifold). The face-vertex indices are required for the
// corner to identify the connectivity between them for later use:
//
// Be sure to reset the combined tags as resolution of ordering
// will also detect non-manifold (or manifold) features, e.g.
// sharpening or the presence of boundaries edges.
//
_combinedTag.Clear();
for (int i = 0; i < _faceSize; ++i) {
FaceVertex & cTop = GetTopology(i);
if (cTop.GetTag().IsUnOrdered()) {
cTop.ConnectUnOrderedFaces(fvIndices);
}
_combinedTag.Combine(cTop.GetTag());
fvIndices += cTop.GetNumFaceVertices();
}
}
void
FaceTopology::print(Index const faceVertIndices[]) const {
MultiVertexTag const & tag = _combinedTag;
printf("FaceTopology:\n");
printf(" face size = %d\n", _faceSize);
printf(" num-face-verts = %d\n", _numFaceVertsTotal);
printf(" Tags:\n");
printf(" inf-sharp verts = %d\n", tag.HasInfSharpVertices());
printf(" semi-sharp verts = %d\n", tag.HasSemiSharpVertices());
printf(" inf-sharp edges = %d\n", tag.HasInfSharpEdges());
printf(" semi-sharp edges = %d\n", tag.HasSemiSharpEdges());
printf(" inf-sharp darts = %d\n", tag.HasInfSharpDarts());
printf(" unsharp boundary = %d\n", tag.HasNonSharpBoundary());
printf(" irregular faces = %d\n", tag.HasIrregularFaceSizes());
printf(" unordered verts = %d\n", tag.HasUnOrderedVertices());
if (faceVertIndices) {
Index const * cornerFaceVertIndices = faceVertIndices;
for (int i = 0; i < _faceSize; ++i) {
printf(" corner %d:\n", i);
FaceVertex const & cTop = GetTopology(i);
printf(" topology: num faces = %d, boundary = %d\n",
cTop.GetNumFaces(), cTop.GetTag().IsBoundary());
printf(" face-vert indices:\n");
for (int j = 0, n = 0; j < cTop.GetNumFaces(); ++j) {
printf(" face %d: ", j);
int S = cTop.GetFaceSize(j);
for (int k = 0; k < S; ++k, ++n) {
printf("%3d", cornerFaceVertIndices[n]);
}
printf("\n");
}
cornerFaceVertIndices += cTop.GetNumFaceVertices();
}
}
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,119 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_FACE_TOPOLOGY_H
#define OPENSUBDIV3_BFR_FACE_TOPOLOGY_H
#include "../version.h"
#include "../bfr/faceVertex.h"
#include "../vtr/stackBuffer.h"
#include "../sdc/types.h"
#include "../sdc/options.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// The FaceTopology class describes the full topological neighborhood
// around a base face of a mesh, which includes everything topologically
// necessary to define the limit surface for that face.
//
// It is used solely by the base SurfaceFactory class -- first partially
// populated by subclasses before being inspected and augmented to help
// assemble the limit surface for the face. Its members include some of
// the members of the SurfaceFactory class (e.g. the subdivision scheme
// and options) to make them more available for its purposes.
//
// The primary component of FaceTopology is an array of instances of
// the FaceVertex class (one for each vertex of the face), which is a
// lightweight wrapper around the public VertexDescriptor class that is
// populated by subclasses of SurfaceFactory.
//
// FaceTopology is one of three key components in defining the limit
// surface around a face. The others are a set of FaceVertexSubsets (one
// for each FaceVertex) that specify the subset of the neighborhood of
// the corners of the face that actually do contribute to its surface,
// and the indices associated with vertices that FaceTopology describes
// (which become the control points of the limit surface).
//
class FaceTopology {
public:
typedef FaceVertex::Index Index;
public:
FaceTopology(Sdc::SchemeType schemeType,
Sdc::Options schemeOptions);
~FaceTopology() { }
void Initialize(int faceSize);
void Finalize();
public:
Sdc::SchemeType GetSchemeType() const { return _schemeType; }
Sdc::Options GetSchemeOptions() const { return _schemeOptions; }
int GetFaceSize() const { return _faceSize; }
int GetRegFaceSize() const { return _regFaceSize; }
FaceVertex & GetTopology(int i) { return _corner[i]; }
FaceVertex const & GetTopology(int i) const { return _corner[i]; }
MultiVertexTag const GetTag() const { return _combinedTag; }
int GetNumFaceVertices() const { return _numFaceVertsTotal; }
int GetNumFaceVertices(int i) const{return _corner[i].GetNumFaceVertices();}
// Methods to test for and resolve unordered corners of the face:
bool HasUnOrderedCorners() const { return GetTag().HasUnOrderedVertices(); }
void ResolveUnOrderedCorners(Index const faceVertexIndices[]);
// Debugging...
void print(Index const faceVertIndices[]) const;
public:
Sdc::SchemeType _schemeType;
Sdc::Options _schemeOptions;
int _faceSize;
int _regFaceSize;
int _numFaceVertsTotal;
MultiVertexTag _combinedTag;
unsigned short _isInitialized : 1;
unsigned short _isFinalized : 1;
Vtr::internal::StackBuffer<FaceVertex,4> _corner;
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_FACE_TOPOLOGY_H */

View File

@ -0,0 +1,903 @@
//
// Copyright 2021 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.
//
#include "../bfr/faceVertex.h"
#include "../sdc/crease.h"
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <map>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Main initialize and finalize methods used to bracket the assignment
// by clients to the VertexDescriptor member:
//
void
FaceVertex::Initialize(int faceSize, int regFaceSize) {
_commonFaceSize = (short) faceSize;
_regFaceSize = (unsigned char) regFaceSize;
_numFaceVerts = 0;
_isExpInfSharp = false;
_isExpSemiSharp = false;
_isImpInfSharp = false;
_isImpSemiSharp = false;
_vDesc._isValid = false;
_vDesc._isInitialized = false;
}
void
FaceVertex::Finalize(int faceInVertex) {
assert(_vDesc._isFinalized);
_faceInRing = (short) faceInVertex;
//
// Initialize members from the VertexDescriptor:
//
if (!_vDesc.HasIncidentFaceSizes()) {
// Common face size was previously initialized to the face size
_numFaceVerts = _vDesc._numFaces * _commonFaceSize;
} else {
_commonFaceSize = 0;
// Recall face sizes are available as differences between offsets:
_numFaceVerts = _vDesc._faceSizeOffsets[_vDesc._numFaces];
}
// Vertex sharpness:
_isExpInfSharp = Sdc::Crease::IsInfinite(_vDesc._vertSharpness);
_isExpSemiSharp = Sdc::Crease::IsSemiSharp(_vDesc._vertSharpness);
//
// Initialize tags from VertexDescriptor and other members
//
// Note that not all tags can be assigned at this point if the vertex
// is defined by a set of unordered faces. In such cases, the tags
// will be assigned later when the connectivity between incident faces
// is determined. Those that can be assigned regardless of ordering
// are set here -- splitting the assignment of those remaining between
// ordered and unordered cases.
//
_tag.Clear();
_tag._unCommonFaceSizes = _vDesc.HasIncidentFaceSizes();
_tag._irregularFaceSizes = (_commonFaceSize != _regFaceSize);
_tag._infSharpVerts = _isExpInfSharp;
_tag._semiSharpVerts = _isExpSemiSharp;
_tag._unOrderedFaces = !_vDesc.IsManifold();
if (_vDesc.IsManifold()) {
finalizeOrderedTags();
}
}
void
FaceVertex::finalizeOrderedTags() {
//
// A vertex with a set of ordered faces is required to be manifold:
//
_tag._unOrderedFaces = false;
_tag._nonManifoldVerts = false;
_tag._boundaryVerts = _vDesc.IsBoundary();
_tag._boundaryNonSharp = _vDesc.IsBoundary();
//
// Assign tags (and other members) affected by edge sharpness:
//
if (_vDesc.HasEdgeSharpness()) {
float const * sharpness = &_vDesc._faceEdgeSharpness[0];
// Detect unsharpened boundary edges:
bool isBoundary = _tag._boundaryVerts;
if (isBoundary) {
int last = 2 * _vDesc._numFaces - 1;
_tag._boundaryNonSharp =
!Sdc::Crease::IsInfinite(sharpness[0]) ||
!Sdc::Crease::IsInfinite(sharpness[last]);
}
// Detect interior inf-sharp and semi-sharp edges:
int numInfSharpEdges = 0;
int numSemiSharpEdges = 0;
for (int i = isBoundary; i < _vDesc._numFaces; ++i ) {
if (Sdc::Crease::IsInfinite(sharpness[2*i])) {
++ numInfSharpEdges;
} else if (Sdc::Crease::IsSharp(sharpness[2*i])) {
++ numSemiSharpEdges;
}
}
// Mark the presence of interior sharp edges:
_tag._infSharpEdges = (numInfSharpEdges > 0);
_tag._semiSharpEdges = (numSemiSharpEdges > 0);
_tag._infSharpDarts = (numInfSharpEdges == 1) && !isBoundary;
// Detect edges effectively making the vertex sharp -- note that
// a vertex can be both explicitly and implicitly sharp (e.g. low
// semi-sharp vertex value with a higher semi-sharp edge):
int numInfSharpTotal = numInfSharpEdges + isBoundary * 2;
if (numInfSharpTotal > 2) {
_isImpInfSharp = true;
} else if ((numInfSharpTotal + numSemiSharpEdges) > 2) {
_isImpSemiSharp = true;
}
// Mark the vertex inf-sharp if implicitly inf-sharp:
if (!_isExpInfSharp && _isImpInfSharp) {
_tag._infSharpVerts = true;
_tag._semiSharpVerts = false;
}
}
}
bool
FaceVertex::HasImplicitVertexSharpness() const {
return _isImpInfSharp || _isImpSemiSharp;
}
float
FaceVertex::GetImplicitVertexSharpness() const {
if (_isImpInfSharp) {
return Sdc::Crease::SHARPNESS_INFINITE;
}
assert(_isImpSemiSharp);
//
// Since this will be applied at an inf-sharp crease, there will be
// two inf-sharp edges in addition to the semi-sharp, so we only
// need find the max of the semi-sharp edges and whatever explicit
// vertex sharpness may have been assigned. Iterate through all
// faces and inspect the sharpness of each leading interior edge:
//
float sharpness = GetVertexSharpness();
for (int i = 0; i < GetNumFaces(); ++i) {
if (GetFacePrevious(i) >= 0) {
sharpness = std::max(sharpness, GetFaceEdgeSharpness(2*i));
}
}
return sharpness;
}
//
// Methods to initialize and/or find subsets of the corner's topology:
//
int
FaceVertex::initCompleteSubset(Subset * subsetPtr) const {
Subset & subset = *subsetPtr;
//
// Initialize with tags and assign the extent:
//
int numFaces = GetNumFaces();
subset.Initialize(GetTag());
subset._numFacesTotal = (short) numFaces;
if (isInterior()) {
subset._numFacesBefore = 0;
subset._numFacesAfter = (short)(numFaces - 1);
} else if (isOrdered()) {
subset._numFacesBefore = _faceInRing;
subset._numFacesAfter = (short)(numFaces - 1 - subset._numFacesBefore);
} else {
// Unordered faces -- boundary needs to identify its orientation:
subset._numFacesAfter = 0;
for (int f = GetFaceNext(_faceInRing); f >= 0; f = GetFaceNext(f)) {
++ subset._numFacesAfter;
}
subset._numFacesBefore = (short)(numFaces - 1 - subset._numFacesAfter);
}
return subset._numFacesTotal;
}
int
FaceVertex::findConnectedSubsetExtent(Subset * subsetPtr) const {
Subset & subset = *subsetPtr;
//
// Initialize with tags and mark manifold:
//
subset.Initialize(GetTag());
subset._tag._nonManifoldVerts = false;
// Add faces to the dflt single face extent by seeking forward/backward:
int fStart = _faceInRing;
for (int f = GetFaceNext(fStart); f >= 0; f = GetFaceNext(f)) {
if (f == fStart) {
// Periodic -- tag as such and return:
subset.SetBoundary(false);
return subset._numFacesTotal;
}
subset._numFacesAfter ++;
subset._numFacesTotal ++;
}
for (int f = GetFacePrevious(fStart); f >= 0; f = GetFacePrevious(f)) {
subset._numFacesBefore ++;
subset._numFacesTotal ++;
}
subset.SetBoundary(true);
return subset._numFacesTotal;
}
int
FaceVertex::GetVertexSubset(Subset * subsetPtr) const {
//
// The subset from a manifold vertex is trivially complete (ordered
// or not), but for non-manifold cases we need to search and update
// the tags according to the content of the subset:
//
if (isManifold()) {
initCompleteSubset(subsetPtr);
} else {
findConnectedSubsetExtent(subsetPtr);
adjustSubsetTags(subsetPtr);
// And if on a non-manifold crease, test for implicit sharpness:
if (!subsetPtr->IsSharp() && HasImplicitVertexSharpness()) {
SharpenSubset(subsetPtr, GetImplicitVertexSharpness());
}
}
return subsetPtr->_numFacesTotal;
}
int
FaceVertex::findFVarSubsetExtent(Subset const & vtxSub,
Subset * fvarSubsetPtr,
Index const fvarIndices[]) const {
Subset & fvarSub = *fvarSubsetPtr;
//
// Initialize with tags and declare as a boundary to start:
//
fvarSub.Initialize(vtxSub._tag);
fvarSub.SetBoundary(true);
if (vtxSub._numFacesTotal == 1) return 1;
//
// Inspect/gather faces "after" (counter-clockwise order from) the
// corner face. If we arrive back at the corner face, a periodic
// set is complete, but check the continuity of the seam and apply
// before returning:
//
int cornerFace = _faceInRing;
int numFacesAfterToVisit = vtxSub._numFacesAfter;
if (numFacesAfterToVisit) {
int thisFace = cornerFace;
int nextFace = GetFaceNext(thisFace);
for (int i = 0; i < numFacesAfterToVisit; ++i) {
if (!FaceIndicesMatchAcrossEdge(thisFace, nextFace, fvarIndices)) {
break;
}
++ fvarSub._numFacesAfter;
++ fvarSub._numFacesTotal;
thisFace = nextFace;
nextFace = GetFaceNext(thisFace);
}
if (nextFace == cornerFace) {
assert(vtxSub._numFacesBefore == 0);
if (FaceIndicesMatchAtEdgeEnd(thisFace, cornerFace, fvarIndices)) {
fvarSub.SetBoundary(false);
}
return fvarSub._numFacesTotal;
}
}
//
// Inspect/gather faces "before" (clockwise order from) the corner
// face. Include any faces "after" in the case of a periodic vertex
// that was interrupted by a discontinuity above:
//
int numFacesBeforeToVisit = vtxSub._numFacesBefore;
if (!vtxSub.IsBoundary()) {
numFacesBeforeToVisit += vtxSub._numFacesAfter - fvarSub._numFacesAfter;
}
if (numFacesBeforeToVisit) {
int thisFace = cornerFace;
int prevFace = GetFacePrevious(thisFace);
for (int i = 0; i < numFacesBeforeToVisit; ++i) {
if (!FaceIndicesMatchAcrossEdge(prevFace, thisFace, fvarIndices)) {
break;
}
++ fvarSub._numFacesBefore;
++ fvarSub._numFacesTotal;
thisFace = prevFace;
prevFace = GetFacePrevious(thisFace);
}
}
return fvarSub._numFacesTotal;
}
int
FaceVertex::FindFaceVaryingSubset(Subset * fvarSubsetPtr,
Index const fvarIndices[],
Subset const & vtxSub) const {
Subset & fvarSub = *fvarSubsetPtr;
//
// Find the face-varying extent and update the tags if its topology
// is a true subset of the vertex. Also reset the sharpness in this
// case as the rules for the FVar interpolation options (applied
// later) take precedence over those of the vertex:
//
findFVarSubsetExtent(vtxSub, &fvarSub, fvarIndices);
bool fvarTopologyMatchesVertex = fvarSub.ExtentMatchesSuperset(vtxSub);
if (!fvarTopologyMatchesVertex) {
if (fvarSub.IsSharp()) {
UnSharpenSubset(&fvarSub);
}
adjustSubsetTags(&fvarSub, &vtxSub);
}
// Sharpen if the vertex is non-manifold:
if (!fvarSub.IsSharp() && !isManifold()) {
SharpenSubset(&fvarSub);
}
// Sharpen if the face-varying value is non-manifold, i.e. if there
// are any occurrences of the corner FVar index outside the subset:
if (!fvarSub.IsSharp() && (fvarSub.GetNumFaces() < vtxSub.GetNumFaces())) {
Index fvarMatch = GetFaceIndexAtCorner(fvarIndices);
int numMatches = 0;
for (int i = 0; i < GetNumFaces(); ++i) {
numMatches += (GetFaceIndexAtCorner(i, fvarIndices) == fvarMatch);
if (numMatches > fvarSub.GetNumFaces()) {
SharpenSubset(&fvarSub);
break;
}
}
}
return fvarSub.GetNumFaces();
}
//
// Method to revise the tags for a subset of the corner, which may no
// longer include properties that trigger exceptional behavior:
//
void
FaceVertex::SharpenSubset(Subset * subset) const {
// Mark the subset sharp and ensure any related tags are also
// updated accordingly:
subset->_tag._infSharpVerts = true;
subset->_tag._semiSharpVerts = false;
}
void
FaceVertex::UnSharpenSubset(Subset * subset) const {
// Restore subset sharpness based on actual sharpness assignment:
subset->_tag._infSharpVerts = _isExpInfSharp;
subset->_tag._semiSharpVerts = _isExpSemiSharp;
}
void
FaceVertex::SharpenSubset(Subset * subset, float sharpness) const {
// Mark the subset according to sharpness value
if (sharpness > subset->_localSharpness) {
subset->_localSharpness = sharpness;
subset->_tag._infSharpVerts = Sdc::Crease::IsInfinite(sharpness);
subset->_tag._semiSharpVerts = Sdc::Crease::IsSemiSharp(sharpness);
}
}
bool
FaceVertex::subsetHasIrregularFaces(Subset const & subset) const {
assert(_tag.HasIrregularFaceSizes());
if (!_tag._unCommonFaceSizes) return true;
int f = GetFaceFirst(subset);
for (int i = 0; i < subset.GetNumFaces(); ++i, f = GetFaceNext(f)) {
if (GetFaceSize(f) != _regFaceSize) return true;
}
return false;
}
bool
FaceVertex::subsetHasInfSharpEdges(Subset const & subset) const {
assert(_tag.HasInfSharpEdges());
int n = subset.GetNumFaces();
if (n > 1) {
int f = GetFaceFirst(subset);
// Reduce number of faces to visit when inspecting trailing edges:
for (int i = subset.IsBoundary(); i < n; ++i, f = GetFaceNext(f)) {
if (IsFaceEdgeInfSharp(f, 1)) return true;
}
}
return false;
}
bool
FaceVertex::subsetHasSemiSharpEdges(Subset const & subset) const {
assert(_tag.HasSemiSharpEdges());
int n = subset.GetNumFaces();
if (n > 1) {
int f = GetFaceFirst(subset);
// Reduce number of faces to visit when inspecting trailing edges:
for (int i = subset.IsBoundary(); i < n; ++i, f = GetFaceNext(f)) {
if (IsFaceEdgeSemiSharp(f, 1)) return true;
}
}
return false;
}
void
FaceVertex::adjustSubsetTags(Subset * subset,
Subset const * superset) const {
VertexTag & subsetTag = subset->_tag;
// Adjust any tags related to boundary or sharpness status:
if (subsetTag.IsBoundary()) {
subsetTag._infSharpDarts = false;
}
if (subsetTag.IsInfSharp()) {
subsetTag._semiSharpVerts = false;
}
// Adjust for the presence of irregular faces or sharp edges if the
// subset is actually a proper subset of this entire corner or the
// optionally provided superset:
int numSuperFaces = superset ? superset->GetNumFaces() : GetNumFaces();
bool superBoundary = superset ? superset->IsBoundary() : isBoundary();
if ((subset->GetNumFaces() < numSuperFaces) ||
(subset->IsBoundary() != superBoundary)) {
if (subsetTag._irregularFaceSizes) {
subsetTag._irregularFaceSizes = subsetHasIrregularFaces(*subset);
}
if (subsetTag._infSharpEdges) {
subsetTag._infSharpEdges = subsetHasInfSharpEdges(*subset);
if (subsetTag._infSharpEdges && subset->IsBoundary()) {
SharpenSubset(subset);
}
}
if (subsetTag._semiSharpEdges) {
subsetTag._semiSharpEdges = subsetHasSemiSharpEdges(*subset);
}
}
}
//
// Main and supporting internal datatypes and methods to connect unordered
// faces and allow for topological traversals of the incident faces:
//
// The fundamental element of this process is the following definition of
// an Edge. It is lightweight and only stores a state (boundary, interior,
// or non-manifold) along with the one or two faces for a manifold edge.
// It is initialized as a boundary when first created and is then modified
// by adding additional incident faces.
//
struct FaceVertex::Edge {
// Empty constructor intentional since we over-allocate what we need:
Edge() { }
void clear() { std::memset(this, 0, sizeof(*this)); }
void Initialize(Index vtx) { clear(), endVertex = vtx; }
// Transition of state as incident faces are added:
void SetBoundary() { boundary = 1; }
void SetInterior() { boundary = 0, interior = 1; }
void SetNonManifold() { boundary = 0, interior = 0, nonManifold = 1; }
// Special cases forcing non-manifold
void SetDegenerate() { SetNonManifold(), degenerate = 1; }
void SetDuplicate() { SetNonManifold(), duplicate = 1; }
void SetSharpness(float sharpness) {
if (sharpness > 0.0f) {
if (Sdc::Crease::IsInfinite(sharpness)) {
infSharp = true;
} else {
semiSharp = true;
}
}
}
void SetFace(int newFace, bool newTrailing) {
trailing = newTrailing;
*(trailing ? &prevFace : &nextFace) = (short) newFace;
}
void AddFace(int newFace, bool newTrailing) {
// Update the state of the Edge based on the added incident face:
if (boundary) {
if (newTrailing == trailing) {
// Edge is reversed
SetNonManifold();
} else if (newFace == (trailing ? prevFace : nextFace)) {
// Edge is repeated in the face
SetNonManifold();
} else {
// Edge is manifold thus far -- promote to interior
SetInterior();
SetFace(newFace, newTrailing);
}
} else if (interior) {
// More than two incident faces -- make non-manifold
SetNonManifold();
}
}
Index endVertex;
unsigned short boundary : 1;
unsigned short interior : 1;
unsigned short nonManifold : 1;
unsigned short trailing : 1;
unsigned short degenerate : 1;
unsigned short duplicate : 1;
unsigned short infSharp : 1;
unsigned short semiSharp : 1;
short prevFace, nextFace;
};
void
FaceVertex::ConnectUnOrderedFaces(Index const fvIndices[]) {
//
// There are two transient sets of data needed here: a set of Edges
// that connect adjoining faces, and a set of indices (one for each
// of the 2*N face-edges) to identify the Edge for each face-edge.
//
// IMPORTANT -- since these later edge indices are of the same type
// and size as the internal face-edge neighbors, we'll use that array
// to avoid a separate declaration (and possible allocation) and will
// update it in place later.
//
int numFaceEdges = GetNumFaces() * 2;
_faceEdgeNeighbors.SetSize(numFaceEdges);
// Allocate and populate the edges and indices referring to them.
// Initialization fails to detect some "duplicate" edges in a face,
// so post-process to catch these before continuing:
Vtr::internal::StackBuffer<Edge,32,true> edges(numFaceEdges);
short * feEdges = &_faceEdgeNeighbors[0];
int numEdges = createUnOrderedEdges(edges, feEdges, fvIndices);
markDuplicateEdges(edges, feEdges, fvIndices);
// Use the connecting edges to assign neighboring faces (overwriting
// our edge indices) and finish initializing the tags retaining the
// properties of the corner:
assignUnOrderedFaceNeighbors(edges, feEdges);
finalizeUnOrderedTags(edges, numEdges);
}
//
// Identify a set of shared edges between unordered faces so that we can
// establish connections between them.
//
// The "face-edge edges" are really just half-edges that refer (by index)
// to potentially shared Edges. As Edges are created, these half-edges
// are made to refer to them, after which the state of the edge may change
// due to the presence or orientation of additional incident faces.
//
int
FaceVertex::createUnOrderedEdges(Edge edges[],
short feEdges[],
Index const fvIndices[]) const {
// Optional map to help construction for high valence:
typedef std::map<Index,int> EdgeMap;
EdgeMap edgeMap;
bool useMap = (GetNumFaces() > 16);
//
// Iterate through the face-edge pairs to find connecting edges:
//
Index vCorner = GetFaceIndexAtCorner(0, fvIndices);
int numFaceEdges = 2 * GetNumFaces();
int numEdges = 0;
// Don't rely on the tag yet to determine presence of sharpness:
bool hasSharpness = _vDesc.HasEdgeSharpness();
for (int feIndex = 0; feIndex < numFaceEdges; ++feIndex) {
Index vIndex = (feIndex & 1) ?
GetFaceIndexTrailing((feIndex >> 1), fvIndices) :
GetFaceIndexLeading( (feIndex >> 1), fvIndices);
int eIndex = -1;
if (vIndex != vCorner) {
if (useMap) {
EdgeMap::iterator eFound = edgeMap.find(vIndex);
if (eFound != edgeMap.end()) {
eIndex = eFound->second;
} else {
// Make sure to create the new edge below at this index
edgeMap[vIndex] = numEdges;
}
} else {
for (int j = 0; j < numEdges; ++j) {
if (edges[j].endVertex == vIndex) {
eIndex = j;
break;
}
}
}
// Update an existing edge or create a new one
if (eIndex >= 0) {
edges[eIndex].AddFace(feIndex >> 1, feIndex & 1);
} else {
// Index of the new (pre-allocated) edge:
eIndex = numEdges ++;
// Initialize a new edge as boundary (manifold)
Edge & E = edges[eIndex];
E.Initialize(vIndex);
E.SetBoundary();
E.SetFace(feIndex >> 1, feIndex & 1);
if (hasSharpness) {
E.SetSharpness(GetFaceEdgeSharpness(feIndex));
}
}
} else {
// If degenerate, create unique edge (non-manifold)
eIndex = numEdges++;
edges[eIndex].Initialize(vIndex);
edges[eIndex].SetDegenerate();
}
assert(eIndex >= 0);
feEdges[feIndex] = (short) eIndex;
}
return numEdges;
}
void
FaceVertex::markDuplicateEdges(Edge edges[],
short const feEdges[],
Index const fvIndices[]) const {
//
// The edge assignment thus far does not correctly detect the presence
// of all edges repeated or duplicated in the same face, e.g. for quad
// with vertices {A, B, A, C} the edge AB occurs both as AB and BA.
// When the face is oriented relative to corner B, we have {B, A, C, A}
// and edge BA will be detected as non-manifold -- but not from corner
// A or C.
//
// So look for repeated instances of the corner vertex in the face and
// inspect its neighbors to see if they match the leading or trailing
// edges.
//
// This is a trivial test for a quad: if the opposite vertex matches
// the corner vertex, both the leading and trailing edges will be
// duplicated and so can immediately be marked non-manifold. So deal
// with the common case of all neighboring quads separately.
//
if (_commonFaceSize == 3) return;
Index vCorner = fvIndices[0];
int numFaces = GetNumFaces();
if (_commonFaceSize == 4) {
Index const * fvOpposite = fvIndices + 2;
for (int face = 0; face < numFaces; ++face, fvOpposite += 4) {
if (*fvOpposite == vCorner) {
edges[feEdges[2*face ]].SetDuplicate();
edges[feEdges[2*face+1]].SetDuplicate();
}
}
} else {
Index const * fv = fvIndices;
for (int face = 0; face < numFaces; ++face) {
int faceSize = GetFaceSize(face);
if (faceSize == 4) {
if (fv[2] == vCorner) {
edges[feEdges[2*face ]].SetDuplicate();
edges[feEdges[2*face+1]].SetDuplicate();
}
} else {
for (int j = 2; j < (faceSize - 2); ++j) {
if (fv[j] == vCorner) {
if (fv[j-1] == fv[1])
edges[feEdges[2*face ]].SetDuplicate();
if (fv[j+1] == fv[faceSize-1])
edges[feEdges[2*face+1]].SetDuplicate();
}
}
}
fv += faceSize;
}
}
}
void
FaceVertex::assignUnOrderedFaceNeighbors(Edge const edges[],
short const feEdges[]) {
int numFaceEdges = 2 * GetNumFaces();
for (int i = 0; i < numFaceEdges; ++i) {
assert(feEdges[i] >= 0);
Edge const & E = edges[feEdges[i]];
bool edgeIsSingular = E.nonManifold || E.boundary;
if (edgeIsSingular) {
_faceEdgeNeighbors[i] = -1;
} else {
_faceEdgeNeighbors[i] = (i & 1) ? E.nextFace : E.prevFace;
}
}
}
void
FaceVertex::finalizeUnOrderedTags(Edge const edges[], int numEdges) {
//
// Summarize properties of the corner given the number and nature of
// the edges around its vertex and initialize remaining members or
// tags that depend on them.
//
// First, take inventory of relevant properties from the edges:
//
int numNonManifoldEdges = 0;
int numInfSharpEdges = 0;
int numSemiSharpEdges = 0;
int numSingularEdges = 0;
bool hasBoundaryEdges = false;
bool hasBoundaryEdgesNotSharp = false;
bool hasDegenerateEdges = false;
bool hasDuplicateEdges = false;
for (int i = 0; i < numEdges; ++i) {
Edge const & E = edges[i];
if (E.interior) {
numInfSharpEdges += E.infSharp;
numSemiSharpEdges += E.semiSharp;
} else if (E.boundary) {
hasBoundaryEdges = true;
hasBoundaryEdgesNotSharp |= !E.infSharp;
} else {
++ numNonManifoldEdges;
hasDegenerateEdges |= E.degenerate;
hasDuplicateEdges |= E.duplicate;
}
// Singular edges include all that are effectively inf-sharp:
numSingularEdges += E.nonManifold || E.boundary || E.infSharp;
}
//
// Next determine whether manifold or not. Some obvious tests quickly
// indicate if the corner is non-manifold, but ultimately it will be
// necessary to traverse the faces to confirm that they form a single
// connected set (e.g. two cones sharing their apex vertex may appear
// manifold to this point but as two connected sets are non-manifold).
//
bool isNonManifold = false;
bool isNonManifoldCrease = false;
if (numNonManifoldEdges) {
isNonManifold = true;
if (!hasDegenerateEdges && !hasDuplicateEdges && !hasBoundaryEdges) {
// Special crease case that avoids sharpening: two interior
// non-manifold edges radiating more than two sets of faces:
isNonManifoldCrease = (numNonManifoldEdges == 2) &&
(GetNumFaces() > numEdges);
}
} else {
// Mismatch between number of incident faces and edges:
isNonManifold = ((numEdges - GetNumFaces()) != (int)hasBoundaryEdges);
if (!isNonManifold) {
// If all faces are not connected, the set is non-manifold:
Subset subset;
int numFacesInSubset = findConnectedSubsetExtent(&subset);
if (numFacesInSubset < GetNumFaces()) {
isNonManifold = true;
}
}
}
//
// Assign tags and other members related to the inventory of edges
// (boundary status is relevant if non-manifold as it can affect
// the presence of the limit surface):
//
_tag._nonManifoldVerts = isNonManifold;
_tag._boundaryVerts = hasBoundaryEdges;
_tag._boundaryNonSharp = hasBoundaryEdgesNotSharp;
_tag._infSharpEdges = (numInfSharpEdges > 0);
_tag._semiSharpEdges = (numSemiSharpEdges > 0);
_tag._infSharpDarts = (numInfSharpEdges == 1) && !hasBoundaryEdges;
// Conditions effectively making the vertex sharp, include the usual
// excess of inf-sharp edges plus some non-manifold cases:
if ((numSingularEdges > 2) || (isNonManifold && !isNonManifoldCrease)) {
_isImpInfSharp = true;
} else if ((numSingularEdges + numSemiSharpEdges) > 2) {
_isImpSemiSharp = true;
}
// Mark the vertex inf-sharp if implicitly inf-sharp:
if (!_isExpInfSharp && _isImpInfSharp) {
_tag._infSharpVerts = true;
_tag._semiSharpVerts = false;
}
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

395
opensubdiv/bfr/faceVertex.h Normal file
View File

@ -0,0 +1,395 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_FACE_VERTEX_H
#define OPENSUBDIV3_BFR_FACE_VERTEX_H
#include "../version.h"
#include "../bfr/vertexTag.h"
#include "../bfr/vertexDescriptor.h"
#include "../bfr/faceVertexSubset.h"
#include "../bfr/surfaceData.h"
#include "../sdc/crease.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// The FaceVertex class is the primary internal class for gathering all
// topological information around the corner of a face. As such, it
// wraps an instance of the public VertexDescriptor class populated by the
// Factory subclasses. It extends an instance of VertexDescriptor with
// additional topological information and methods to make it more widely
// available and useful to internal classes.
//
// One fundamental extension of FaceVertex is that it includes the
// location of the face in the ring of incident faces around the vertex.
// VertexDescriptor alone simply specifies the neighborhood of the
// vertex, but the FaceVertex provides context relative to the face for
// which all of this information is being gathered.
//
// Several instances of FaceVertex (one for each corner of a face)
// are necessary to fully define the limit surface for a face, but in
// many cases, only a subset of the FaceVertex's incident faces will
// actually contribute to the surface. A companion class for defining
// that subset is defined elsewhere.
//
class FaceVertex {
public:
typedef internal::SurfaceData::Index Index;
public:
FaceVertex() { }
~FaceVertex() { }
// Methods supporting construction/initialization (subclass required
// to populate VertexDescriptor between Initialize and Finalize):
void Initialize(int faceSize, int regFaceSize);
void Finalize(int faceInVertex);
VertexDescriptor & GetVertexDescriptor() { return _vDesc; }
void ConnectUnOrderedFaces(Index const faceVertexIndices[]);
public:
// Methods to initialize and find subsets:
typedef FaceVertexSubset Subset;
int GetVertexSubset(Subset * subset) const;
int FindFaceVaryingSubset(Subset * fvarSubset,
Index const fvarIndices[],
Subset const & vtxSubset) const;
// Methods to control sharpness of the corner in a subset:
void SharpenSubset(Subset * subset) const;
void SharpenSubset(Subset * subset, float sharpness) const;
void UnSharpenSubset(Subset * subset) const;
public:
//
// Public methods to query simple properties:
//
VertexTag GetTag() const { return _tag; }
int GetFace() const { return _faceInRing; }
int GetNumFaces() const { return _vDesc._numFaces; }
int GetNumFaceVertices() const { return _numFaceVerts; }
bool HasCommonFaceSize() const { return (_commonFaceSize > 0); }
int GetCommonFaceSize() const { return _commonFaceSize; }
public:
//
// Public methods to inspect incident faces and connected neighbors:
//
int GetFaceSize(int face) const;
// Get neighbors of a specific face (return -1 if unconnected):
int GetFaceNext( int face) const;
int GetFacePrevious(int face) const;
// Find faces relative to this face (require a known safe step size):
int GetFaceAfter (int stepForwardFromCornerFace) const;
int GetFaceBefore(int stepBackwardFromCornerFace) const;
// Get first and last faces of a subset:
int GetFaceFirst(Subset const & subset) const;
int GetFaceLast( Subset const & subset) const;
public:
//
// Public methods to access indices assigned to incident faces:
//
int GetFaceIndexOffset(int face) const;
Index GetFaceIndexAtCorner(Index const indices[]) const;
Index GetFaceIndexAtCorner(int face, Index const indices[]) const;
Index GetFaceIndexTrailing(int face, Index const indices[]) const;
Index GetFaceIndexLeading( int face, Index const indices[]) const;
bool FaceIndicesMatchAtCorner( int f1, int f2, Index const indices[])const;
bool FaceIndicesMatchAtEdgeEnd( int f1, int f2, Index const indices[])const;
bool FaceIndicesMatchAcrossEdge(int f1, int f2, Index const indices[])const;
public:
//
// Public methods for sharpness of the vertex or its incident edges:
//
float GetVertexSharpness() const;
float GetFaceEdgeSharpness(int faceEdge) const;
float GetFaceEdgeSharpness(int face, bool trailingEdge) const;
bool IsFaceEdgeSharp( int face, bool trailingEdge) const;
bool IsFaceEdgeInfSharp( int face, bool trailingEdge) const;
bool IsFaceEdgeSemiSharp(int face, bool trailingEdge) const;
bool HasImplicitVertexSharpness() const;
float GetImplicitVertexSharpness() const;
private:
// Internal convenience methods:
bool isOrdered() const { return _tag.IsOrdered(); }
bool isUnOrdered() const { return _tag.IsUnOrdered(); }
bool isBoundary() const { return _tag.IsBoundary(); }
bool isInterior() const { return _tag.IsInterior(); }
bool isManifold() const { return _tag.IsManifold(); }
int getConnectedFaceNext(int face) const;
int getConnectedFacePrev(int face) const;
private:
// Internal methods for assembling and managing subsets:
int initCompleteSubset(Subset * subset) const;
int findConnectedSubsetExtent(Subset * subset) const;
int findFVarSubsetExtent(Subset const & vtxSubset,
Subset * fvarSubset,
Index const fvarIndices[]) const;
void adjustSubsetTags(Subset * subset,
Subset const * superset = 0) const;
bool subsetHasInfSharpEdges( Subset const & subset) const;
bool subsetHasSemiSharpEdges(Subset const & subset) const;
bool subsetHasIrregularFaces(Subset const & subset) const;
private:
// Internal methods to connect a set of unordered faces (given their
// associated face-vertex indices) and assess the resulting topology:
struct Edge;
int createUnOrderedEdges(Edge edges[],
short faceEdgeIndices[],
Index const faceVertIndices[]) const;
void markDuplicateEdges(Edge edges[],
short const faceEdgeIndices[],
Index const faceVertIndices[]) const;
void assignUnOrderedFaceNeighbors(Edge const edges[],
short const faceEdgeIndices[]);
void finalizeUnOrderedTags(Edge const edges[], int numEdges);
// Ordered counterpart to the above method for finalizing tags
void finalizeOrderedTags();
private:
typedef Vtr::internal::StackBuffer<short,16,true> ShortBuffer;
// Private members:
VertexDescriptor _vDesc;
VertexTag _tag;
short _faceInRing;
short _commonFaceSize;
unsigned char _regFaceSize;
unsigned char _isExpInfSharp : 1;
unsigned char _isExpSemiSharp : 1;
unsigned char _isImpInfSharp : 1;
unsigned char _isImpSemiSharp : 1;
int _numFaceVerts;
ShortBuffer _faceEdgeNeighbors;
};
//
// Inline methods for inspecting/traversing incident faces of the vertex:
//
inline int
FaceVertex::GetFaceSize(int face) const {
return _commonFaceSize ? _commonFaceSize :
(_vDesc._faceSizeOffsets[face+1] - _vDesc._faceSizeOffsets[face]);
}
inline int
FaceVertex::getConnectedFaceNext(int face) const {
return _faceEdgeNeighbors[2*face + 1];
}
inline int
FaceVertex::getConnectedFacePrev(int face) const {
return _faceEdgeNeighbors[2*face];
}
inline int
FaceVertex::GetFaceNext(int face) const {
if (isUnOrdered()) {
return getConnectedFaceNext(face);
} else if (face < (_vDesc._numFaces - 1)) {
return face + 1;
} else {
return isBoundary() ? -1 : 0;
}
}
inline int
FaceVertex::GetFacePrevious(int face) const {
if (isUnOrdered()) {
return getConnectedFacePrev(face);
} else if (face) {
return face - 1;
} else {
return isBoundary() ? -1 : (_vDesc._numFaces - 1);
}
}
inline int
FaceVertex::GetFaceAfter(int step) const {
assert(step >= 0);
if (isOrdered()) {
return (_faceInRing + step) % _vDesc._numFaces;
} else if (step == 1) {
return getConnectedFaceNext(_faceInRing);
} else if (step == 2) {
return getConnectedFaceNext(getConnectedFaceNext(_faceInRing));
} else {
int face = _faceInRing;
for ( ; step > 0; --step) {
face = getConnectedFaceNext(face);
}
return face;
}
}
inline int
FaceVertex::GetFaceBefore(int step) const {
assert(step >= 0);
if (isOrdered()) {
return (_faceInRing - step + _vDesc._numFaces) % _vDesc._numFaces;
} else if (step == 1) {
return getConnectedFacePrev(_faceInRing);
} else if (step == 2) {
return getConnectedFacePrev(getConnectedFacePrev(_faceInRing));
} else {
int face = _faceInRing;
for ( ; step > 0; --step) {
face = getConnectedFacePrev(face);
}
return face;
}
}
inline int
FaceVertex::GetFaceFirst(Subset const & subset) const {
return GetFaceBefore(subset._numFacesBefore);
}
inline int
FaceVertex::GetFaceLast(Subset const & subset) const {
return GetFaceAfter(subset._numFacesAfter);
}
//
// Inline methods for accessing indices associated with incident faces:
//
inline int
FaceVertex::GetFaceIndexOffset(int face) const {
return _commonFaceSize ? (face * _commonFaceSize) :
_vDesc._faceSizeOffsets[face];
}
inline FaceVertex::Index
FaceVertex::GetFaceIndexAtCorner(Index const indices[]) const {
return indices[GetFaceIndexOffset(_faceInRing)];
}
inline FaceVertex::Index
FaceVertex::GetFaceIndexAtCorner(int face, Index const indices[]) const {
return indices[GetFaceIndexOffset(face)];
}
inline FaceVertex::Index
FaceVertex::GetFaceIndexLeading(int face, Index const indices[]) const {
return indices[GetFaceIndexOffset(face) + 1];
}
inline FaceVertex::Index
FaceVertex::GetFaceIndexTrailing(int face, Index const indices[]) const {
// It is safe to use "face+1" here for the last face:
return indices[GetFaceIndexOffset(face+1) - 1];
}
inline bool
FaceVertex::FaceIndicesMatchAtCorner(int facePrev, int faceNext,
Index const indices[]) const {
return GetFaceIndexAtCorner(facePrev, indices) ==
GetFaceIndexAtCorner(faceNext, indices);
}
inline bool
FaceVertex::FaceIndicesMatchAtEdgeEnd(int facePrev, int faceNext,
Index const indices[]) const {
return GetFaceIndexTrailing(facePrev, indices) ==
GetFaceIndexLeading(faceNext, indices);
}
inline bool
FaceVertex::FaceIndicesMatchAcrossEdge(int facePrev, int faceNext,
Index const indices[]) const {
return FaceIndicesMatchAtCorner (facePrev, faceNext, indices) &&
FaceIndicesMatchAtEdgeEnd(facePrev, faceNext, indices);
}
//
// Inline methods for accessing vertex and edge sharpness:
//
inline float
FaceVertex::GetVertexSharpness() const {
return _vDesc._vertSharpness;
}
inline float
FaceVertex::GetFaceEdgeSharpness(int faceEdge) const {
return _vDesc._faceEdgeSharpness[faceEdge];
}
inline float
FaceVertex::GetFaceEdgeSharpness(int face, bool trailing) const {
return _vDesc._faceEdgeSharpness[face*2 + trailing];
}
inline bool
FaceVertex::IsFaceEdgeSharp(int face, bool trailing) const {
return Sdc::Crease::IsSharp(_vDesc._faceEdgeSharpness[face*2+trailing]);
}
inline bool
FaceVertex::IsFaceEdgeInfSharp(int face, bool trailing) const {
return Sdc::Crease::IsInfinite(_vDesc._faceEdgeSharpness[face*2+trailing]);
}
inline bool
FaceVertex::IsFaceEdgeSemiSharp(int face, bool trailing) const {
return Sdc::Crease::IsSemiSharp(_vDesc._faceEdgeSharpness[face*2+trailing]);
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_FACE_VERTEX_H */

View File

@ -0,0 +1,98 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_FACE_VERTEX_SUBSET_H
#define OPENSUBDIV3_BFR_FACE_VERTEX_SUBSET_H
#include "../version.h"
#include "../bfr/vertexTag.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// FaceVertexSubset is a simple struct and companion of FaceVertex that
// identifies a subset of the topology around a corner. Such subsets are
// what ultimately define the limit surface around a face and so are used
// by higher level classes in conjunction with FaceVertex.
//
// WIP - this is simple enough to warrant a nested class in FaceVertex
// - it serves no purpose without a FaceVertex and the FaceVertex
// class has several methods to initialize/modify FaceVertexSubsets
//
struct FaceVertexSubset {
FaceVertexSubset() { }
void Initialize(VertexTag tag) {
_tag = tag;
_numFacesBefore = 0;
_numFacesAfter = 0;
_numFacesTotal = 1;
_localSharpness = 0.0f;
}
// Queries consistent with other classes:
VertexTag GetTag() const { return _tag; }
int GetNumFaces() const { return _numFacesTotal; }
// Simple get/set methods to avoid the tedious syntax of the tag:
bool IsBoundary() const { return _tag._boundaryVerts; }
bool IsSharp() const { return _tag._infSharpVerts; }
void SetBoundary(bool on) { _tag._boundaryVerts = on; }
void SetSharp(bool on) { _tag._infSharpVerts = on; }
// Methods comparing to a superset (not any arbitrary subset):
bool ExtentMatchesSuperset(FaceVertexSubset const & sup) const {
return (GetNumFaces() == sup.GetNumFaces()) &&
(IsBoundary() == sup.IsBoundary());
}
bool ShapeMatchesSuperset(FaceVertexSubset const & sup) const {
return ExtentMatchesSuperset(sup) &&
(IsSharp() == sup.IsSharp());
}
// Member tags containing boundary and sharp bits:
VertexTag _tag;
// Members defining the extent of the subset:
short _numFacesBefore;
short _numFacesAfter;
short _numFacesTotal;
// Member to override vertex sharpness (rarely used):
float _localSharpness;
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_FACE_VERTEX_SUBSET_H */

706
opensubdiv/bfr/hash.cpp Normal file
View File

@ -0,0 +1,706 @@
//
// Copyright 2021 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.
//
//
// Functions for hashing to 32- and 64-bit integers. These are the same
// functions used in USD (pxr/base/arch/hash.*) under similar conditions.
//
#include "hash.h"
//
// SpookyHash: a 128-bit noncryptographic hash function
// By Bob Jenkins, public domain
// Oct 31 2010: alpha, framework + SpookyHash::Mix appears right
// Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right
// Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas
// Feb 2 2012: production, same bits as beta
// Feb 5 2012: adjusted definitions of uint* to be more portable
// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough.
// August 5 2012: SpookyV2 (different results)
//
// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages.
// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit.
//
// This was developed for and tested on 64-bit x86-compatible processors.
// It assumes the processor is little-endian. There is a macro
// controlling whether unaligned reads are allowed (by default they are).
// This should be an equally good hash on big-endian machines, but it will
// compute different results on them than on little-endian machines.
//
// Google's CityHash has similar specs to SpookyHash, and CityHash is faster
// on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders
// of magnitude slower. CRCs are two or more times slower, but unlike
// SpookyHash, they have nice math for combining the CRCs of pieces to form
// the CRCs of wholes. There are also cryptographic hashes, but those are even
// slower than MD5.
//
#include <cstring>
#include <cstddef>
// Local typedefs to match the original code:
typedef uint64_t uint64;
typedef uint32_t uint32;
typedef uint16_t uint16;
typedef uint8_t uint8;
namespace {
class SpookyHash
{
public:
//
// SpookyHash: hash a single message in one call, produce 128-bit output
//
static void Hash128(
const void *message, // message to hash
size_t length, // length of message in bytes
uint64 *hash1, // in/out: in seed 1, out hash value 1
uint64 *hash2); // in/out: in seed 2, out hash value 2
//
// Hash64: hash a single message in one call, return 64-bit output
//
static uint64 Hash64(
const void *message, // message to hash
size_t length, // length of message in bytes
uint64 seed) // seed
{
uint64 hash1 = seed;
Hash128(message, length, &hash1, &seed);
return hash1;
}
//
// Hash32: hash a single message in one call, produce 32-bit output
//
static uint32 Hash32(
const void *message, // message to hash
size_t length, // length of message in bytes
uint32 seed) // seed
{
uint64 hash1 = seed, hash2 = seed;
Hash128(message, length, &hash1, &hash2);
return (uint32)hash1;
}
#ifdef OPENSUBDIV3_BFR_HASH_INCLUDE_UNUSED_FUNCTIONS
//
// Init: initialize the context of a SpookyHash
//
void Init(
uint64 seed1, // any 64-bit value will do, including 0
uint64 seed2); // different seeds produce independent hashes
//
// Update: add a piece of a message to a SpookyHash state
//
void Update(
const void *message, // message fragment
size_t length); // length of message fragment in bytes
//
// Final: compute the hash for the current SpookyHash state
//
// This does not modify the state; you can keep updating it afterward
//
// The result is the same as if SpookyHash() had been called with
// all the pieces concatenated into one message.
//
void Final(
uint64 *hash1, // out only: first 64 bits of hash value.
uint64 *hash2); // out only: second 64 bits of hash value.
#endif
//
// left rotate a 64-bit value by k bytes
//
static inline uint64 Rot64(uint64 x, int k)
{
return (x << k) | (x >> (64 - k));
}
//
// This is used if the input is 96 bytes long or longer.
//
// The internal state is fully overwritten every 96 bytes.
// Every input bit appears to cause at least 128 bits of entropy
// before 96 other bytes are combined, when run forward or backward
// For every input bit,
// Two inputs differing in just that input bit
// Where "differ" means xor or subtraction
// And the base value is random
// When run forward or backwards one Mix
// I tried 3 pairs of each; they all differed by at least 212 bits.
//
static inline void Mix(
const uint64 *data,
uint64 &s0, uint64 &s1, uint64 &s2, uint64 &s3,
uint64 &s4, uint64 &s5, uint64 &s6, uint64 &s7,
uint64 &s8, uint64 &s9, uint64 &s10,uint64 &s11)
{
s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = Rot64(s0,11); s11 += s1;
s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = Rot64(s1,32); s0 += s2;
s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = Rot64(s2,43); s1 += s3;
s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = Rot64(s3,31); s2 += s4;
s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = Rot64(s4,17); s3 += s5;
s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = Rot64(s5,28); s4 += s6;
s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = Rot64(s6,39); s5 += s7;
s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = Rot64(s7,57); s6 += s8;
s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = Rot64(s8,55); s7 += s9;
s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = Rot64(s9,54); s8 += s10;
s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = Rot64(s10,22); s9 += s11;
s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = Rot64(s11,46); s10 += s0;
}
//
// Mix all 12 inputs together so that h0, h1 are a hash of them all.
//
// For two inputs differing in just the input bits
// Where "differ" means xor or subtraction
// And the base value is random, or a counting value starting at that bit
// The final result will have each bit of h0, h1 flip
// For every input bit,
// with probability 50 +- .3%
// For every pair of input bits,
// with probability 50 +- 3%
//
// This does not rely on the last Mix() call having already mixed some.
// Two iterations was almost good enough for a 64-bit result, but a
// 128-bit result is reported, so End() does three iterations.
//
static inline void EndPartial(
uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3,
uint64 &h4, uint64 &h5, uint64 &h6, uint64 &h7,
uint64 &h8, uint64 &h9, uint64 &h10,uint64 &h11)
{
h11+= h1; h2 ^= h11; h1 = Rot64(h1,44);
h0 += h2; h3 ^= h0; h2 = Rot64(h2,15);
h1 += h3; h4 ^= h1; h3 = Rot64(h3,34);
h2 += h4; h5 ^= h2; h4 = Rot64(h4,21);
h3 += h5; h6 ^= h3; h5 = Rot64(h5,38);
h4 += h6; h7 ^= h4; h6 = Rot64(h6,33);
h5 += h7; h8 ^= h5; h7 = Rot64(h7,10);
h6 += h8; h9 ^= h6; h8 = Rot64(h8,13);
h7 += h9; h10^= h7; h9 = Rot64(h9,38);
h8 += h10; h11^= h8; h10= Rot64(h10,53);
h9 += h11; h0 ^= h9; h11= Rot64(h11,42);
h10+= h0; h1 ^= h10; h0 = Rot64(h0,54);
}
static inline void End(
const uint64 *data,
uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3,
uint64 &h4, uint64 &h5, uint64 &h6, uint64 &h7,
uint64 &h8, uint64 &h9, uint64 &h10,uint64 &h11)
{
h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3];
h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7];
h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11];
EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
}
//
// The goal is for each bit of the input to expand into 128 bits of
// apparent entropy before it is fully overwritten.
// n trials both set and cleared at least m bits of h0 h1 h2 h3
// n: 2 m: 29
// n: 3 m: 46
// n: 4 m: 57
// n: 5 m: 107
// n: 6 m: 146
// n: 7 m: 152
// when run forwards or backwards
// for all 1-bit and 2-bit diffs
// with diffs defined by either xor or subtraction
// with a base of all zeros plus a counter, or plus another bit, or random
//
static inline void ShortMix(uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3)
{
h2 = Rot64(h2,50); h2 += h3; h0 ^= h2;
h3 = Rot64(h3,52); h3 += h0; h1 ^= h3;
h0 = Rot64(h0,30); h0 += h1; h2 ^= h0;
h1 = Rot64(h1,41); h1 += h2; h3 ^= h1;
h2 = Rot64(h2,54); h2 += h3; h0 ^= h2;
h3 = Rot64(h3,48); h3 += h0; h1 ^= h3;
h0 = Rot64(h0,38); h0 += h1; h2 ^= h0;
h1 = Rot64(h1,37); h1 += h2; h3 ^= h1;
h2 = Rot64(h2,62); h2 += h3; h0 ^= h2;
h3 = Rot64(h3,34); h3 += h0; h1 ^= h3;
h0 = Rot64(h0,5); h0 += h1; h2 ^= h0;
h1 = Rot64(h1,36); h1 += h2; h3 ^= h1;
}
//
// Mix all 4 inputs together so that h0, h1 are a hash of them all.
//
// For two inputs differing in just the input bits
// Where "differ" means xor or subtraction
// And the base value is random, or a counting value starting at that bit
// The final result will have each bit of h0, h1 flip
// For every input bit,
// with probability 50 +- .3% (it is probably better than that)
// For every pair of input bits,
// with probability 50 +- .75% (the worst case is approximately that)
//
static inline void ShortEnd(uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3)
{
h3 ^= h2; h2 = Rot64(h2,15); h3 += h2;
h0 ^= h3; h3 = Rot64(h3,52); h0 += h3;
h1 ^= h0; h0 = Rot64(h0,26); h1 += h0;
h2 ^= h1; h1 = Rot64(h1,51); h2 += h1;
h3 ^= h2; h2 = Rot64(h2,28); h3 += h2;
h0 ^= h3; h3 = Rot64(h3,9); h0 += h3;
h1 ^= h0; h0 = Rot64(h0,47); h1 += h0;
h2 ^= h1; h1 = Rot64(h1,54); h2 += h1;
h3 ^= h2; h2 = Rot64(h2,32); h3 += h2;
h0 ^= h3; h3 = Rot64(h3,25); h0 += h3;
h1 ^= h0; h0 = Rot64(h0,63); h1 += h0;
}
private:
//
// Short is used for messages under 192 bytes in length
// Short has a low startup cost, the normal mode is good for long
// keys, the cost crossover is at about 192 bytes. The two modes were
// held to the same quality bar.
//
static void Short(
const void *message, // message (array of bytes, not necessarily
// aligned)
size_t length, // length of message (in bytes)
uint64 *hash1, // in/out: in the seed, out the hash value
uint64 *hash2); // in/out: in the seed, out the hash value
// number of uint64's in internal state
static const size_t sc_numVars = 12;
// size of the internal state
static const size_t sc_blockSize = sc_numVars*8;
// size of buffer of unhashed data, in bytes
static const size_t sc_bufSize = 2*sc_blockSize;
//
// sc_const: a constant which:
// * is not zero
// * is odd
// * is a not-very-regular mix of 1's and 0's
// * does not need any other special mathematical properties
//
static const uint64 sc_const = 0xdeadbeefdeadbeefLL;
uint64 m_data[2*sc_numVars]; // unhashed data, for partial messages
uint64 m_state[sc_numVars]; // internal state of the hash
size_t m_length; // total length of the input so far
uint8 m_remainder; // length of unhashed data stashed in m_data
};
#define ALLOW_UNALIGNED_READS 1
//
// short hash ... it could be used on any message,
// but it's used by Spooky just for short messages.
//
void SpookyHash::Short(
const void *message,
size_t length,
uint64 *hash1,
uint64 *hash2)
{
uint64 buf[2*sc_numVars];
union
{
const uint8 *p8;
uint32 *p32;
uint64 *p64;
size_t i;
} u;
u.p8 = (const uint8 *)message;
if (!ALLOW_UNALIGNED_READS && (u.i & 0x7))
{
memcpy(buf, message, length);
u.p64 = buf;
}
size_t remainder = length%32;
uint64 a=*hash1;
uint64 b=*hash2;
uint64 c=sc_const;
uint64 d=sc_const;
if (length > 15)
{
const uint64 *end = u.p64 + (length/32)*4;
// handle all complete sets of 32 bytes
for (; u.p64 < end; u.p64 += 4)
{
c += u.p64[0];
d += u.p64[1];
ShortMix(a,b,c,d);
a += u.p64[2];
b += u.p64[3];
}
//Handle the case of 16+ remaining bytes.
if (remainder >= 16)
{
c += u.p64[0];
d += u.p64[1];
ShortMix(a,b,c,d);
u.p64 += 2;
remainder -= 16;
}
}
// Handle the last 0..15 bytes, and its length
d += ((uint64)length) << 56;
switch (remainder)
{
case 15:
d += ((uint64)u.p8[14]) << 48;
// FALLTHRU
case 14:
d += ((uint64)u.p8[13]) << 40;
// FALLTHRU
case 13:
d += ((uint64)u.p8[12]) << 32;
// FALLTHRU
case 12:
d += u.p32[2];
c += u.p64[0];
break;
case 11:
d += ((uint64)u.p8[10]) << 16;
// FALLTHRU
case 10:
d += ((uint64)u.p8[9]) << 8;
// FALLTHRU
case 9:
d += (uint64)u.p8[8];
// FALLTHRU
case 8:
c += u.p64[0];
break;
case 7:
c += ((uint64)u.p8[6]) << 48;
// FALLTHRU
case 6:
c += ((uint64)u.p8[5]) << 40;
// FALLTHRU
case 5:
c += ((uint64)u.p8[4]) << 32;
// FALLTHRU
case 4:
c += u.p32[0];
break;
case 3:
c += ((uint64)u.p8[2]) << 16;
// FALLTHRU
case 2:
c += ((uint64)u.p8[1]) << 8;
// FALLTHRU
case 1:
c += (uint64)u.p8[0];
break;
case 0:
c += sc_const;
d += sc_const;
}
ShortEnd(a,b,c,d);
*hash1 = a;
*hash2 = b;
}
// do the whole hash in one call
void SpookyHash::Hash128(
const void *message,
size_t length,
uint64 *hash1,
uint64 *hash2)
{
if (length < sc_bufSize)
{
Short(message, length, hash1, hash2);
return;
}
uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
uint64 buf[sc_numVars];
uint64 *end;
union
{
const uint8 *p8;
uint64 *p64;
size_t i;
} u;
size_t remainder;
h0=h3=h6=h9 = *hash1;
h1=h4=h7=h10 = *hash2;
h2=h5=h8=h11 = sc_const;
u.p8 = (const uint8 *)message;
end = u.p64 + (length/sc_blockSize)*sc_numVars;
// handle all whole sc_blockSize blocks of bytes
if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0))
{
while (u.p64 < end)
{
Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
u.p64 += sc_numVars;
}
}
else
{
while (u.p64 < end)
{
memcpy(buf, u.p64, sc_blockSize);
Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
u.p64 += sc_numVars;
}
}
// handle the last partial block of sc_blockSize bytes
remainder = (length - ((const uint8 *)end-(const uint8 *)message));
memcpy(buf, end, remainder);
memset(((uint8 *)buf)+remainder, 0, sc_blockSize-remainder);
((uint8 *)buf)[sc_blockSize-1] = static_cast<uint8>(remainder);
// do some final mixing
End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
*hash1 = h0;
*hash2 = h1;
}
#ifdef OPENSUBDIV3_BFR_HASH_INCLUDE_UNUSED_FUNCTIONS
// init spooky state
void SpookyHash::Init(uint64 seed1, uint64 seed2)
{
m_length = 0;
m_remainder = 0;
m_state[0] = seed1;
m_state[1] = seed2;
}
// add a message fragment to the state
void SpookyHash::Update(const void *message, size_t length)
{
uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
size_t newLength = length + m_remainder;
uint8 remainder;
union
{
const uint8 *p8;
uint64 *p64;
size_t i;
} u;
const uint64 *end;
// Is this message fragment too short? If it is, stuff it away.
if (newLength < sc_bufSize)
{
memcpy(&((uint8 *)m_data)[m_remainder], message, length);
m_length = length + m_length;
m_remainder = (uint8)newLength;
return;
}
// init the variables
if (m_length < sc_bufSize)
{
h0=h3=h6=h9 = m_state[0];
h1=h4=h7=h10 = m_state[1];
h2=h5=h8=h11 = sc_const;
}
else
{
h0 = m_state[0];
h1 = m_state[1];
h2 = m_state[2];
h3 = m_state[3];
h4 = m_state[4];
h5 = m_state[5];
h6 = m_state[6];
h7 = m_state[7];
h8 = m_state[8];
h9 = m_state[9];
h10 = m_state[10];
h11 = m_state[11];
}
m_length = length + m_length;
// if we've got anything stuffed away, use it now
if (m_remainder)
{
uint8 prefix = sc_bufSize-m_remainder;
memcpy(&(((uint8 *)m_data)[m_remainder]), message, prefix);
u.p64 = m_data;
Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
u.p8 = ((const uint8 *)message) + prefix;
length -= prefix;
}
else
{
u.p8 = (const uint8 *)message;
}
// handle all whole blocks of sc_blockSize bytes
end = u.p64 + (length/sc_blockSize)*sc_numVars;
remainder = (uint8)(length-((const uint8 *)end-u.p8));
if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0)
{
while (u.p64 < end)
{
Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
u.p64 += sc_numVars;
}
}
else
{
while (u.p64 < end)
{
memcpy(m_data, u.p8, sc_blockSize);
Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
u.p64 += sc_numVars;
}
}
// stuff away the last few bytes
m_remainder = remainder;
memcpy(m_data, end, remainder);
// stuff away the variables
m_state[0] = h0;
m_state[1] = h1;
m_state[2] = h2;
m_state[3] = h3;
m_state[4] = h4;
m_state[5] = h5;
m_state[6] = h6;
m_state[7] = h7;
m_state[8] = h8;
m_state[9] = h9;
m_state[10] = h10;
m_state[11] = h11;
}
// report the hash for the concatenation of all message fragments so far
void SpookyHash::Final(uint64 *hash1, uint64 *hash2)
{
// init the variables
if (m_length < sc_bufSize)
{
*hash1 = m_state[0];
*hash2 = m_state[1];
Short( m_data, m_length, hash1, hash2);
return;
}
const uint64 *data = (const uint64 *)m_data;
uint8 remainder = m_remainder;
uint64 h0 = m_state[0];
uint64 h1 = m_state[1];
uint64 h2 = m_state[2];
uint64 h3 = m_state[3];
uint64 h4 = m_state[4];
uint64 h5 = m_state[5];
uint64 h6 = m_state[6];
uint64 h7 = m_state[7];
uint64 h8 = m_state[8];
uint64 h9 = m_state[9];
uint64 h10 = m_state[10];
uint64 h11 = m_state[11];
if (remainder >= sc_blockSize)
{
// m_data can contain two blocks; handle any whole first block
Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
data += sc_numVars;
remainder -= sc_blockSize;
}
// mix in the last partial block, and the length mod sc_blockSize
memset(&((uint8 *)data)[remainder], 0, (sc_blockSize-remainder));
((uint8 *)data)[sc_blockSize-1] = remainder;
// do some final mixing
End(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
*hash1 = h0;
*hash2 = h1;
}
#endif
} // anon
//
// Public functions exposed for OpenSubdiv:
//
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
namespace internal {
uint32_t
Hash32(const void *data, size_t len)
{
return SpookyHash::Hash32(data, len, /*seed=*/0);
}
uint32_t
Hash32(const void *data, size_t len, uint32_t seed)
{
return SpookyHash::Hash32(data, len, seed);
}
uint64_t
Hash64(const void *data, size_t len)
{
return SpookyHash::Hash64(data, len, /*seed=*/0);
}
uint64_t
Hash64(const void *data, size_t len, uint64_t seed)
{
return SpookyHash::Hash64(data, len, seed);
}
} // end namespace internal
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

63
opensubdiv/bfr/hash.h Normal file
View File

@ -0,0 +1,63 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_HASH_H
#define OPENSUBDIV3_BFR_HASH_H
#include "../version.h"
#include <cstdint>
#include <cstddef>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
namespace internal {
//
// Internal functions to hash data to unsigned ints for caching. Both
// 32- and 64-bit versions are provided here, but only 64-bit versions
// are currently intended for internal use (so consider removing 32).
//
// To compute a hash value for data that is not contiguous in memory,
// iterate over all the contiguous blocks of memory and accumulate the
// hash value by passing it on as a seed. Note that this is *not*
// equivalent to hashing the contiguous pieces as a whole. Support
// for that may be added in future.
//
uint32_t Hash32(const void *data, size_t len);
uint32_t Hash32(const void *data, size_t len, uint32_t seed);
uint64_t Hash64(const void *data, size_t len);
uint64_t Hash64(const void *data, size_t len, uint64_t seed);
} // end namespace internal
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_HASH_H */

View File

@ -0,0 +1,916 @@
//
// Copyright 2021 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.
//
#include "../bfr/irregularPatchBuilder.h"
#include "../bfr/patchTreeBuilder.h"
#include "../bfr/patchTree.h"
#include "../far/topologyDescriptor.h"
#include "../far/topologyRefiner.h"
#include <cstring>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Trivial constructor -- initializes members related to the control hull:
//
IrregularPatchBuilder::IrregularPatchBuilder(
FaceSurface const & surfaceDescription, Options const & options) :
_surface(surfaceDescription),
_options(options) {
initializeControlHullInventory();
}
//
// Inline private methods for accessing indices associated with the
// face-vertex topology and indices stored in map or vector members:
//
inline IrregularPatchBuilder::Index const *
IrregularPatchBuilder::getSurfaceIndices() const {
return _surface.GetIndices();
}
inline IrregularPatchBuilder::Index const *
IrregularPatchBuilder::getCornerIndices(int corner) const {
return getSurfaceIndices() +
_cornerHullInfo[corner].surfaceIndicesOffset;
}
inline IrregularPatchBuilder::Index const *
IrregularPatchBuilder::getBaseFaceIndices() const {
FaceVertex const & corner0 = _surface.GetCornerTopology(0);
return getSurfaceIndices() +
corner0.GetFaceIndexOffset(corner0.GetFace());
}
inline IrregularPatchBuilder::Index const *
IrregularPatchBuilder::getCornerFaceIndices(int corner, int face) const {
return getCornerIndices(corner) +
_surface.GetCornerTopology(corner).GetFaceIndexOffset(face);
}
inline int
IrregularPatchBuilder::getLocalControlVertex(Index meshVertIndex) const {
return _controlVertMap.find(meshVertIndex)->second;
}
inline IrregularPatchBuilder::Index
IrregularPatchBuilder::getMeshControlVertex(int localVertIndex) const {
return _controlVerts[localVertIndex];
}
//
// The IrregularPatchBuilder assembles a control hull for the base face
// from the topology information given for each corner of the face. It
// first initializes the number of control vertices and faces required,
// along with the contributions of each from the corners of the face.
//
// Once initialized, iteration over the corners of the base face is
// expected to follow a similar pattern when inspecting the incident
// faces of a corner:
//
// - deal with faces after the base face (skipping the first)
// - deal with boundary vertex between faces after and before
// - deal with faces before the base face (all)
//
// Tags and other inventory assigned here help to expedite and simplify
// those iterations.
//
void
IrregularPatchBuilder::initializeControlHullInventory() {
//
// Iterate through the corners to identify the vertices, faces and
// face-vertices that contribute to the collective control hull --
// keeping track of a few situations that cause complications:
//
int numVal2IntCorners = 0;
int numVal3IntAdjTris = 0;
int numSrcFaceIndices = 0;
int faceSize = _surface.GetFaceSize();
_cornerHullInfo.SetSize(faceSize);
_numControlFaces = 1;
_numControlVerts = faceSize;
_numControlFaceVerts = faceSize;
for (int corner = 0; corner < faceSize; ++corner) {
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
//
// Inspect faces after the corner face first -- dealing with a few
// special cases for interior vertices of low valence -- followed
// by those faces before the corner face:
//
CornerHull & cHull = _cornerHullInfo[corner];
cHull.Clear();
int numCornerFaceVerts = 0;
if (cSub._numFacesAfter) {
int nextFace = cTop.GetFaceNext(cTop.GetFace());
if (cSub.IsBoundary()) {
// Boundary -- no special cases:
for (int i = 1; i < cSub._numFacesAfter; ++i) {
nextFace = cTop.GetFaceNext(nextFace);
int S = cTop.GetFaceSize(nextFace);
cHull.numControlVerts += S - 2;
numCornerFaceVerts += S;
}
cHull.numControlFaces = cSub._numFacesAfter - 1;
// Include unshared vertex of trailing edge
cHull.numControlVerts ++;
} else if ((cSub._numFacesTotal == 3) &&
(cTop.GetFaceSize(cTop.GetFaceAfter(2)) == 3)) {
// Interior, valence-3, adjacent triangle -- special case:
if (++numVal3IntAdjTris == faceSize) {
cHull.singleSharedVert = true;
cHull.numControlVerts = 1;
}
cHull.numControlFaces = 1;
numCornerFaceVerts = 3;
} else if (cSub._numFacesTotal > 2) {
// Interior -- general case:
for (int i = 2; i < cSub._numFacesTotal; ++i) {
nextFace = cTop.GetFaceNext(nextFace);
int S = cTop.GetFaceSize(nextFace);
cHull.numControlVerts += S - 2;
numCornerFaceVerts += S;
}
cHull.numControlFaces = cSub._numFacesTotal - 2;
// Exclude vertex shared with/contributed by next corner
cHull.numControlVerts --;
} else {
// Interior, valence-2 -- special case:
if (++numVal2IntCorners == faceSize) {
cHull.singleSharedFace = true;
cHull.numControlFaces = 1;
numCornerFaceVerts = faceSize;
}
}
}
if (cSub._numFacesBefore) {
assert(cSub.IsBoundary());
int nextFace = cTop.GetFaceFirst(cSub);
for (int i = 0; i < cSub._numFacesBefore; ++i) {
int S = cTop.GetFaceSize(nextFace);
nextFace = cTop.GetFaceNext(nextFace);
cHull.numControlVerts += S - 2;
numCornerFaceVerts += S;
}
cHull.numControlFaces += cSub._numFacesBefore;
// Exclude vertex shared with/contributed by next corner
cHull.numControlVerts --;
}
// Assign the contributions for this corner:
cHull.nextControlVert = _numControlVerts;
cHull.surfaceIndicesOffset = numSrcFaceIndices;
_numControlFaces += cHull.numControlFaces;
_numControlVerts += cHull.numControlVerts;
_numControlFaceVerts += numCornerFaceVerts;
numSrcFaceIndices += cTop.GetNumFaceVertices();
}
//
// Use/build a map for the control vertex indices when incident
// faces overlap to an extent that makes traversal ill-defined:
//
_controlFacesOverlap = (numVal2IntCorners > 0);
_useControlVertMap = _controlFacesOverlap;
if (_useControlVertMap) {
initializeControlVertexMap();
}
}
void
IrregularPatchBuilder::addMeshControlVertex(Index meshVertIndex) {
if (_controlVertMap.find(meshVertIndex) == _controlVertMap.end()) {
int newLocalVertIndex = (int) _controlVerts.size();
_controlVertMap[meshVertIndex] = newLocalVertIndex;
_controlVerts.push_back(meshVertIndex);
}
}
void
IrregularPatchBuilder::addMeshControlVertices(Index const fVerts[], int fSize) {
// Ignore the first index of the face, which corresponds to a corner
for (int i = 1; i < fSize; ++i) {
addMeshControlVertex(fVerts[i]);
}
}
void
IrregularPatchBuilder::initializeControlVertexMap() {
//
// Add CV indices from the base face first -- be careful to ensure
// that a vector entry is made for each base face vertex in cases
// when repeated indices may occur:
Index const * baseVerts = getBaseFaceIndices();
int faceSize = _surface.GetFaceSize();
for (int i = 0; i < faceSize; ++i) {
addMeshControlVertex(baseVerts[i]);
if ((int)_controlVerts.size() == i) {
_controlVerts.push_back(baseVerts[i]);
}
}
//
// For each corner, add face-vertices to the map only for those
// incident faces that contribute to the control hull:
//
for (int corner = 0; corner < faceSize; ++corner) {
CornerHull & cHull = _cornerHullInfo[corner];
if (cHull.numControlFaces == 0) continue;
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
// Special case of a single shared back-to-back face first:
if (cHull.singleSharedFace) {
int nextFace = cTop.GetFaceAfter(1);
addMeshControlVertices(getCornerFaceIndices(corner, nextFace),
cTop.GetFaceSize(nextFace));
continue;
}
// Follow the common pattern: faces after, boundary, faces before
// (no need to deal with isolated boundary vertex in this case)
if (cSub._numFacesAfter > 1) {
int nextFace = cTop.GetFaceAfter(1);
for (int j = 1; j < cSub._numFacesAfter; ++j) {
nextFace = cTop.GetFaceNext(nextFace);
addMeshControlVertices(getCornerFaceIndices(corner, nextFace),
cTop.GetFaceSize(nextFace));
}
}
if (cSub._numFacesBefore) {
int nextFace = cTop.GetFaceFirst(cSub);
for (int i = 0; i < cSub._numFacesBefore; ++i) {
addMeshControlVertices(getCornerFaceIndices(corner, nextFace),
cTop.GetFaceSize(nextFace));
nextFace = cTop.GetFaceNext(nextFace);
}
}
}
_numControlVerts = (int) _controlVerts.size();
}
//
// Methods for gathering control vertices, faces, sharpness, etc. -- the
// method to gather control vertex indices is for external use, while the
// rest are internal:
//
int
IrregularPatchBuilder::GatherControlVertexIndices(Index cvIndices[]) const {
//
// If a map was built, simply copy the associated vector of indices:
//
if (_useControlVertMap) {
std::memcpy(cvIndices, &_controlVerts[0], _numControlVerts*sizeof(int));
return _numControlVerts;
}
//
// Assign CV indices from the base face first:
//
int faceSize = _surface.GetFaceSize();
int numIndices = faceSize;
std::memcpy(cvIndices, getBaseFaceIndices(), faceSize * sizeof(Index));
//
// Assign vertex indices identified as contributed by each corner:
//
for (int corner = 0; corner < faceSize; ++corner) {
CornerHull const & cHull = _cornerHullInfo[corner];
if (cHull.numControlVerts == 0) continue;
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
// Special case with all val-3 interior triangles:
if (cHull.singleSharedVert) {
assert(!cSub.IsBoundary() && (cSub._numFacesTotal == 3) &&
(cTop.GetFaceSize(cTop.GetFaceAfter(2)) == 3));
cvIndices[numIndices++] =
getCornerFaceIndices(corner, cTop.GetFaceAfter(2))[1];
continue;
}
//
// Follow the common pattern: faces after, boundary, faces before
//
if (cSub._numFacesAfter > 1) {
int nextFace = cTop.GetFaceAfter(1);
int N = cSub._numFacesAfter - 1;
for (int j = 0; j < N; ++j) {
nextFace = cTop.GetFaceNext(nextFace);
Index const * faceVerts = getCornerFaceIndices(corner,nextFace);
int S = cTop.GetFaceSize(nextFace);
int L = ((j < (N-1)) || cSub.IsBoundary()) ? 0 : 1;
int M = (S - 2) - L;
for (int k = 1; k <= M; ++k) {
cvIndices[numIndices++] = faceVerts[k];
}
}
}
if (cSub._numFacesAfter && cSub.IsBoundary()) {
// Include trailing edge for boundary before crossing the gap:
int nextFace = cTop.GetFaceAfter(cSub._numFacesAfter);
cvIndices[numIndices++] = cTop.GetFaceIndexTrailing(nextFace,
getCornerIndices(corner));
}
if (cSub._numFacesBefore) {
int nextFace = cTop.GetFaceFirst(cSub);
int N = cSub._numFacesBefore;
for (int j = 0; j < N; ++j) {
Index const * faceVerts = getCornerFaceIndices(corner,nextFace);
int S = cTop.GetFaceSize(nextFace);
int L = (j < (N-1)) ? 0 : 1;
int M = (S - 2) - L;
for (int k = 1; k <= M; ++k) {
cvIndices[numIndices++] = faceVerts[k];
}
nextFace = cTop.GetFaceNext(nextFace);
}
}
}
assert(numIndices == _numControlVerts);
return numIndices;
}
int
IrregularPatchBuilder::gatherControlFaces(int faceSizes[],
int faceVertices[]) const {
//
// Assign face-vertices for the first/base face:
//
int * faceVerts = faceVertices;
int faceSize = _surface.GetFaceSize();
for (int i = 0; i < faceSize; ++i) {
*faceVerts++ = i;
}
*faceSizes++ = faceSize;
//
// Assign face-vertex indices for faces "local to" each corner:
//
for (int corner = 0; corner < faceSize; ++corner) {
CornerHull const & cHull = _cornerHullInfo[corner];
if (cHull.numControlFaces == 0) continue;
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
// Special case of a single shared opposing face first:
if (cHull.singleSharedFace) {
assert(_useControlVertMap);
getControlFaceVertices(faceVerts, faceSize, corner,
getCornerFaceIndices(corner, cTop.GetFaceAfter(1)));
*faceSizes++ = faceSize;
faceVerts += faceSize;
continue;
}
//
// Follow the common pattern: faces after, boundary, faces before
//
int nextVert = cHull.nextControlVert;
if (cSub._numFacesAfter > 1) {
int nextFace = cTop.GetFaceAfter(2);
int N = cSub._numFacesAfter - 1;
for (int j = 0; j < N; ++j) {
int S = cTop.GetFaceSize(nextFace);
if (_useControlVertMap) {
getControlFaceVertices(faceVerts, S, corner,
getCornerFaceIndices(corner, nextFace));
} else if (cSub.IsBoundary()) {
getControlFaceVertices(faceVerts, S, corner, nextVert);
} else {
getControlFaceVertices(faceVerts, S, corner, nextVert,
(j == (N - 1)));
}
*faceSizes++ = S;
faceVerts += S;
nextVert += S - 2;
nextFace = cTop.GetFaceNext(nextFace);
}
}
if (cSub._numFacesAfter && cSub.IsBoundary()) {
nextVert ++;
}
if (cSub._numFacesBefore) {
int nextFace = cTop.GetFaceFirst(cSub);
int N = cSub._numFacesBefore;
for (int j = 0; j < N; ++j) {
int S = cTop.GetFaceSize(nextFace);
if (_useControlVertMap) {
getControlFaceVertices(faceVerts, S, corner,
getCornerFaceIndices(corner, nextFace));
} else {
getControlFaceVertices(faceVerts, S, corner, nextVert,
(j == (N - 1)));
}
*faceSizes++ = S;
faceVerts += S;
nextVert += S - 2;
nextFace = cTop.GetFaceNext(nextFace);
}
}
}
assert((faceVerts - faceVertices) == _numControlFaceVerts);
return _numControlFaceVerts;
}
int
IrregularPatchBuilder::gatherControlVertexSharpness(
int vertIndices[], float vertSharpness[]) const {
int nSharpVerts = 0;
for (int i = 0; i < _surface.GetFaceSize(); ++i) {
FaceVertexSubset const & cSub = _surface.GetCornerSubset(i);
if (cSub._tag.IsInfSharp()) {
vertSharpness[nSharpVerts] = Sdc::Crease::SHARPNESS_INFINITE;
vertIndices[nSharpVerts++] = i;
} else if (cSub._tag.IsSemiSharp()) {
vertSharpness[nSharpVerts] = (cSub._localSharpness > 0.0f) ?
cSub._localSharpness :
_surface.GetCornerTopology(i).GetVertexSharpness();
vertIndices[nSharpVerts++] = i;
}
}
return nSharpVerts;
}
int
IrregularPatchBuilder::gatherControlEdgeSharpness(
int edgeVertPairs[], float edgeSharpness[]) const {
//
// First test the forward edge of each corner of the face (avoid
// including redundant inf-sharp boundary edges):
//
int nSharpEdges = 0;
int faceSize = _surface.GetFaceSize();
for (int corner = 0; corner < faceSize; ++corner) {
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
if (!cSub._tag.HasSharpEdges()) continue;
if (!cSub.IsBoundary() || cSub._numFacesBefore) {
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
int cornerFace = cTop.GetFace();
float sharpness = cTop.GetFaceEdgeSharpness(cornerFace, 0);
if (Sdc::Crease::IsSharp(sharpness)) {
*edgeSharpness++ = sharpness;
*edgeVertPairs++ = corner;
*edgeVertPairs++ = (corner + 1) % faceSize;
nSharpEdges++;
}
}
}
//
// For each corner, test any interior edges connected to vertices
// on the perimeter:
//
for (int corner = 0; corner < faceSize; ++corner) {
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
if (!cSub._tag.HasSharpEdges()) continue;
CornerHull const & cHull = _cornerHullInfo[corner];
if (cHull.numControlFaces == 0) continue;
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
//
// Inspect interior edges around the subset -- testing sharpness
// of the trailing edge of the faces after/before the corner face.
//
// Follow the common pattern: faces after, boundary, faces before
//
// Track perimeter index to identify verts at end of sharp edges:
int maxVert = _numControlVerts;
int nextVert = cHull.nextControlVert;
Index const * cVerts = getCornerIndices(corner);
if (cSub._numFacesAfter > 1) {
int nextFace = cTop.GetFaceAfter(1);
for (int i = 1; i < cSub._numFacesAfter; ++i) {
float sharpness = cTop.GetFaceEdgeSharpness(nextFace, 1);
if (Sdc::Crease::IsSharp(sharpness)) {
int edgeVert = (nextVert < maxVert) ? nextVert : faceSize;
if (_useControlVertMap) {
edgeVert = getLocalControlVertex(
cTop.GetFaceIndexTrailing(nextFace, cVerts));
}
*edgeSharpness++ = sharpness;
*edgeVertPairs++ = corner;
*edgeVertPairs++ = edgeVert;
nSharpEdges++;
}
nextFace = cTop.GetFaceNext(nextFace);
nextVert += cTop.GetFaceSize(nextFace) - 2;
}
}
if (cSub._numFacesAfter && cSub.IsBoundary()) {
nextVert += cSub.IsBoundary();
}
if (cSub._numFacesBefore) {
int nextFace = cTop.GetFaceFirst(cSub);
for (int i = 1; i < cSub._numFacesBefore; ++i) {
nextVert += cTop.GetFaceSize(nextFace) - 2;
float sharpness = cTop.GetFaceEdgeSharpness(nextFace, 1);
if (Sdc::Crease::IsSharp(sharpness)) {
int edgeVert = (nextVert < maxVert) ? nextVert : faceSize;
if (_useControlVertMap) {
edgeVert = getLocalControlVertex(
cTop.GetFaceIndexTrailing(nextFace, cVerts));
}
*edgeSharpness++ = sharpness;
*edgeVertPairs++ = corner;
*edgeVertPairs++ = edgeVert;
nSharpEdges++;
}
nextFace = cTop.GetFaceNext(nextFace);
}
}
}
return nSharpEdges;
}
//
// Methods to gather the local face-vertices for a particular incident
// face of a corner. The first is the trivial case -- where all vertices
// other than the initial corner vertex lie on the perimeter and do not
// wrap around. The second deals handles more of the complications and
// is used in all cases where the trivial method cannot be applied.
//
void
IrregularPatchBuilder::getControlFaceVertices(int fVerts[], int numFVerts,
int corner, Index const srcVerts[]) const {
assert(_useControlVertMap);
*fVerts++ = corner;
for (int i = 1; i < numFVerts; ++i) {
*fVerts++ = getLocalControlVertex(srcVerts[i]);
}
}
void
IrregularPatchBuilder::getControlFaceVertices(int fVerts[], int numFVerts,
int corner, int nextPerimeterVert) const {
*fVerts++ = corner;
for (int i = 1; i < numFVerts; ++i) {
*fVerts++ = nextPerimeterVert + i - 1;
}
}
void
IrregularPatchBuilder::getControlFaceVertices(int fVerts[], int numFVerts,
int corner, int nextPerimeterVert, bool lastFace) const {
int S = numFVerts;
int N = _surface.GetFaceSize();
//
// The typical case: the corner vertex first, followed by vertices
// on the perimeter -- potentially wrapping around the perimeter to
// the first corner or the base face:
//
// - for the last corner only, the face-vert preceding the last
// will wrap around the perimeter
// - for all corners, the last face-vert of the last face wraps
// around the face to the next corner
//
*fVerts++ = corner;
for (int i = 1; i < S - 2; ++i) {
*fVerts++ = nextPerimeterVert + i - 1;
}
int nextToLastPerimOfFace = nextPerimeterVert + S - 3;
if (nextToLastPerimOfFace == _numControlVerts) {
nextToLastPerimOfFace = N;
}
*fVerts++ = nextToLastPerimOfFace;
int lastPerimOfFace = nextPerimeterVert + S - 2;
if (lastPerimOfFace == _numControlVerts) {
lastPerimOfFace = N;
}
*fVerts++ = lastFace ? ((corner + 1) % N) : lastPerimOfFace;
}
//
// Detection and removal of duplicate control faces -- which can only
// occur when incident faces overlap with the base face:
//
namespace {
//
// Internal helper functions to detect duplicate faces:
//
bool
doFacesMatch(int size, int const a[], int const b[], int bStart) {
for (int i = 0, j = bStart; i < size; ++i, ++j) {
j = (j == size) ? 0 : j;
if (a[i] != b[j]) return false;
}
return true;
}
bool
doFacesMatch(int size, int const a[], int const b[]) {
// Find a matching vertex to correlate possible rotation:
for (int i = 0; i < size; ++i) {
if (b[i] == a[0]) {
return doFacesMatch(size, a, b, i);
}
}
return false;
}
}
void
IrregularPatchBuilder::removeDuplicateControlFaces(
int faceSizes[], int faceVerts[],
int * numFaces, int * numFaceVerts) const {
//
// Work backwards from the last face -- detecting if it matches a
// face earlier in the arrays and removing it if so:
//
int numSizesAfter = 0;
int numVertsAfter = 0;
int * sizesAfter = faceSizes + *numFaces;
int * vertsAfter = faceVerts + *numFaceVerts;
for (int i = *numFaces - 1; i > 1; --i) {
int iSize = faceSizes[i];
int * iVerts = vertsAfter - iSize;
// Inspect the faces preceding this face for a duplicate:
bool isDuplicate = false;
int * jVerts = iVerts;
for (int j = i - 1; !isDuplicate && (j > 0); --j) {
jVerts = jVerts - faceSizes[j];
if (iSize == faceSizes[j]) {
isDuplicate = doFacesMatch(iSize, iVerts, jVerts);
}
}
// If this face was duplicated by one preceding it, remove it:
if (isDuplicate) {
if (numSizesAfter) {
std::memmove(sizesAfter - 1, sizesAfter,
numSizesAfter * sizeof(int));
std::memmove(vertsAfter - iSize, vertsAfter,
numVertsAfter * sizeof(int));
}
(*numFaces) --;
(*numFaceVerts) -= iSize;
} else {
numSizesAfter ++;
numVertsAfter += iSize;
}
sizesAfter --;
vertsAfter -= iSize;
}
}
void
IrregularPatchBuilder::sharpenBoundaryControlEdges(
int edgeVertPairs[], float edgeSharpness[], int * numSharpEdges) const {
//
// When extracting a manifold subset from a greater non-manifold
// region, the boundary edges for the subset sometimes occur on
// non-manifold edges. When extracting the subset topology in
// cases where faces overlap, those boundary edges can sometimes
// be misinterpreted as manifold interior edges -- as the extra
// faces connected to them that made the edge non-manifold are
// not included in the subset.
//
// So boundary edges are sharpened here -- which generally has no
// effect (as boundary edges are implicitly sharpened) but ensures
// that if the edge is misinterpreted as interior, it will remain
// sharp. And only boundary edges of the base face are sharpened
// here -- it is not necessary to deal with others.
//
int faceSize = _surface.GetFaceSize();
for (int corner = 0; corner < faceSize; ++corner) {
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
if (cSub.IsBoundary() && (cSub._numFacesBefore == 0)) {
*edgeSharpness++ = Sdc::Crease::SHARPNESS_INFINITE;
*edgeVertPairs++ = corner;
*edgeVertPairs++ = (corner + 1) % faceSize;
(*numSharpEdges) ++;
}
}
}
//
// The main build/assembly method to create a PatchTree:
//
// Note that the IrregularPatchBuilder was conceived to potentially build
// different representations of irregular patches (all sharing a virtual
// interface to hide that from it clients). It was here that topology
// would be inspected and builders for the different representations would
// be dispatched.
//
// At this point, the PatchTree is used for all topological cases, so
// those future intentions are not reflected here.
//
internal::IrregularPatchSharedPtr
IrregularPatchBuilder::Build() {
//
// Build a PatchTree -- the sole representation used for all irregular
// patch topologies.
//
// Given the PatchTree's origin in Far, it's builder class requires a
// Far::TopologyRefiner. Now that PatchTreeBuilder is part of Bfr, it
// could be adapted to accept the Bfr::FaceSurface directly and deal
// with any any intermediate TopologyRefiner internally.
//
// For now, the quickest way of constructing a TopologyRefiner is via
// a Far::TopologyDescriptor -- which simply refers to pre-allocated
// topology arrays. Those arrays will be allocated on the stack here
// to accommodate typical cases not involving excessively high valence.
//
// Use of TopologyDescriptor could be eliminated by defining a factory
// to create a TopologyRefiner directly from the FaceSurface, but the
// benefits relative to the cost of creating the PatchTree are not
// significant. The fact that the current assembly requires removing
// duplicate faces in some cases further complicates that process.
//
int numVerts = _numControlVerts;
int numFaces = _numControlFaces;
int numFaceVerts = _numControlFaceVerts;
int numCorners = _surface.GetFaceSize();
int numCreases = _numControlVerts;
// Allocate and partition stack buffers for the topology arrays:
int numInts = numFaces + numFaceVerts + numCorners + numCreases*2;
int numFloats = numCorners + numCreases;
Vtr::internal::StackBuffer<int, 256,true> intBuffer(numInts);
Vtr::internal::StackBuffer<float,64,true> floatBuffer(numFloats);
int * faceSizes = intBuffer;
int * faceVerts = faceSizes + numFaces;
int * cornerIndices = faceVerts + numFaceVerts;
int * creaseIndices = cornerIndices + numCorners;
float * cornerWeights = floatBuffer;
float * creaseWeights = cornerWeights + numCorners;
// Gather face topology (sizes and vertices) and explicit sharpness:
gatherControlFaces(faceSizes, faceVerts);
numCorners = _surface.GetTag().HasSharpVertices() ?
gatherControlVertexSharpness(cornerIndices, cornerWeights) : 0;
numCreases = _surface.GetTag().HasSharpEdges() ?
gatherControlEdgeSharpness(creaseIndices, creaseWeights) : 0;
// Make some adjustments when control faces may overlap:
if (controlFacesMayOverlap()) {
if (numFaces > 2) {
removeDuplicateControlFaces(faceSizes, faceVerts,
&numFaces, &numFaceVerts);
}
if (_surface.GetTag().HasBoundaryVertices()) {
sharpenBoundaryControlEdges(creaseIndices, creaseWeights,
&numCreases);
}
}
// Declare a TopologyDescriptor to reference the data gathered above:
Far::TopologyDescriptor topDescriptor;
topDescriptor.numVertices = numVerts;
topDescriptor.numFaces = numFaces;
topDescriptor.numVertsPerFace = faceSizes;
topDescriptor.vertIndicesPerFace = faceVerts;
if (numCorners) {
topDescriptor.numCorners = numCorners;
topDescriptor.cornerVertexIndices = cornerIndices;
topDescriptor.cornerWeights = cornerWeights;
}
if (numCreases) {
topDescriptor.numCreases = numCreases;
topDescriptor.creaseVertexIndexPairs = creaseIndices;
topDescriptor.creaseWeights = creaseWeights;
}
// Construct a TopologyRefiner in order to create a PatchTree:
typedef Far::TopologyDescriptor Descriptor;
typedef Far::TopologyRefinerFactory<Descriptor> RefinerFactory;
RefinerFactory::Options refinerOptions;
refinerOptions.schemeType = _surface.GetSdcScheme();
refinerOptions.schemeOptions = _surface.GetSdcOptionsInEffect();
// WIP - enable for debugging
//refinerOptions.validateFullTopology = true;
Far::TopologyRefiner * refiner =
RefinerFactory::Create(topDescriptor, refinerOptions);
// Create the PatchTree from the TopologyRefiner:
PatchTreeBuilder::Options patchTreeOptions;
patchTreeOptions.includeInteriorPatches = false;
patchTreeOptions.maxPatchDepthSharp = (unsigned char)_options.sharpLevel;
patchTreeOptions.maxPatchDepthSmooth = (unsigned char)_options.smoothLevel;
patchTreeOptions.useDoublePrecision = _options.doublePrecision;
PatchTreeBuilder patchTreeBuilder(*refiner, patchTreeOptions);
PatchTree const * patchTree = patchTreeBuilder.Build();
assert(patchTree->GetNumControlPoints() == _numControlVerts);
delete refiner;
return internal::IrregularPatchSharedPtr(patchTree);
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,179 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_IRREGULAR_PATCH_BUILDER_H
#define OPENSUBDIV3_IRREGULAR_PATCH_BUILDER_H
#include "../version.h"
#include "../bfr/faceSurface.h"
#include "../bfr/irregularPatchType.h"
#include "../vtr/stackBuffer.h"
#include <map>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// IrregularPatchBuilder takes a FaceSurface (that has been flagged as not
// regular) and builds a representation for the limit surface it defines.
//
// It is intended to hide the construction details and final representation
// of the limit surface from its clients, i.e. the SurfaceFactory. If the
// preferred representation changes, or more than one is made available, it
// should have minimal impact on its clients (ideally none).
//
// WIP - the nature of the approximating options needs more work...
// - we need some way of specifying the options of PatchTree in a
// way that's more in line with the Factory's public interface
//
class IrregularPatchBuilder {
public:
typedef FaceSurface::Index Index;
public:
// WIP - see note above
struct Options {
Options() : sharpLevel(6), smoothLevel(2), doublePrecision(false) { }
int sharpLevel;
int smoothLevel;
bool doublePrecision;
};
public:
IrregularPatchBuilder(FaceSurface const & surfaceDescription,
Options const & options = Options());
~IrregularPatchBuilder() { }
// Debugging:
void print() const;
public:
// Methods to query the number and indices of control vertices:
int GetNumControlVertices() const { return _numControlVerts; }
int GatherControlVertexIndices(Index cvIndices[]) const;
public:
// Methods to build irregular patches:
internal::IrregularPatchSharedPtr Build();
private:
// Private methods to assemble the control hull:
// A simple struct keeps track of the contribution of each corner to
// the collective control hull. These are first initialized and then
// used by methods to gather the various topological data that define
// the hull.
//
// Note that vertices of the base face and the base face itself are
// not included as part of this inventory for each corner, e.g. the
// number of control vertices or control faces may be zero if the
// corner has no incident faces.
//
struct CornerHull {
void Clear() { std::memset(this, 0, sizeof(*this)); }
int numControlFaces;
int numControlVerts;
int nextControlVert;
int surfaceIndicesOffset;
unsigned int singleSharedVert : 1;
unsigned int singleSharedFace : 1;
};
void initializeControlHullInventory();
// Methods to access the control vertex indices:
Index const * getSurfaceIndices() const;
Index const * getBaseFaceIndices() const;
Index const * getCornerIndices(int corner) const;
Index const * getCornerFaceIndices(int corner, int face) const;
// Methods to gather topology defining the control hull:
int gatherControlFaces(int faceSizes[], int faceVertices[]) const;
int gatherControlVertexSharpness(int indices[], float sharpness[]) const;
int gatherControlEdgeSharpness( int indices[], float sharpness[]) const;
// Methods to identify face-verts for an individual control face:
void getControlFaceVertices(int faceVerts[], int numFaceVerts,
int corner, int nextPerimeterVert) const;
void getControlFaceVertices(int faceVerts[], int numFaceVerts,
int corner, int nextPerimeterVert,
bool lastFace) const;
void getControlFaceVertices(int faceVerts[], int numFaceVerts,
int corner, Index const srcVerts[]) const;
// Methods for dealing with the control vertex map:
void initializeControlVertexMap();
void addMeshControlVertex(Index faceVertIndex);
void addMeshControlVertices(Index const faceVertIndices[], int faceSize);
int getLocalControlVertex(Index meshVertexIndex) const;
Index getMeshControlVertex( int localVertexIndex) const;
// Methods for dealing with potentially overlapping faces:
bool controlFacesMayOverlap() const { return _controlFacesOverlap; }
void removeDuplicateControlFaces(int faceSizes[], int faceVerts[],
int * numFaces, int * numFaceVerts) const;
void sharpenBoundaryControlEdges(int edgeIndices[], float edgeSharpness[],
int * numSharpEdges) const;
private:
// Private members:
FaceSurface const & _surface;
Options _options;
// Members defining the control hull of the surface -- some storing
// contributions to the control hull for each corner:
typedef Vtr::internal::StackBuffer<CornerHull,8,true> CornerHullArray;
int _numControlVerts;
int _numControlFaces;
int _numControlFaceVerts;
bool _controlFacesOverlap;
bool _useControlVertMap;
CornerHullArray _cornerHullInfo;
std::map<Index,int> _controlVertMap;
std::vector<Index> _controlVerts;
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_IRREGULAR_PATCH_BUILDER_H */

View File

@ -0,0 +1,63 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_IRREGULAR_PATCH_TYPE_H
#define OPENSUBDIV3_BFR_IRREGULAR_PATCH_TYPE_H
#include "../version.h"
#include <memory>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// This header was intended to contain an abstract class providing the
// common interface to potentially more than one representations of the
// irregular patches. But given only one implementation exists -- the
// PatchTree -- that level of abstraction has been deferred.
//
// So this header serves to encapsulate the forward declaration of that
// single representation, along with related typedefs for internal use.
//
class PatchTree;
namespace internal {
typedef PatchTree IrregularPatchType;
typedef std::shared_ptr<const IrregularPatchType> IrregularPatchSharedPtr;
} // end namespace internal
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_IRREGULAR_PATCH_TYPE */

55
opensubdiv/bfr/limits.h Normal file
View File

@ -0,0 +1,55 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_LIMITS_H
#define OPENSUBDIV3_BFR_LIMITS_H
#include "../version.h"
#include "../far/types.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
///
/// @brief Simple struct with limits related to topology
///
struct Limits {
/// @brief Returns the maximum allowable valence for a vertex
static int MaxValence() { return Far::VALENCE_LIMIT; }
/// @brief Returns the maximum allowable size for a face (number of
/// vertices)
static int MaxFaceSize() { return Far::VALENCE_LIMIT; }
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_LIMITS_H */

View File

@ -0,0 +1,238 @@
//
// Copyright 2021 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.
//
#include "../bfr/parameterization.h"
#include "../bfr/limits.h"
#include "../sdc/types.h"
#include <cmath>
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Construction:
//
Parameterization::Parameterization(Sdc::SchemeType scheme, int faceSize) {
int regFaceSize = Sdc::SchemeTypeTraits::GetRegularFaceSize(scheme);
_type = (unsigned char) ((regFaceSize == 4) ? QUAD : TRI);
_faceSize = (unsigned short) faceSize;
_uDim = 0;
if (faceSize != regFaceSize) {
if ((faceSize < 3) || (faceSize > Limits::MaxFaceSize())) {
// Reset size to 0 (invalid) for degenerate or excessive size
_faceSize = 0;
} else if (regFaceSize == 3) {
// Reset size to 0 (invalid) for non-triangles of tri schemes:
_faceSize = 0;
} else {
// Quad sub-faces -- use int sqrt for udim to preserve accuracy:
_type = QUAD_SUBFACES;
_uDim = (faceSize < 10) ?
(unsigned char)(2 + (faceSize > 4)) :
(unsigned char)(1 + (int) std::sqrt((float)(faceSize - 1)));
}
}
}
//
// Simple coordinate queries:
//
template <typename REAL>
void
Parameterization::GetVertexCoord(int vertex, REAL uv[2]) const {
switch (GetType()) {
case QUAD:
uv[0] = (REAL) (vertex && (vertex < 3));
uv[1] = (REAL) (vertex > 1);
break;
case TRI:
uv[0] = (REAL) (vertex == 1);
uv[1] = (REAL) (vertex == 2);
break;
case QUAD_SUBFACES:
uv[0] = (REAL) (vertex % _uDim);
uv[1] = (REAL) (vertex / _uDim);
break;
default:
uv[0] = -1.0f;
uv[1] = -1.0f;
break;
}
}
template <typename REAL>
void
Parameterization::GetEdgeCoord(int edge, REAL t, REAL uv[2]) const {
switch (GetType()) {
case QUAD:
switch (edge) {
case 0: uv[0] = t; uv[1] = 0.0f; break;
case 1: uv[0] = 1.0f; uv[1] = t; break;
case 2: uv[0] = 1.0f - t; uv[1] = 1.0f; break;
case 3: uv[0] = 0.0f; uv[1] = 1.0f - t; break;
}
break;
case TRI:
switch (edge) {
case 0: uv[0] = t; uv[1] = 0.0f; break;
case 1: uv[0] = 1.0f - t; uv[1] = t; break;
case 2: uv[0] = 0.0f; uv[1] = 1.0f - t; break;
}
break;
case QUAD_SUBFACES:
if (t < 0.5f) {
GetVertexCoord(edge, uv);
uv[0] += t;
} else {
GetVertexCoord((edge + 1) % _faceSize, uv);
uv[1] += 1.0f - t;
}
break;
default:
uv[0] = -1.0f;
uv[1] = -1.0f;
break;
}
}
template <typename REAL>
void
Parameterization::GetCenterCoord(REAL uv[2]) const {
if (GetType() == TRI) {
uv[0] = 1.0f / 3.0f;
uv[1] = 1.0f / 3.0f;
} else {
uv[0] = 0.5f;
uv[1] = 0.5f;
}
}
//
// Private sub-face coordinate conversion methods used externally:
//
template <typename REAL>
int
Parameterization::convertCoordToSubFace(bool normalized,
REAL const uvCoord[2], REAL subCoord[2]) const {
assert(HasSubFaces());
int uTile = (int) uvCoord[0];
int vTile = (int) uvCoord[1];
REAL uFrac = uvCoord[0] - (REAL) uTile;
REAL vFrac = uvCoord[1] - (REAL) vTile;
// Allow for coords slightly outside the domain of each tile:
if (uFrac > 0.75f) {
uTile ++;
uFrac = uFrac - 1.0f;
}
if (vFrac > 0.75f) {
vTile ++;
vFrac = vFrac - 1.0f;
}
// Be sure this assignment always supports in-place conversion:
if (normalized) {
subCoord[0] = uFrac * 2.0f;
subCoord[1] = vFrac * 2.0f;
} else {
subCoord[0] = uFrac;
subCoord[1] = vFrac;
}
return _uDim * vTile + uTile;
}
template <typename REAL>
void
Parameterization::convertSubFaceToCoord(bool normalized, int subFace,
REAL const subCoord[2], REAL uvCoord[2]) const {
assert(HasSubFaces());
int uTile = subFace % _uDim;
int vTile = subFace / _uDim;
// Be sure this assignment always supports in-place conversion:
if (normalized) {
uvCoord[0] = (REAL) uTile + subCoord[0] * 0.5f;
uvCoord[1] = (REAL) vTile + subCoord[1] * 0.5f;
} else {
uvCoord[0] = (REAL) uTile + subCoord[0];
uvCoord[1] = (REAL) vTile + subCoord[1];
}
}
//
// Explicit instantiation of template methods for <REAL>:
//
// Coordinate queries:
template void
Parameterization::GetVertexCoord<float>(int, float uv[2]) const;
template void
Parameterization::GetEdgeCoord<float>(int, float, float uv[2]) const;
template void
Parameterization::GetCenterCoord<float>(float uv[2]) const;
template void
Parameterization::GetVertexCoord<double>(int, double uv[2]) const;
template void
Parameterization::GetEdgeCoord<double>(int, double, double uv[2]) const;
template void
Parameterization::GetCenterCoord<double>(double uv[2]) const;
// Sub-face conversions:
template int
Parameterization::convertCoordToSubFace<float>(bool,
float const uvIn[2], float uvOut[2]) const;
template void
Parameterization::convertSubFaceToCoord<float>(bool, int,
float const uvIn[2], float uvOut[2]) const;
template int
Parameterization::convertCoordToSubFace<double>(bool,
double const uvIn[2], double uvOut[2]) const;
template void
Parameterization::convertSubFaceToCoord<double>(bool, int,
double const uvIn[2], double uvOut[2]) const;
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv

View File

@ -0,0 +1,247 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_PARAMETERIZATION_H
#define OPENSUBDIV3_BFR_PARAMETERIZATION_H
#include "../version.h"
#include "../sdc/types.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
///
/// @brief Simple class defining the 2D parameterization of a face
///
/// Parameterization is a simple class that provides information about the
/// parameterization of a face in a local (u,v) coordinate system. It is
/// defined by the size of a face (i.e. its number of vertices) and the
/// subdivision scheme that determines its limit surface.
///
/// As an example of how the subdivision scheme is essential in determining
/// the Parameterization, consider the case of a triangle. A triangle is
/// regular for the Loop scheme and so has a very simple parameterization
/// as a triangular patch. But for the Catmull-Clark scheme, a triangle is
/// an irregular face that must first be subdivided -- making its limit
/// surface a piecewise collection of quadrilateral patches.
///
class Parameterization {
public:
///
/// @brief Enumerated type for the different kinds of Parameterizations.
///
/// The three kinds of parameterizations defined are: quadrilateral,
/// triangle and quadrangulated sub-faces. This is not intended for
/// common use, but is publicly available for situations when it is
/// necessary to distinguish:
///
enum Type { QUAD, ///< Quadrilateral
TRI, ///< Triangle
QUAD_SUBFACES ///< Partitioned into quadrilateral sub-faces
};
public:
//@{
/// @name Construction and initialization
///
/// Valid construction of a Parameterization is only achieved with
/// the non-default constructor. A Parameterization will be invalid
/// (and so unusable) if default constructed, or constructed using
/// arguments that describe a face that cannot be parameterized.
///
/// @brief Primary constructor with subdivision scheme and face size
Parameterization(Sdc::SchemeType scheme, int faceSize);
/// @brief Returns true if correctly initialized
bool IsValid() const { return (_faceSize > 0); }
/// @brief Default construction produces an invalid instance
Parameterization() : _type(0), _uDim(0), _faceSize(0) { }
Parameterization(Parameterization const &) = default;
Parameterization & operator=(Parameterization const &) = default;
~Parameterization() = default;
//@}
//@{
/// @name Simple queries
///
/// Simple queries of a valid Parameterization.
///
/// @brief Returns the type of parameterization assigned
Type GetType() const { return (Type) _type; }
/// @brief Returns the size (number of vertices) of the corresponding face
int GetFaceSize() const { return _faceSize; }
//@}
public:
//@{
/// @name Methods to inspect parametric features
///
/// Methods are available to inspect common topological features of a
/// Parameterization, i.e. the parametric coordinates corresponding
/// to the vertices, edges or center of the face it represents.
///
/// Methods for vertices and edges require an index of the desired
/// vertex or edge. The edge parameter "t" locally parameterizes the
/// edge over [0,1] in a counter-clockwise orientation.
///
/// @brief Returns the (u,v) coordinate of a given vertex
template <typename REAL>
void GetVertexCoord(int vertexIndex, REAL uvCoord[2]) const;
/// @brief Returns the (u,v) coordinate at any point on a given edge
template <typename REAL>
void GetEdgeCoord(int edgeIndex, REAL t, REAL uvCoord[2]) const;
/// @brief Returns the (u,v) coordinate for the center of the face
template <typename REAL>
void GetCenterCoord(REAL uvCoord[2]) const;
//@}
public:
//@{
/// @name Methods to deal with discontinuous parameterizations
///
/// Parameterizations that have been partitioned into sub-faces are
/// discontinuous and warrant care in order to process them effectively --
/// often requiring explicit conversions.
///
/// These conversion methods to and from the local coordinates of a
/// sub-face are only for use with instances of Parameterization that
/// have such sub-faces. Results for input coordinates that are
/// significantly outside the domain of the input parameterization are
/// undefined.
///
/// Note that sub-face coordinates that are normalized correspond to
/// coordinates for Ptex faces.
///
/// @brief Returns if Parameterization has been partitioned into sub-faces
bool HasSubFaces() const;
/// @brief Returns the integer sub-face containing the given (u,v)
template <typename REAL>
int GetSubFace(REAL const uvCoord[2]) const;
/// @brief Convert (u,v) to a sub-face (return value) and its local (u,v)
/// coordinate
template <typename REAL>
int ConvertCoordToSubFace(
REAL const uvCoord[2], REAL subFaceCoord[2]) const;
/// @brief Convert a sub-face and its local (u,v) coordinate to (u,v)
template <typename REAL>
void ConvertSubFaceToCoord(int subFace,
REAL const subFaceCoord[2], REAL uvCoord[2]) const;
/// @brief Convert (u,v) to a sub-face (return value) and its normalized
/// (u,v) coordinate
template <typename REAL>
int ConvertCoordToNormalizedSubFace(
REAL const uvCoord[2], REAL subFaceCoord[2]) const;
/// @brief Convert a sub-face and its normalized (u,v) coordinate to (u,v)
template <typename REAL>
void ConvertNormalizedSubFaceToCoord(int subFace,
REAL const subFaceCoord[2], REAL uvCoord[2]) const;
//@}
private:
template <typename REAL>
int convertCoordToSubFace(bool normalized,
REAL const uvCoord[2], REAL subFaceCoord[2]) const;
template <typename REAL>
void convertSubFaceToCoord(bool normalized, int subFace,
REAL const subFaceCoord[2], REAL uvCoord[2]) const;
private:
unsigned char _type;
unsigned char _uDim;
unsigned short _faceSize;
};
//
// Inline sub-face coordinate conversion methods:
//
inline bool
Parameterization::HasSubFaces() const {
return (_type == QUAD_SUBFACES);
}
template <typename REAL>
inline int
Parameterization::GetSubFace(REAL const uvCoord[2]) const {
if (!HasSubFaces()) return 0;
int uTile = (int) uvCoord[0];
int vTile = (int) uvCoord[1];
return (vTile + ((uvCoord[1] - (REAL) vTile) > 0.75f)) * _uDim +
(uTile + ((uvCoord[0] - (REAL) uTile) > 0.75f));
}
// Conversions to unnormalized sub-face coordinates:
template <typename REAL>
inline int
Parameterization::ConvertCoordToSubFace(
REAL const uvCoord[2], REAL subCoord[2]) const {
return convertCoordToSubFace<REAL>(false, uvCoord, subCoord);
}
template <typename REAL>
inline void
Parameterization::ConvertSubFaceToCoord(
int subFace, REAL const subCoord[2], REAL uvCoord[2]) const {
convertSubFaceToCoord<REAL>(false, subFace, subCoord, uvCoord);
}
// Conversions to normalized sub-face coordinates:
template <typename REAL>
inline int
Parameterization::ConvertCoordToNormalizedSubFace(
REAL const uvCoord[2], REAL subCoord[2]) const {
return convertCoordToSubFace<REAL>(true, uvCoord, subCoord);
}
template <typename REAL>
inline void
Parameterization::ConvertNormalizedSubFaceToCoord(
int subFace, REAL const subCoord[2], REAL uvCoord[2]) const {
convertSubFaceToCoord<REAL>(true, subFace, subCoord, uvCoord);
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_PARAMETERIZATION */

View File

@ -0,0 +1,503 @@
//
// Copyright 2021 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.
//
#include "../bfr/patchTree.h"
#include "../far/patchBasis.h"
#include <algorithm>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
using Far::PatchDescriptor;
using Far::PatchParam;
//
// Avoid warnings comparing floating point values to zero:
//
namespace {
#ifdef __INTEL_COMPILER
#pragma warning (push)
#pragma warning disable 1572
#endif
template <typename REAL>
inline bool isWeightZero(REAL w) { return (w == (REAL)0.0); }
#ifdef __INTEL_COMPILER
#pragma warning (pop)
#endif
}
//
// Simple inline methods for the PatchTree::TreeNode:
//
// sets all the children to point to the patch of given index
inline void
PatchTree::TreeNode::SetChildren(int index) {
for (int i=0; i<4; ++i) {
children[i].isSet = true;
children[i].isLeaf = true;
children[i].SetIndex(index);
}
}
// sets the child in "quadrant" to point to node or patch of the given index
inline void
PatchTree::TreeNode::SetChild(int quadrant, int index, bool isLeaf) {
assert(!children[quadrant].isSet);
children[quadrant].isSet = true;
children[quadrant].isLeaf = isLeaf;
children[quadrant].SetIndex(index);
}
//
// PatchTree constructor and destructor:
//
PatchTree::PatchTree() :
_useDoublePrecision(false),
_patchesIncludeNonLeaf(false),
_patchesAreTriangular(false),
_regPatchType(PatchDescriptor::NON_PATCH),
_irregPatchType(PatchDescriptor::NON_PATCH),
_regPatchSize(0),
_irregPatchSize(0),
_patchPointStride(0),
_numSubFaces(0),
_numControlPoints(0),
_numRefinedPoints(0),
_numSubPatchPoints(0),
_numIrregPatches(0),
_treeDepth(-1) {
}
PatchTree::~PatchTree() {
}
//
// Class methods supporting access to patches:
//
PatchTree::PatchPointArray
PatchTree::GetSubPatchPoints(int patchIndex) const {
return PatchPointArray(
&_patchPoints[patchIndex * _patchPointStride],
_patchParams[patchIndex].IsRegular() ? _regPatchSize
: _irregPatchSize);
}
template <typename REAL>
int
PatchTree::EvalSubPatchBasis(int patchIndex, REAL u, REAL v,
REAL wP[], REAL wDu[], REAL wDv[],
REAL wDuu[], REAL wDuv[], REAL wDvv[]) const {
PatchParam const & param = _patchParams[patchIndex];
return Far::internal::EvaluatePatchBasis(
param.IsRegular() ? _regPatchType : _irregPatchType,
param, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
template <typename REAL>
int
PatchTree::EvalSubPatchStencils(int patchIndex, REAL u, REAL v,
REAL sP[], REAL sDu[], REAL sDv[],
REAL sDuu[], REAL sDuv[], REAL sDvv[]) const{
//
// If evaluating a regular interior patch at the base level, evaluate
// basis directly into the output stencils:
//
PatchParam const & param = _patchParams[patchIndex];
if ((param.GetDepth() == 0) && param.IsRegular() && !param.GetBoundary()) {
assert(_regPatchSize == _numControlPoints);
return Far::internal::EvaluatePatchBasis(
_regPatchType, param, u, v, sP, sDu, sDv, sDuu, sDuv, sDuv);
}
// Invoke according to precision of the internal stencil matrix:
if (_useDoublePrecision) {
return evalSubPatchStencils<double>(patchIndex, u, v,
sP, sDu, sDv, sDuu, sDuv, sDvv);
} else {
return evalSubPatchStencils<float>(patchIndex, u, v,
sP, sDu, sDv, sDuu, sDuv, sDvv);
}
}
namespace {
template <typename REAL_SRC, typename REAL_DST>
void addToArray(REAL_DST * dst, int n, REAL_DST w, REAL_SRC const * src) {
if (isWeightZero(w)) return;
// WIP - we can guarantee these vectors are aligned (in future),
// so anything here to make use of SSE/AVX will be worth it
// - prefer something portable to ensure auto-vectorization
// - we can also pad the matrix so "n" is a multiple of 4...
// - note will need to specialize when cast required
for (int i = 0; i < n; ++i) {
dst[i] += (REAL_DST) (w * src[i]);
}
}
}
template <typename REAL_MATRIX, typename REAL>
int
PatchTree::evalSubPatchStencils(int patchIndex, REAL u, REAL v,
REAL sP[], REAL sDu[], REAL sDv[],
REAL sDuu[], REAL sDuv[], REAL sDvv[]) const{
PatchParam const & param = _patchParams[patchIndex];
//
// Basis weights must be evaluated into local arrays and transformed
// into stencil weights (in terms of the base level control points
// rather than the patch's control points):
//
REAL wDuBuffer[20], wDvBuffer[20];
REAL wDuuBuffer[20], wDuvBuffer[20], wDvvBuffer[20];
REAL wP[20];
REAL * wDu = 0;
REAL * wDv = 0;
REAL * wDuu = 0;
REAL * wDuv = 0;
REAL * wDvv = 0;
bool d1 = sDu && sDv;
if (d1) {
wDu = wDuBuffer;
wDv = wDvBuffer;
}
bool d2 = d1 && sDuu && sDuv && sDvv;
if (d2) {
wDuu = wDuuBuffer;
wDuv = wDuvBuffer;
wDvv = wDvvBuffer;
}
Far::internal::EvaluatePatchBasis(
param.IsRegular() ? _regPatchType : _irregPatchType,
param, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
PatchPointArray patchPoints = GetSubPatchPoints(patchIndex);
//
// Clear and accumulate the stencil weights for the contribution of
// each point of the patch:
//
std::memset(sP, 0, sizeof(REAL) * _numControlPoints);
if (d1) {
std::memset(sDu, 0, sizeof(REAL) * _numControlPoints);
std::memset(sDv, 0, sizeof(REAL) * _numControlPoints);
}
if (d2) {
std::memset(sDuu, 0, sizeof(REAL) * _numControlPoints);
std::memset(sDuv, 0, sizeof(REAL) * _numControlPoints);
std::memset(sDvv, 0, sizeof(REAL) * _numControlPoints);
}
for (int i = 0; i < patchPoints.size(); ++i) {
int pIndex = patchPoints[i];
if (pIndex < _numControlPoints) {
sP[pIndex] += wP [i];
if (d1) {
sDu[pIndex] += wDu[i];
sDv[pIndex] += wDv[i];
}
if (d2) {
sDuu[pIndex] += wDuu[i];
sDuv[pIndex] += wDuv[i];
sDvv[pIndex] += wDvv[i];
}
} else {
std::vector<REAL_MATRIX> const & pStencilMtx =
getStencilMatrix<REAL_MATRIX>();
assert(!pStencilMtx.empty());
REAL_MATRIX const * pStencilRow =
&pStencilMtx[(pIndex - _numControlPoints) * _numControlPoints];
addToArray(sP, _numControlPoints, wP[i], pStencilRow);
if (d1) {
addToArray(sDu, _numControlPoints, wDu[i], pStencilRow);
addToArray(sDv, _numControlPoints, wDv[i], pStencilRow);
}
if (d2) {
addToArray(sDuu, _numControlPoints, wDuu[i], pStencilRow);
addToArray(sDuv, _numControlPoints, wDuv[i], pStencilRow);
addToArray(sDvv, _numControlPoints, wDvv[i], pStencilRow);
}
}
}
return _numControlPoints;
}
//
// Local functions and class methods supporting tree searches and construction:
//
namespace {
template <typename T>
inline int
transformUVToQuadQuadrant(T const & median, T & u, T & v) {
int uHalf = (u >= median);
if (uHalf) u -= median;
int vHalf = (v >= median);
if (vHalf) v -= median;
return (vHalf << 1) | uHalf;
}
template <typename T>
int inline
transformUVToTriQuadrant(T const & median, T & u, T & v, bool & rotated) {
if (!rotated) {
if (u >= median) {
u -= median;
return 1;
}
if (v >= median) {
v -= median;
return 2;
}
if ((u + v) >= median) {
rotated = true;
return 3;
}
return 0;
} else {
if (u < median) {
v -= median;
return 1;
}
if (v < median) {
u -= median;
return 2;
}
u -= median;
v -= median;
if ((u + v) < median) {
rotated = true;
return 3;
}
return 0;
}
}
} // end namespace
inline PatchTree::TreeNode *
PatchTree::assignLeafOrChildNode(TreeNode * node,
bool isLeaf, int quadrant, int patchIndex) {
// This is getting far enough away from PatchMap's original
// structure and implementation that it warrants a face lift...
if (!node->children[quadrant].isSet) {
if (isLeaf) {
node->SetChild(quadrant, patchIndex, true);
return node;
} else {
int newNodeIndex = (int)_treeNodes.size();
_treeNodes.push_back(TreeNode());
node->SetChild(quadrant, newNodeIndex, false);
return &_treeNodes[newNodeIndex];
}
}
if (isLeaf || node->children[quadrant].isLeaf) {
// Need to replace the leaf index with new node and index:
int newNodeIndex = (int)_treeNodes.size();
_treeNodes.push_back(TreeNode());
TreeNode * newNode = &_treeNodes[newNodeIndex];
// Move existing patch index from child to new child node:
newNode->patchIndex = node->children[quadrant].index;
node->children[quadrant].SetIndex(newNodeIndex);
node->children[quadrant].isLeaf = false;
if (isLeaf) {
newNode->SetChild(quadrant, patchIndex, true);
}
return newNode;
} else {
// Simply return the existing interior node:
return &_treeNodes[node->children[quadrant].index];
}
}
void
PatchTree::buildQuadtree() {
int numPatches = (int) _patchParams.size();
_treeNodes.reserve(numPatches);
_treeNodes.resize(_numSubFaces ? _numSubFaces : 1);
_treeDepth = 0;
for (int patchIndex = 0; patchIndex < numPatches; ++patchIndex) {
PatchParam const & param = _patchParams[patchIndex];
int depth = param.GetDepth();
int rootDepth = param.NonQuadRoot();
int subFace = param.GetFaceId();
assert((subFace == 0) || (subFace < _numSubFaces));
TreeNode * node = &_treeNodes[subFace];
_treeDepth = std::max(depth, _treeDepth);
if (depth == rootDepth) {
node->patchIndex = patchIndex;
continue;
}
if (!_patchesAreTriangular) {
// Use the UV bits of the PatchParam directly for quad patches:
int u = param.GetU();
int v = param.GetV();
for (int j = rootDepth + 1; j <= depth; ++j) {
int uBit = (u >> (depth - j)) & 1;
int vBit = (v >> (depth - j)) & 1;
int quadrant = (vBit << 1) | uBit;
node = assignLeafOrChildNode(node, (j == depth), quadrant,
patchIndex);
}
} else {
// Use an interior UV point of triangles to identify quadrants:
double u = 0.25f;
double v = 0.25f;
param.UnnormalizeTriangle(u, v);
double median = 0.5f;
bool triRotated = false;
for (int j = rootDepth + 1; j <= depth; ++j, median *= 0.5f) {
int quadrant = transformUVToTriQuadrant(median, u, v,
triRotated);
node = assignLeafOrChildNode(node, (j == depth), quadrant,
patchIndex);
}
}
}
}
int
PatchTree::searchQuadtree(double u, double v,
int subFace, int searchDepth) const {
//
// These details warrant closer inspection and possible tweaking
// since non-leaf patches were optionally added to the tree...
//
//
// Identify the root patch and make a quick exit when seeking it. If
// there is no patch at level 0 but subpatches present (possible e.g.
// if adjacent to an irregular face) force the search to level 1:
//
TreeNode const * node = &_treeNodes[subFace];
if (_treeDepth == 0) {
assert(node->patchIndex >= 0);
return node->patchIndex;
}
int maxDepth = ((searchDepth >= 0) && _patchesIncludeNonLeaf) ? searchDepth
: _treeDepth;
if (maxDepth == (_numSubFaces > 0)) {
if (node->patchIndex >= 0) {
return node->patchIndex;
}
maxDepth = 1;
}
//
// Search the tree for the sub-patch containing the given (u,v)
//
double median = 0.5f;
bool triRotated = false;
for (int depth = 1; depth <= maxDepth; ++depth, median *= 0.5f) {
int quadrant = _patchesAreTriangular
? transformUVToTriQuadrant(median, u, v, triRotated)
: transformUVToQuadQuadrant(median, u, v);
// Identify child patch if leaf, otherwise child node:
if (node->children[quadrant].isLeaf) {
return node->children[quadrant].index;
} else if (node->children[quadrant].isSet) {
node = &_treeNodes[node->children[quadrant].index];
}
}
assert(node->patchIndex >= 0);
return node->patchIndex;
}
//
// Explicit instantiation for methods supporting float and double:
//
template int PatchTree::EvalSubPatchBasis<float>(int patchIndex,
float u, float v,
float wP[], float wDu[], float wDv[],
float wDuu[], float wDuv[], float wDvv[]) const;
template int PatchTree::EvalSubPatchStencils<float>(int patchIndex,
float u, float v,
float sP[], float sDu[], float sDv[],
float sDuu[], float sDuv[], float sDvv[]) const;
template int PatchTree::EvalSubPatchBasis<double>(int patchIndex,
double u, double v,
double wP[], double wDu[], double wDv[],
double wDuu[], double wDuv[], double wDvv[]) const;
template int PatchTree::EvalSubPatchStencils<double>(int patchIndex,
double u, double v,
double sP[], double sDu[], double sDv[],
double sDuu[], double sDuv[], double sDvv[]) const;
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

251
opensubdiv/bfr/patchTree.h Normal file
View File

@ -0,0 +1,251 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_PATCH_TREE_H
#define OPENSUBDIV3_BFR_PATCH_TREE_H
#include "../version.h"
#include "../far/patchDescriptor.h"
#include "../far/patchParam.h"
#include "../vtr/array.h"
#include <vector>
#include <cstring>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// A PatchTree is a hierarchical collection of parametric patches that
// form a piecewise representation of the limit surface for a single face
// of a mesh. Using the patch representations from Far, it combines
// stripped down versions of the PatchTable and PatchMap from Far and a
// raw representation of stencils into a more compact representation
// suited to evaluating a single face. These are constructed based on
// adaptive refinement of a single face of a Far::TopologyRefiner.
//
// As the internal representation for the limit surface of a face with
// irregular topology, the PatchTree is not publicly exposed. As is the
// case with other internal Bfr classes whose headers are not exported,
// it is not further protected by the "internal" namespace.
//
// PatchTree was initially developed as an internal class for Far, so
// some comments may still reflect that origin.
//
// PatchTree also includes functionality beyond what is needed for Bfr
// for potential future use. Most notable is the ability for the tree
// to store a patch at interior nodes -- in addition to the leaf nodes.
// This allows the depth of evaluation to be varied, which takes place
// when searching a patch from the tree (specifying a maximum depth for
// the search). Construction options allow this functionality to be
// selectively enabled/disabled, and Bfr currently disables it.
//
class PatchTree {
public:
// Constructors are protected
~PatchTree();
// Simple public accessors:
int GetNumControlPoints() const { return _numControlPoints; }
int GetNumSubPatchPoints() const { return _numSubPatchPoints; }
int GetNumPointsTotal() const { return _numControlPoints +
_numSubPatchPoints; }
// These queries may not be necessary...
int GetDepth() const { return _treeDepth; }
int GetNumPatches() const { return (int)_patchParams.size(); }
// Methods to access stencils to compute patch points:
template <typename REAL>
REAL const * GetStencilMatrix() const;
bool UsesDoublePrecision() const { return _useDoublePrecision; }
// Methods supporting evaluation:
int HasSubFaces() const { return _numSubFaces > 0; }
int GetNumSubFaces() const { return _numSubFaces; }
int FindSubPatch(double u, double v, int subFace=0, int maxDep=-1) const;
typedef Vtr::ConstArray<int> PatchPointArray;
PatchPointArray GetSubPatchPoints(int subPatch) const;
Far::PatchParam GetSubPatchParam( int subPatch) const;
// Main evaluation methods - basis weights or limit stencils:
template <typename REAL>
int EvalSubPatchBasis(int subPatch, REAL u, REAL v, REAL w[],
REAL wDu[], REAL wDv[],
REAL wDuu[], REAL wDuv[], REAL wDvv[]) const;
template <typename REAL>
int EvalSubPatchStencils(int subPatch, REAL u, REAL v, REAL s[],
REAL sDu[], REAL sDv[],
REAL sDuu[], REAL sDuv[], REAL sDvv[]) const;
protected:
PatchTree();
friend class PatchTreeBuilder;
//
// Internal utilities to support the stencil matrix of variable precision
//
template <typename REAL> std::vector<REAL> const & getStencilMatrix() const;
template <typename REAL> std::vector<REAL> & getStencilMatrix();
template <typename REAL_MATRIX, typename REAL>
int evalSubPatchStencils(int subPatch, REAL u, REAL v, REAL s[],
REAL sDu[], REAL sDv[],
REAL sDuu[], REAL sDuv[], REAL sDvv[]) const;
protected:
// Internal quad-tree node type and assembly and search methods:
struct TreeNode {
struct Child {
unsigned int isSet : 1;
unsigned int isLeaf : 1;
unsigned int index : 28;
void SetIndex(int indexArg) { index = indexArg & 0xfffffff; }
};
TreeNode() : patchIndex(-1) {
std::memset(children, 0, sizeof(children));
}
void SetChildren(int index);
void SetChild(int quadrant, int index, bool isLeaf);
int patchIndex;
Child children[4];
};
int searchQuadtree(double u, double v, int subFace=0, int depth=-1) const;
void buildQuadtree();
TreeNode * assignLeafOrChildNode(TreeNode * node,
bool isLeaf, int quadrant, int index);
private:
// Private members:
typedef Far::PatchDescriptor::Type PatchType;
// Simple configuration members:
unsigned int _useDoublePrecision : 1;
unsigned int _patchesIncludeNonLeaf : 1;
unsigned int _patchesAreTriangular : 1;
PatchType _regPatchType;
PatchType _irregPatchType;
int _regPatchSize;
int _irregPatchSize;
int _patchPointStride;
// Simple topology inventory members:
int _numSubFaces;
int _numControlPoints;
int _numRefinedPoints;
int _numSubPatchPoints;
int _numIrregPatches;
// Vectors for points and PatchParams of all patches:
//
// Note we store both regular and irregular patch point indices in the
// same vector (using a common stride for each patch) and the patch type
// determined by the PatchParam -- in the same way that face-varying
// patches are stored in the PatchTable. Could also be stored in
// separate "patch arrays" or separated in other ways and managed with
// a bit more book-keeping.
//
std::vector<int> _patchPoints;
std::vector<Far::PatchParam> _patchParams;
// The quadtree organizing the patches:
std::vector<TreeNode> _treeNodes;
int _treeDepth;
// Array of stencils for computing patch points from control points
// (single or double to be used as specified on construction):
std::vector<float> _stencilMatrixFloat;
std::vector<double> _stencilMatrixDouble;
};
//
// Internal specializations to support the stencil matrix of variable
// precision -- const access presumes it is non-empty (and so assert)
// while non-const access may be used to populate it.
//
template <>
inline std::vector<float> const &
PatchTree::getStencilMatrix<float>() const {
assert(!_stencilMatrixFloat.empty());
return _stencilMatrixFloat;
}
template <>
inline std::vector<double> const &
PatchTree::getStencilMatrix<double>() const {
assert(!_stencilMatrixDouble.empty());
return _stencilMatrixDouble;
}
template <>
inline std::vector<float> &
PatchTree::getStencilMatrix<float>() {
return _stencilMatrixFloat;
}
template <>
inline std::vector<double> &
PatchTree::getStencilMatrix<double>() {
return _stencilMatrixDouble;
}
//
// Inline methods:
//
inline Far::PatchParam
PatchTree::GetSubPatchParam(int subPatch) const {
return _patchParams[subPatch];
}
inline int
PatchTree::FindSubPatch(double u, double v, int subFace, int maxDep) const {
return searchQuadtree(u, v, subFace, maxDep);
}
template <typename REAL>
inline REAL const *
PatchTree::GetStencilMatrix() const {
return &getStencilMatrix<REAL>()[0];
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_PATCH_TREE */

View File

@ -0,0 +1,598 @@
//
// Copyright 2021 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.
//
#include "../bfr/patchTreeBuilder.h"
#include "../far/primvarRefiner.h"
#include "../far/topologyRefiner.h"
#include "../far/topologyDescriptor.h"
#include "../far/patchBuilder.h"
#include "../far/sparseMatrix.h"
#include "../far/ptexIndices.h"
#include "../vtr/stackBuffer.h"
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
using Vtr::internal::Level;
using Vtr::internal::StackBuffer;
using Far::TopologyRefiner;
using Far::Index;
using Far::ConstIndexArray;
using Far::SparseMatrix;
using Far::PatchBuilder;
using Far::PatchDescriptor;
using Far::PatchParam;
//
// Construction initializes some of the main components of the
// build process (e.g. the Far::PatchBuilder) but defers most of
// the work to other methods:
//
PatchTreeBuilder::PatchTreeBuilder(TopologyRefiner & faceRefiner,
Options const & options) :
_patchTree(new PatchTree),
_faceRefiner(faceRefiner),
_faceAtRoot(0),
_patchBuilder(0) {
//
// Adaptive refinement in Far requires smooth level <= sharp level,
// with the sharp level taking precedence. And if attempting to
// generate patches at the base level, force at least one level of
// refinement when necessary:
//
int adaptiveLevelPrimary = options.maxPatchDepthSharp;
int adaptiveLevelSecondary = options.maxPatchDepthSmooth;
if (adaptiveLevelSecondary > adaptiveLevelPrimary) {
adaptiveLevelSecondary = adaptiveLevelPrimary;
}
// If primary is 0, so is secondary -- see if level 1 required:
if (adaptiveLevelSecondary == 0) {
if (rootFaceNeedsRefinement()) {
adaptiveLevelPrimary = std::max(1, adaptiveLevelPrimary);
adaptiveLevelSecondary = 1;
}
}
//
// Apply adaptive refinement to a local refiner for this face:
//
TopologyRefiner::AdaptiveOptions adaptiveOptions(adaptiveLevelPrimary);
adaptiveOptions.secondaryLevel = adaptiveLevelSecondary;
adaptiveOptions.useInfSharpPatch = true;
adaptiveOptions.useSingleCreasePatch = false;
adaptiveOptions.considerFVarChannels = false;
ConstIndexArray baseFaceArray(&_faceAtRoot, 1);
_faceRefiner.RefineAdaptive(adaptiveOptions, baseFaceArray);
//
// Determine offsets per level (we could eventually include local
// points in the levels in which the patch occurs)
//
int numLevels = _faceRefiner.GetNumLevels();
_levelOffsets.resize(1 + numLevels);
_levelOffsets[0] = 0;
for (int i = 0; i < numLevels; ++i) {
_levelOffsets[1 + i] = _levelOffsets[i]
+ _faceRefiner.GetLevel(i).GetNumVertices();
}
//
// Create a PatchBuilder for this refiner:
//
PatchBuilder::BasisType patchBuilderIrregularBasis;
if (options.irregularBasis == Options::REGULAR) {
patchBuilderIrregularBasis = PatchBuilder::BASIS_REGULAR;
} else if (options.irregularBasis == Options::LINEAR) {
patchBuilderIrregularBasis = PatchBuilder::BASIS_LINEAR;
} else {
patchBuilderIrregularBasis = PatchBuilder::BASIS_GREGORY;
}
PatchBuilder::Options patchOptions;
patchOptions.regBasisType = PatchBuilder::BASIS_REGULAR;
patchOptions.irregBasisType = patchBuilderIrregularBasis;
patchOptions.approxInfSharpWithSmooth = false;
patchOptions.approxSmoothCornerWithSharp = false;
patchOptions.fillMissingBoundaryPoints = true;
_patchBuilder = PatchBuilder::Create(faceRefiner, patchOptions);
//
// Initialize general PatchTree members relating to patch topology:
//
Vtr::internal::Level const & baseLevel = _faceRefiner.getLevel(0);
int thisFaceSize = baseLevel.getFaceVertices(_faceAtRoot).size();
int regFaceSize = _patchBuilder->GetRegularFaceSize();
// Configuration:
_patchTree->_useDoublePrecision = options.useDoublePrecision;
_patchTree->_patchesIncludeNonLeaf = options.includeInteriorPatches;
_patchTree->_patchesAreTriangular = (regFaceSize == 3);
_patchTree->_regPatchType = _patchBuilder->GetRegularPatchType();
_patchTree->_irregPatchType = _patchBuilder->GetIrregularPatchType();
_patchTree->_regPatchSize =
PatchDescriptor(_patchTree->_regPatchType).GetNumControlVertices();
_patchTree->_irregPatchSize =
PatchDescriptor(_patchTree->_irregPatchType).GetNumControlVertices();
_patchTree->_patchPointStride =
std::max(_patchTree->_regPatchSize, _patchTree->_irregPatchSize);
// Topology:
_patchTree->_numSubFaces = (thisFaceSize == regFaceSize) ? 0 : thisFaceSize;
_patchTree->_numControlPoints = _faceRefiner.GetLevel(0).GetNumVertices();
_patchTree->_numRefinedPoints = _faceRefiner.GetNumVerticesTotal()
- _patchTree->_numControlPoints;
_patchTree->_numSubPatchPoints = _patchTree->_numRefinedPoints;
}
PatchTreeBuilder::~PatchTreeBuilder() {
delete _patchBuilder;
}
const PatchTree *
PatchTreeBuilder::Build() {
identifyPatches();
initializePatches();
if (_patchTree->_useDoublePrecision) {
initializeStencilMatrix<double>();
} else {
initializeStencilMatrix<float>();
}
initializeQuadTree();
return _patchTree;
}
bool
PatchTreeBuilder::rootFaceNeedsRefinement() const {
//
// The Far::PatchBuilder cannot construct a single patch from a face
// in the base level under the following circumstances:
//
// - the face is or is adjacent to an irregular face (non-quad)
// - the face contains an inf-sharp dart vertex
// - the face contains an interior val-2 vertex
// - the face contains an interior val-3 vertex adj to a tri
//
// All but the first are subject to additional conditions (e.g.
// whether the irregular feature is isolated or not) but until those
// conditions are clear, such features will trigger refinement.
//
int baseFace = _faceAtRoot;
Level const & baseLevel = _faceRefiner.getLevel(0);
Level::VTag const & fTags = baseLevel.getFaceCompositeVTag(baseFace);
ConstIndexArray fVerts = baseLevel.getFaceVertices(baseFace);
//
// Vertices incident non-quads in any way are easily detected:
//
if (fTags._incidIrregFace) return true;
//
// A dart and inf-sharp irregularity may indicate an inf-sharp dart,
// so inspect the face-vertices:
//
if ((fTags._rule & Sdc::Crease::RULE_DART) && fTags._infIrregular) {
for (int i = 0; i < fVerts.size(); ++i) {
Level::VTag const & vTag = baseLevel.getVertexTag(fVerts[i]);
if ((vTag._rule & Sdc::Crease::RULE_DART) && vTag._infSharpEdges) {
// WIP - inf-sharp dart is fine in some cases (TBD):
// - possibly when the edge-end isolated
// - refine for any occurrence until fully determined
return true;
}
}
}
//
// Interior extra-ordinary vertices of low valence require inspection
// of the face-vertices to test valence and other conditions:
//
if (fTags._xordinary) {
for (int i = 0, fSize = fVerts.size(); i < fSize; ++i) {
Level::VTag const & vTag = baseLevel.getVertexTag(fVerts[i]);
if (vTag._xordinary && !vTag._boundary && !vTag._infSharpEdges) {
int vValence = baseLevel.getVertexFaces(fVerts[i]).size();
if ((vValence == 2) || ((vValence == 3) && (fSize == 3))) {
// WIP - low valence verts are fine in some cases (TBD)
// - val-2 a problem only when two adjacent
// - refine for any occurrence until fully determined
return true;
}
}
}
}
return false;
}
bool
PatchTreeBuilder::testFaceAncestors() const {
// Conditions of overlapping faces that require testing base face:
return (_patchBuilder->GetRegularFaceSize() == 3) &&
(_faceRefiner.getLevel(0).getNumEdges() == 3) &&
(_faceRefiner.getLevel(0).getNumFaces() > 1);
}
bool
PatchTreeBuilder::faceAncestorIsRoot(int level, int face) const {
// Move up the hierarchy to the base level:
for (int i = level; i > 0; --i) {
face = _faceRefiner.getRefinement(i-1).getChildFaceParentFace(face);
}
return (face == _faceAtRoot);
}
void
PatchTreeBuilder::identifyPatches() {
//
// Take inventory of the patches. Only one face exists at the base
// level -- the root face. Check all other levels breadth first:
//
bool incNonLeaf = _patchTree->_patchesIncludeNonLeaf;
_patchFaces.clear();
int numIrregPatches = 0;
if (_patchBuilder->IsFaceAPatch(0, _faceAtRoot)) {
if (incNonLeaf || _patchBuilder->IsFaceALeaf(0, _faceAtRoot)) {
bool isRegular = _patchBuilder->IsPatchRegular(0, _faceAtRoot);
_patchFaces.push_back(PatchFace(0, _faceAtRoot, isRegular));
numIrregPatches += !isRegular;
}
}
// Under rare circumstances, the normally quick test for a patch is
// flawed and includes faces descended from neighboring faces:
bool testBaseFace = testFaceAncestors();
int numLevels = _faceRefiner.GetNumLevels();
for (int level = 1; level < numLevels; ++level) {
int numFaces = _faceRefiner.getLevel(level).getNumFaces();
for (int face = 0; face < numFaces; ++face) {
if (testBaseFace && !faceAncestorIsRoot(level, face)) continue;
if (_patchBuilder->IsFaceAPatch(level, face)) {
if (incNonLeaf || _patchBuilder->IsFaceALeaf(level, face)) {
bool isRegular = _patchBuilder->IsPatchRegular(level, face);
_patchFaces.push_back(PatchFace(level, face, isRegular));
numIrregPatches += !isRegular;
}
}
}
}
//
// Allocate and populate the arrays of patch data for the identified
// patches:
//
int numPatches = (int) _patchFaces.size();
assert(numPatches);
_patchTree->_patchPoints.resize(numPatches * _patchTree->_patchPointStride);
_patchTree->_patchParams.resize(numPatches);
_patchTree->_numIrregPatches = numIrregPatches;
_patchTree->_numSubPatchPoints += numIrregPatches *
_patchTree->_irregPatchSize;
}
void
PatchTreeBuilder::initializePatches() {
// Keep track of the growing index of local points in irregular patches:
int irregPointIndexBase = _patchTree->_numControlPoints +
_patchTree->_numRefinedPoints;
Far::PtexIndices ptexIndices(_faceRefiner);
for (size_t i = 0; i < _patchFaces.size(); ++i) {
PatchFace const & pf = _patchFaces[i];
PatchParam & patchParam = _patchTree->_patchParams[i];
Index * patchPoints =
&_patchTree->_patchPoints[i * _patchTree->_patchPointStride];
if (pf.isRegular) {
// Determine boundary mask before computing/assigning PatchParam:
int boundaryMask =
_patchBuilder->GetRegularPatchBoundaryMask(pf.level, pf.face);
patchParam = _patchBuilder->ComputePatchParam(pf.level, pf.face,
ptexIndices, true, boundaryMask, true);
// Gather the points of the patch -- since they are assigned
// directly into the PatchTree's buffer by the PatchBuilder
// here, they must be offset as a post-process:
_patchBuilder->GetRegularPatchPoints(pf.level, pf.face,
boundaryMask, patchPoints);
for (int j = 0; j < _patchTree->_regPatchSize; ++j) {
patchPoints[j] += _levelOffsets[pf.level];
}
} else {
// Compute/assign the PatchParam for an irregular patch:
patchParam = _patchBuilder->ComputePatchParam(pf.level, pf.face,
ptexIndices, false /*irreg*/, 0 /*mask*/, false);
// Assign indices of new/local points for this irregular patch:
for (int j = 0; j < _patchTree->_irregPatchSize; ++j) {
patchPoints[j] = irregPointIndexBase ++;
}
}
}
}
//
// Some local interpolatable types for combining stencil vectors -- the
// rows of the stencil matrix:
//
namespace {
//
// When accessing a "row" for a control point, the only non-zero
// entry is that at the index, with a value of 1, so just store
// that index so the StencilRows can combine it:
//
struct ControlRow {
ControlRow(int index) : _index(index) { }
ControlRow() { }
ControlRow operator[] (int index) const {
return ControlRow(index);
}
// Members:
int _index;
};
//
// A "row" for each stencil is just our typical vector of variable
// size that needs to support [].
//
// For the first level, there are no source rows for the control
// points so combine with the proxy ControlRow defined above. All
// other levels will accumulate StencilRows as weighted combinations
// of other StencilRows.
//
// WIP - consider combining StencilRows to exploit SSE/AVX vectorization
// - we can (in future) easily guarantee both are 4-word aligned
// - we can also pad the rows to a multiple of 4
// - prefer writing the combination in a portable way that makes
// use of auto-vectorization
//
template <typename REAL>
struct StencilRow {
StencilRow() : _data(0), _size(0) { }
StencilRow(REAL * data, int size) :
_data(data), _size(size) { }
StencilRow(REAL const * data, int size) :
_data(const_cast<REAL*>(data)), _size(size) { }
void Clear() {
for (int i = 0; i < _size; ++i) {
_data[i] = 0.0f;
}
}
void AddWithWeight(ControlRow const & src, REAL weight) {
assert(src._index >= 0);
_data[src._index] += weight;
}
void AddWithWeight(StencilRow const & src, REAL weight) {
assert(src._size == _size);
// Weights passed here by PrimvarRefiner should be non-zero
// WIP - see note on potential/future auto-vectorization above
for (int i = 0; i < _size; ++i) {
_data[i] += weight * src._data[i];
}
}
StencilRow operator[](int index) const {
return StencilRow(_data + index * _size, _size);
}
// Members:
REAL * _data;
int _size;
};
}
template <typename REAL>
void
PatchTreeBuilder::initializeStencilMatrix() {
if (_patchTree->_numSubPatchPoints == 0) return;
//
// Allocate and initialize a full matrix of true stencils (i.e.
// factored in terms of the control points):
//
int numPointStencils = _patchTree->_numRefinedPoints +
(_patchTree->_numIrregPatches *
_patchTree->_irregPatchSize);
int numControlPoints = _patchTree->_numControlPoints;
std::vector<REAL> & stencilMatrix = _patchTree->getStencilMatrix<REAL>();
stencilMatrix.resize(numPointStencils*numControlPoints);
//
// For refined points, initialize successive rows of the stencil matrix
// a level at a time using the PrimvarRefiner to accumulate contributing
// rows:
//
int numLevels = _faceRefiner.GetNumLevels();
if (numLevels > 1) {
Far::PrimvarRefinerReal<REAL> primvarRefiner(_faceRefiner);
StencilRow<REAL> dstRow(&stencilMatrix[0], numControlPoints);
primvarRefiner.Interpolate(1, ControlRow(-1), dstRow);
for (int level = 2; level < numLevels; ++level) {
StencilRow<REAL> srcRow = dstRow;
dstRow = srcRow[_faceRefiner.getLevel(level-1).getNumVertices()];
primvarRefiner.Interpolate(level, srcRow, dstRow);
}
}
//
// For irregular patch points, append rows for each irregular patch:
//
if (_patchTree->_numIrregPatches) {
SparseMatrix<REAL> irregConvMatrix;
std::vector<Index> irregSourcePoints;
int stencilIndexBase = _patchTree->_numRefinedPoints;
for (size_t i = 0; i < _patchFaces.size(); ++i) {
if (!_patchFaces[i].isRegular) {
getIrregularPatchConversion(_patchFaces[i],
irregConvMatrix, irregSourcePoints);
appendConversionStencilsToMatrix(stencilIndexBase,
irregConvMatrix,irregSourcePoints);
stencilIndexBase += _patchTree->_irregPatchSize;
}
}
}
}
template <typename REAL>
void
PatchTreeBuilder::appendConversionStencilsToMatrix(
int stencilBaseIndex,
SparseMatrix<REAL> const & conversionMatrix,
std::vector<Index> const & sourcePoints) {
//
// Each row of the sparse conversion matrix corresponds to a row
// of the stencil matrix -- which will be computed from the weights
// and indices of stencils indicated by the SparseMatrix row:
//
int numControlPoints = _patchTree->_numControlPoints;
int numPatchPoints = conversionMatrix.GetNumRows();
std::vector<REAL> & stencilMatrix = _patchTree->getStencilMatrix<REAL>();
StencilRow<REAL> srcStencils(&stencilMatrix[0], numControlPoints);
StencilRow<REAL> dstStencils = srcStencils[stencilBaseIndex];
for (int i = 0; i < numPatchPoints; ++i) {
StencilRow<REAL> dstStencil = dstStencils[i];
dstStencil.Clear();
int const * rowIndices = &conversionMatrix.GetRowColumns(i)[0];
REAL const * rowWeights = &conversionMatrix.GetRowElements(i)[0];
int rowSize = conversionMatrix.GetRowSize(i);
for (int j = 0; j < rowSize; ++j) {
REAL srcWeight = rowWeights[j];
int srcIndex = sourcePoints[rowIndices[j]];
// Simply increment single weight if this is a control point
if (srcIndex < numControlPoints) {
dstStencil._data[srcIndex] += srcWeight;
} else {
int srcStencilIndex = srcIndex - numControlPoints;
StencilRow<REAL> srcStencil = srcStencils[srcStencilIndex];
dstStencil.AddWithWeight(srcStencil, srcWeight);
}
}
}
}
void
PatchTreeBuilder::initializeQuadTree() {
_patchTree->buildQuadtree();
}
template <typename REAL>
void
PatchTreeBuilder::getIrregularPatchConversion(PatchFace const & pf,
SparseMatrix<REAL> & conversionMatrix,
std::vector<Index> & sourcePoints) {
//
// The topology of an irregular patch is determined by its four corners:
//
Level::VSpan cornerSpans[4];
_patchBuilder->GetIrregularPatchCornerSpans(pf.level, pf.face, cornerSpans);
//
// Compute the conversion matrix from refined/source points to the
// set of points local to this patch:
//
_patchBuilder->GetIrregularPatchConversionMatrix(pf.level, pf.face,
cornerSpans, conversionMatrix);
//
// Identify the refined/source points for the patch and append stencils
// for the local patch points in terms of the source points:
//
int numSourcePoints = conversionMatrix.GetNumColumns();
sourcePoints.resize(numSourcePoints);
_patchBuilder->GetIrregularPatchSourcePoints(pf.level, pf.face,
cornerSpans, &sourcePoints[0]);
int sourceIndexOffset = _levelOffsets[pf.level];
for (int i = 0; i < numSourcePoints; ++i) {
sourcePoints[i] += sourceIndexOffset;
}
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,145 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_PATCH_TREE_BUILDER_H
#define OPENSUBDIV3_BFR_PATCH_TREE_BUILDER_H
#include "../version.h"
#include "../bfr/patchTree.h"
#include "../far/topologyRefiner.h"
#include "../far/patchBuilder.h"
#include "../far/sparseMatrix.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// The PatchTreeBuilder class assemble a PatchTree from one or more
// topological descriptions -- keeping the PatchTree class free from
// the dependencies of whatever its topological source may be.
//
// Having been originally conceived as part of Far, the PatchTree is
// assembled from a given Far::TopologyRefiner, which is constructed
// for a local neighborhood by another class. Some of the internal
// details of PatchTree assembly also include complexity from that
// history when a PatchTree could be constructed from any face of any
// given TopologyRefiner -- which is no longer the case. The public
// now prevents such unsupported flexibility, but internal support
// remains.
//
class PatchTreeBuilder {
public:
//
// Minimize the number of shape approximating Options here (compared
// to the Far classes):
//
// Note that the "interior patches" capability of PatchTree is not
// used in Bfr and so is never enabled. It is left available as a
// reminder of that ability for future use.
//
struct Options {
enum BasisType { REGULAR, GREGORY, LINEAR };
Options(int depth = 4) : irregularBasis((unsigned char) GREGORY),
maxPatchDepthSharp((unsigned char) depth),
maxPatchDepthSmooth(15),
includeInteriorPatches(false),
useDoublePrecision(false) { }
unsigned char irregularBasis;
unsigned char maxPatchDepthSharp;
unsigned char maxPatchDepthSmooth;
unsigned char includeInteriorPatches : 1;
unsigned char useDoublePrecision : 1;
};
public:
//
// Public interface intended for use by other builders requiring
// PatchTrees -- now reduced essentially to a single method:
//
PatchTreeBuilder(Far::TopologyRefiner & refiner, Options const & options);
~PatchTreeBuilder();
const PatchTree * Build();
PatchTree * GetPatchTree() const { return _patchTree; }
private:
// Internal struct for a patch in the refinement hierarchy:
struct PatchFace {
PatchFace(int levelArg, int faceArg, bool isReg = true) :
face(faceArg), level((short)levelArg), isRegular(isReg) { }
int face;
short level;
short isRegular;
};
// Internal methods to identify and assemble patches and the tree:
bool rootFaceNeedsRefinement() const;
bool testFaceAncestors() const;
bool faceAncestorIsRoot(int level, int face) const;
void identifyPatches();
void initializePatches();
void initializeQuadTree();
// Internal methods to assemble the matrix of stencils converting
// points of irregular patches from points in the refined levels:
template <typename REAL>
void initializeStencilMatrix();
template <typename REAL>
void getIrregularPatchConversion(PatchFace const & patchFace,
Far::SparseMatrix<REAL> & convMatrix,
std::vector<Far::Index> & srcPoints);
template <typename REAL>
void appendConversionStencilsToMatrix(int stencilIndexBase,
Far::SparseMatrix<REAL> const & convMatrix,
std::vector<Far::Index> const & srcPoints);
private:
// The PatchTree instance being assembled:
PatchTree * _patchTree;
// Member variables supporting its assembly:
Far::TopologyRefiner & _faceRefiner;
Far::Index _faceAtRoot;
std::vector<int> _levelOffsets;
std::vector<PatchFace> _patchFaces;
Far::PatchBuilder * _patchBuilder;
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_PATCH_TREE_BUILDER_H */

View File

@ -0,0 +1,543 @@
//
// 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.
//
#ifndef OPENSUBDIV3_BFR_POINT_OPERATIONS_H
#define OPENSUBDIV3_BFR_POINT_OPERATIONS_H
#include "../version.h"
#include <cstring>
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Internal utilities for efficiently dealing with single and multiple
// floating point tuples, i.e. "points":
//
namespace points {
//
// Simple classes for primitive point operations -- to be specialized for
// small, fixed sizes via template <int SIZE>.
//
// These class templates with static methods are partially specialized for
// SIZE. The copy operation is made a separate class due to its additional
// template parameters (different precision for source and destination).
// Combining the two can lead to undesired ambiguities with the desired
// partial specializations.
//
template <typename REAL, int SIZE>
struct PointBuilder {
static void Set(REAL pDst[], REAL w, REAL const pSrc[], int size) {
for (int i = 0; i < size; ++i) {
pDst[i] = w * pSrc[i];
}
}
static void Add(REAL pDst[], REAL w, REAL const pSrc[], int size) {
for (int i = 0; i < size; ++i) {
pDst[i] += w * pSrc[i];
}
}
};
template <typename REAL_DST, typename REAL_SRC, int SIZE = 0>
struct PointCopier {
static void Copy(REAL_DST * pDst, REAL_SRC const * pSrc, int size) {
for (int i = 0; i < size; ++i) {
pDst[i] = (REAL_DST) pSrc[i];
}
}
};
// Specialization for SIZE = 1:
template <typename REAL>
struct PointBuilder<REAL, 1> {
static void Set(REAL * pDst, REAL w, REAL const * pSrc, int) {
pDst[0] = w * pSrc[0];
}
static void Add(REAL * pDst, REAL w, REAL const * pSrc, int) {
pDst[0] += w * pSrc[0];
}
};
template <typename REAL>
struct PointCopier<REAL, REAL, 1> {
static void Copy(REAL * pDst, REAL const * pSrc, int) {
pDst[0] = pSrc[0];
}
};
// Specialization for SIZE = 2:
template <typename REAL>
struct PointBuilder<REAL, 2> {
static void Set(REAL * pDst, REAL w, REAL const * pSrc, int) {
pDst[0] = w * pSrc[0];
pDst[1] = w * pSrc[1];
}
static void Add(REAL * pDst, REAL w, REAL const * pSrc, int) {
pDst[0] += w * pSrc[0];
pDst[1] += w * pSrc[1];
}
};
template <typename REAL>
struct PointCopier<REAL, REAL, 2> {
static void Copy(REAL * pDst, REAL const * pSrc, int) {
pDst[0] = pSrc[0];
pDst[1] = pSrc[1];
}
};
// Specialization for SIZE = 3:
template <typename REAL>
struct PointBuilder<REAL, 3> {
static void Set(REAL * pDst, REAL w, REAL const * pSrc, int) {
pDst[0] = w * pSrc[0];
pDst[1] = w * pSrc[1];
pDst[2] = w * pSrc[2];
}
static void Add(REAL * pDst, REAL w, REAL const * pSrc, int) {
pDst[0] += w * pSrc[0];
pDst[1] += w * pSrc[1];
pDst[2] += w * pSrc[2];
}
};
template <typename REAL>
struct PointCopier<REAL, REAL, 3> {
static void Copy(REAL * pDst, REAL const * pSrc, int) {
pDst[0] = pSrc[0];
pDst[1] = pSrc[1];
pDst[2] = pSrc[2];
}
};
// Specialization for SIZE = 4:
template <typename REAL>
struct PointBuilder<REAL, 4> {
static void Set(REAL * pDst, REAL w, REAL const * pSrc, int) {
pDst[0] = w * pSrc[0];
pDst[1] = w * pSrc[1];
pDst[2] = w * pSrc[2];
pDst[3] = w * pSrc[3];
}
static void Add(REAL * pDst, REAL w, REAL const * pSrc, int) {
pDst[0] += w * pSrc[0];
pDst[1] += w * pSrc[1];
pDst[2] += w * pSrc[2];
pDst[3] += w * pSrc[3];
}
};
template <typename REAL>
struct PointCopier<REAL, REAL, 4> {
static void Copy(REAL * pDst, REAL const * pSrc, int) {
pDst[0] = pSrc[0];
pDst[1] = pSrc[1];
pDst[2] = pSrc[2];
pDst[3] = pSrc[3];
}
};
// Additional specialization for copy when precision matches:
template <typename REAL, int SIZE>
struct PointCopier<REAL, REAL, SIZE> {
static void Copy(REAL * pDst, REAL const * pSrc, int size) {
std::memcpy(pDst, pSrc, size * sizeof(REAL));
}
};
//
// Each major operation is encapsulated in a separate class consisting of
// the following:
//
// - a struct containing all parameters of the operation
// - a single private method with a generic implementing for all SIZEs
// - a public method invoking specializations for small SIZEs
//
// Further specializations of the private implementation of each operation
// are possible.
//
//
// The Parameters for the operations vary slightly (based on the operation
// and potentially varying data destinations) but generally consist of the
// following:
//
// - a description of a full set of input points involved, including:
// - the data, size and stride of the array of points
//
// - a description of the subset of the input points used, including
// - the number and optional indices for each "source" point
//
// - a description of the resulting points, including:
// - the number of resulting points
// - an array of locations or single location for resulting points
// - an array of or consecutive weights for all resulting points
//
// Several operations combining control points for patch evaluation make
// use of the same parameters, so these are encapsulated in a common set.
// All classes are expected to declare Parameters for their operation --
// even if it is simply a typedef for the set of common parameters.
//
// Common set of parameters for operations combining points:
template <typename REAL>
struct CommonCombinationParameters {
REAL const * pointData;
int pointSize;
int pointStride;
int const * srcIndices;
int srcCount;
int resultCount;
REAL ** resultArray;
REAL const * const * weightArray;
};
//
// Combination of source points into a single result (for use computing
// position only, applying single stencils, and other purposes):
//
template <typename REAL>
class Combine1 {
public:
typedef CommonCombinationParameters<REAL> Parameters;
private:
template <int SIZE = 0>
static void apply(Parameters const & args) {
typedef struct PointBuilder<REAL,SIZE> Point;
int pSize = args.pointSize;
int pStride = args.pointStride;
REAL const * w = args.weightArray[0];
REAL * p = args.resultArray[0];
if (args.srcIndices == 0) {
REAL const * pSrc = args.pointData;
Point::Set(p, w[0], pSrc, pSize);
for (int i = 1; i < args.srcCount; ++i) {
pSrc += pStride;
Point::Add(p, w[i], pSrc, pSize);
}
} else {
REAL const * pSrc = args.pointData + pStride * args.srcIndices[0];
Point::Set(p, w[0], pSrc, pSize);
for (int i = 1; i < args.srcCount; ++i) {
pSrc = args.pointData + pStride * args.srcIndices[i];
Point::Add(p, w[i], pSrc, pSize);
}
}
}
public:
static void Apply(Parameters const & parameters) {
switch (parameters.pointSize) {
case 1: apply<1>(parameters); break;
case 2: apply<2>(parameters); break;
case 3: apply<3>(parameters); break;
case 4: apply<4>(parameters); break;
default: apply<>(parameters); break;
}
}
};
//
// Combination of source points into three results (for use computing
// position and 1st derivatives):
//
template <typename REAL>
class Combine3 {
public:
typedef CommonCombinationParameters<REAL> Parameters;
private:
template <int SIZE = 0>
static void apply(Parameters const & args) {
typedef struct PointBuilder<REAL,SIZE> Point;
int pSize = args.pointSize;
int pStride = args.pointStride;
REAL const * const * wArray = args.weightArray;
REAL ** pArray = args.resultArray;
//
// Apply each successive control point to all derivatives at once,
// rather than computing each derivate independently:
//
REAL const * pSrc = (args.srcIndices == 0) ? args.pointData :
(args.pointData + pStride * args.srcIndices[0]);
Point::Set(pArray[0], wArray[0][0], pSrc, pSize);
Point::Set(pArray[1], wArray[1][0], pSrc, pSize);
Point::Set(pArray[2], wArray[2][0], pSrc, pSize);
for (int i = 1; i < args.srcCount; ++i) {
pSrc = (args.srcIndices == 0) ? (pSrc + pStride) :
(args.pointData + pStride * args.srcIndices[i]);
Point::Add(pArray[0], wArray[0][i], pSrc, pSize);
Point::Add(pArray[1], wArray[1][i], pSrc, pSize);
Point::Add(pArray[2], wArray[2][i], pSrc, pSize);
}
}
public:
static void Apply(Parameters const & parameters) {
switch (parameters.pointSize) {
case 1: apply<1>(parameters); break;
case 2: apply<2>(parameters); break;
case 3: apply<3>(parameters); break;
case 4: apply<4>(parameters); break;
default: apply<>(parameters); break;
}
}
};
//
// Combination of source points into an arbitrary array of results (for
// use computing position with all derivatives, i.e. 6 results):
//
template <typename REAL>
class CombineMultiple {
public:
typedef CommonCombinationParameters<REAL> Parameters;
private:
template <int SIZE = 0>
static void
apply(Parameters const & args) {
typedef struct PointBuilder<REAL,SIZE> Point;
int pSize = args.pointSize;
int pStride = args.pointStride;
REAL const * const * wArray = args.weightArray;
REAL ** pArray = args.resultArray;
//
// Apply each successive control point to all derivatives at once,
// rather than computing each derivate independently:
//
REAL const * pSrc = (args.srcIndices == 0) ? args.pointData :
(args.pointData + pStride * args.srcIndices[0]);
for (int j = 0; j < args.resultCount; ++j) {
Point::Set(pArray[j], wArray[j][0], pSrc, pSize);
}
for (int i = 1; i < args.srcCount; ++i) {
pSrc = (args.srcIndices == 0) ? (pSrc + pStride) :
(args.pointData + pStride * args.srcIndices[i]);
for (int j = 0; j < args.resultCount; ++j) {
Point::Add(pArray[j], wArray[j][i], pSrc, pSize);
}
}
}
public:
static void
Apply(Parameters const & parameters) {
switch (parameters.pointSize) {
case 1: apply<1>(parameters); break;
case 2: apply<2>(parameters); break;
case 3: apply<3>(parameters); break;
case 4: apply<4>(parameters); break;
default: apply<>(parameters); break;
}
}
};
//
// Combination of a subset of N input points into M resulting points
// in consecutive memory locations. The weights for the resulting M
// points (N for the input points contributing to each result) are
// also stored consecutively:
//
template <typename REAL>
class CombineConsecutive {
public:
struct Parameters {
REAL const * pointData;
int pointSize;
int pointStride;
int srcCount;
int resultCount;
REAL * resultData;
REAL const * weightData;
};
private:
template <int SIZE = 0>
static void
apply(Parameters const & args) {
typedef struct PointBuilder<REAL,SIZE> Point;
REAL const * w = args.weightData;
REAL * p = args.resultData;
for (int i = 0; i < args.resultCount; ++i) {
REAL const * pSrc = args.pointData;
Point::Set(p, w[0], pSrc, args.pointSize);
for (int j = 1; j < args.srcCount; ++j) {
pSrc += args.pointStride;
Point::Add(p, w[j], pSrc, args.pointSize);
}
p += args.pointStride;
w += args.srcCount;
}
}
public:
static void
Apply(Parameters const & parameters) {
switch (parameters.pointSize) {
case 1: apply<1>(parameters); break;
case 2: apply<2>(parameters); break;
case 3: apply<3>(parameters); break;
case 4: apply<4>(parameters); break;
default: apply<>(parameters); break;
}
}
};
//
// Split the N-sided face formed by the N input control points, i.e.
// compute the midpoint of the face and the midpoint of each edge --
// to be stored consecutively in the given location for results:
//
template <typename REAL>
class SplitFace {
public:
struct Parameters {
REAL const * pointData;
int pointSize;
int pointStride;
int srcCount;
REAL * resultData;
};
private:
template <int SIZE = 0>
static void apply(Parameters const & args) {
typedef struct PointBuilder<REAL,SIZE> Point;
int N = args.srcCount;
REAL invN = 1.0f / (REAL) N;
REAL * facePoint = args.resultData;
std::memset(facePoint, 0, args.pointSize * sizeof(REAL));
for (int i = 0; i < N; ++i) {
int j = (i < (N - 1)) ? (i + 1) : 0;
REAL const * pi = args.pointData + args.pointStride * i;
REAL const * pj = args.pointData + args.pointStride * j;
Point::Add(facePoint, invN, pi, args.pointSize);
REAL * edgePoint = args.resultData + args.pointStride * (1 + i);
Point::Set(edgePoint, 0.5f, pi, args.pointSize);
Point::Add(edgePoint, 0.5f, pj, args.pointSize);
}
}
public:
static void Apply(Parameters const & parameters) {
switch (parameters.pointSize) {
case 1: apply<1>(parameters); break;
case 2: apply<2>(parameters); break;
case 3: apply<3>(parameters); break;
case 4: apply<4>(parameters); break;
default: apply<>(parameters); break;
}
}
};
//
// Copy a subset of N input points -- identified by the indices given for
// each -- to the resulting location specified:
//
template <typename REAL_DST, typename REAL_SRC>
class CopyConsecutive {
public:
struct Parameters {
REAL_SRC const * pointData;
int pointSize;
int pointStride;
int const * srcIndices;
int srcCount;
REAL_DST * resultData;
int resultStride;
};
private:
template <int SIZE = 0>
static void apply(Parameters const & args) {
typedef struct PointCopier<REAL_DST,REAL_SRC,SIZE> Point;
for (int i = 0; i < args.srcCount; ++i) {
REAL_DST * pDst = args.resultData + args.resultStride * i;
REAL_SRC const * pSrc = args.pointData +
args.pointStride * args.srcIndices[i];
Point::Copy(pDst, pSrc, args.pointSize);
}
}
public:
static void Apply(Parameters const & parameters) {
switch (parameters.pointSize) {
case 1: apply<1>(parameters); break;
case 2: apply<2>(parameters); break;
case 3: apply<3>(parameters); break;
case 4: apply<4>(parameters); break;
default: apply<>(parameters); break;
}
}
};
} // end namespace points
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_POINT_OPERATIONS_H */

View File

@ -0,0 +1,554 @@
//
// Copyright 2021 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.
//
#include "../bfr/refinerSurfaceFactory.h"
#include "../bfr/vertexDescriptor.h"
#include "../far/topologyRefiner.h"
#include <map>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
using Far::ConstIndexArray;
using Far::ConstLocalIndexArray;
//
// Main constructor and destructor:
//
RefinerSurfaceFactoryBase::RefinerSurfaceFactoryBase(
Far::TopologyRefiner const & mesh, Options const & factoryOptions) :
SurfaceFactory(mesh.GetSchemeType(),
mesh.GetSchemeOptions(),
factoryOptions),
_mesh(mesh),
_numFaces(mesh.GetLevel(0).GetNumFaces()),
_numFVarChannels(mesh.GetNumFVarChannels()) {
// Management of internal cache deferred to subclasses
}
//
// Inline support method to provide a valid face-varying channel from
// a given face-varying ID/handle used in the factory interface:
//
inline int
RefinerSurfaceFactoryBase::getFaceVaryingChannel(FVarID fvarID) const {
return ((0 <= fvarID) && (fvarID < _numFVarChannels)) ? (int)fvarID : -1;
}
//
// Virtual methods from the SurfaceFactoryMeshAdapter interface supporting
// Surface construction and initialization:
//
// Simple/trivial face queries:
//
bool
RefinerSurfaceFactoryBase::isFaceHole(Index face) const {
return _mesh.HasHoles() && _mesh.getLevel(0).isFaceHole(face);
}
int
RefinerSurfaceFactoryBase::getFaceSize(Index baseFace) const {
return _mesh.GetLevel(0).GetFaceVertices(baseFace).size();
}
//
// Specifying vertex or face-varying indices for a face:
//
int
RefinerSurfaceFactoryBase::getFaceVertexIndices(Index baseFace,
Index indices[]) const {
ConstIndexArray fVerts = _mesh.GetLevel(0).GetFaceVertices(baseFace);
std::memcpy(indices, &fVerts[0], fVerts.size() * sizeof(Index));
return fVerts.size();
}
int
RefinerSurfaceFactoryBase::getFaceFVarValueIndices(Index baseFace,
FVarID fvarID, Index indices[]) const {
int fvarChannel = getFaceVaryingChannel(fvarID);
if (fvarChannel < 0) return 0;
ConstIndexArray fvarValues =
_mesh.GetLevel(0).GetFaceFVarValues(baseFace, fvarChannel);
std::memcpy(indices, &fvarValues[0], fvarValues.size() * sizeof(Index));
return fvarValues.size();
}
//
// Specifying the topology around a face-vertex:
//
int
RefinerSurfaceFactoryBase::populateFaceVertexDescriptor(
Index baseFace, int cornerVertex,
VertexDescriptor * vertexDescriptor) const {
VertexDescriptor & vd = *vertexDescriptor;
//
// Identify the vertex index for the specified corner of the face
// and topology information related to it:
//
Vtr::internal::Level const & baseLevel = _mesh.getLevel(0);
Far::Index vIndex = baseLevel.getFaceVertices(baseFace)[cornerVertex];
ConstIndexArray vFaces = baseLevel.getVertexFaces(vIndex);
int nFaces = vFaces.size();
Vtr::internal::Level::VTag vTag = baseLevel.getVertexTag(vIndex);
bool isManifold = !vTag._nonManifold;
//
// Initialize, assign and finalize the vertex topology:
//
// Note there is no need to check valence or face sizes with any
// max here as TopologyRefiner construction excludes extreme cases.
//
vd.Initialize(nFaces);
{
// Assign ordering and boundary status:
vd.SetManifold(isManifold);
vd.SetBoundary(vTag._boundary);
// Assign face sizes if not all regular:
if (vTag._incidIrregFace) {
for (int i = 0; i < nFaces; ++i) {
vd.SetIncidentFaceSize(i,
baseLevel.getFaceVertices(vFaces[i]).size());
}
}
// Assign vertex sharpness when present:
if (vTag._semiSharp || vTag._infSharp) {
vd.SetVertexSharpness(
baseLevel.getVertexSharpness(vIndex));
}
// Assign edge sharpness when present:
if (vTag._semiSharpEdges || vTag._infSharpEdges) {
if (isManifold) {
// Can use manifold/ordered edge indices here:
ConstIndexArray vEdges = baseLevel.getVertexEdges(vIndex);
for (int i = 0; i < vEdges.size(); ++i) {
vd.SetManifoldEdgeSharpness(i,
baseLevel.getEdgeSharpness(vEdges[i]));
}
} else {
// Must use face-edges and identify next/prev edges in face:
ConstLocalIndexArray vInFace =
baseLevel.getVertexFaceLocalIndices(vIndex);
for (int i = 0; i < nFaces; ++i) {
ConstIndexArray fEdges = baseLevel.getFaceEdges(vFaces[i]);
int eLeading = vInFace[i];
int eTrailing = (eLeading ? eLeading : fEdges.size()) - 1;
vd.SetIncidentFaceEdgeSharpness(i,
baseLevel.getEdgeSharpness(fEdges[eLeading]),
baseLevel.getEdgeSharpness(fEdges[eTrailing]));
}
}
}
}
vd.Finalize();
//
// Return the index of the base face around the vertex:
//
// Remember that for some non-manifold cases the face may occur
// multiple times around this vertex, so make sure to identify the
// instance that matches the specified corner of the face.
//
if (isManifold) {
return vFaces.FindIndex(baseFace);
} else {
ConstLocalIndexArray vInFace =
baseLevel.getVertexFaceLocalIndices(vIndex);
for (int i = 0; i < vFaces.size(); ++i) {
if ((vFaces[i] == baseFace) && (vInFace[i] == cornerVertex)) {
return i;
}
}
assert("Cannot identify face-vertex around non-manifold vertex." == 0);
return -1;
}
}
//
// Specifying vertex and face-varying indices around a face-vertex --
// both virtual methods trivially use a common internal method to get
// the indices for a particular vertex Index:
//
int
RefinerSurfaceFactoryBase::getFaceVertexPointIndices(
Index baseFace, int cornerVertex,
Index indices[], int vtxOrFVarChannel) const {
Vtr::internal::Level const & baseLevel = _mesh.getLevel(0);
Far::Index vIndex = baseLevel.getFaceVertices(baseFace)[cornerVertex];
ConstIndexArray vFaces = baseLevel.getVertexFaces(vIndex);
ConstLocalIndexArray vInFace = baseLevel.getVertexFaceLocalIndices(vIndex);
int nIndices = 0;
for (int i = 0; i < vFaces.size(); ++i) {
ConstIndexArray srcIndices = (vtxOrFVarChannel < 0) ?
baseLevel.getFaceVertices(vFaces[i]) :
baseLevel.getFaceFVarValues(vFaces[i], vtxOrFVarChannel);
int srcStart = vInFace[i];
int srcCount = srcIndices.size();
for (int j = srcStart; j < srcCount; ++j) {
indices[nIndices++] = srcIndices[j];
}
for (int j = 0; j < srcStart; ++j) {
indices[nIndices++] = srcIndices[j];
}
}
return nIndices;
}
int
RefinerSurfaceFactoryBase::getFaceVertexIncidentFaceVertexIndices(
Index baseFace, int cornerVertex,
Index indices[]) const {
return getFaceVertexPointIndices(baseFace, cornerVertex, indices, -1);
}
int
RefinerSurfaceFactoryBase::getFaceVertexIncidentFaceFVarValueIndices(
Index baseFace, int corner,
FVarID fvarID, Index indices[]) const {
int fvarChannel = getFaceVaryingChannel(fvarID);
if (fvarChannel < 0) return 0;
return getFaceVertexPointIndices(baseFace, corner, indices, fvarChannel);
}
//
// Optional SurfaceFactoryMeshAdapter methods for determining if a face has
// purely regular topology, and retrieving its control point indices if so:
//
bool
RefinerSurfaceFactoryBase::getFaceNeighborhoodVertexIndicesIfRegular(
Index baseFace, Index vtxIndices[]) const {
//
// Get the composite tag for the corners of the face and reject some
// of the obvious irregular features first:
//
Vtr::internal::Level const & baseLevel = _mesh.getLevel(0);
Vtr::internal::Level::VTag fTag = baseLevel.getFaceCompositeVTag(baseFace);
if (fTag._xordinary || fTag._nonManifold
|| fTag._incidIrregFace
|| fTag._semiSharp || fTag._semiSharpEdges
|| fTag._infIrregular) {
return false;
}
//
// At this point, we have not rejected inf-sharp features, as they
// are intertwined with boundaries -- which may be regular. Recall
// that both edges and vertices may have been explicitly sharpened
// by application of the boundary interpolation options, so we must
// exclude faces with any inf-sharp features added elsewhere.
//
// Recall also that in the case of "boundary none", a face that does
// not have a limit surface will have been tagged as a hole. So all
// faces here -- and all regular faces in general -- have a limit.
//
// To determine regular patches with inf-sharp features, we can first
// trivially reject an interior face if it has any inf-sharp features.
// Otherwise, we have to inspect the vertices of boundary patches:
//
assert(!baseLevel.isFaceHole(baseFace));
if (!fTag._boundary) {
if (fTag._infSharp || fTag._infSharpEdges) {
return false;
}
} else {
ConstIndexArray fVerts = baseLevel.getFaceVertices(baseFace);
for (int i = 0; i < fVerts.size(); ++i) {
Far::Index vIndex = fVerts[i];
Vtr::internal::Level::VTag vTag = baseLevel.getVertexTag(vIndex);
if (!vTag._boundary) {
if (vTag._rule != Sdc::Crease::RULE_SMOOTH) return false;
} else if (baseLevel.getVertexFaces(vIndex).size() == 1) {
if (vTag._rule != Sdc::Crease::RULE_CORNER) return false;
} else {
if (vTag._rule != Sdc::Crease::RULE_CREASE) return false;
}
}
}
// Only regular cases make it this far -- assign indices if requested:
if (vtxIndices) {
getFacePatchPointIndices(baseFace, vtxIndices, -1);
}
return true;
}
bool
RefinerSurfaceFactoryBase::getFaceNeighborhoodFVarValueIndicesIfRegular(
Index baseFace, FVarID fvarID, Index fvarIndices[]) const {
int fvarChannel = getFaceVaryingChannel(fvarID);
if (fvarChannel < 0) return false;
//
// This method will only be invoked when the vertex topology is
// regular, so no need to confirm that here.
//
// It is also recommended that this method only be used when the
// face-varying topology exactly matches the vertex topology, i.e.
// don't try to return a regular boundary patch that is a subset
// of a regular interior patch, as face-varying interpolation
// rules may affect that boundary patch (making it irregular).
//
Vtr::internal::Level const & baseLevel = _mesh.getLevel(0);
bool isRegular = baseLevel.doesFaceFVarTopologyMatch(baseFace, fvarChannel);
if (isRegular && fvarIndices) {
getFacePatchPointIndices(baseFace, fvarIndices, fvarChannel);
}
return isRegular;
}
//
// Supporting functions to extract regular patch points.
//
// The two main functions here load the patch points from faces into
// arrays for patches -- knowing that the mesh topology is regular.
// Various methods to do all or part if this in different forms exist
// in other places in the Far code, but they typically cater to more
// general purposes (e.g. including rotations). These are written to
// be as fast as possible for the purpose here.
//
namespace {
// Local less-verbose typedefs for Far indices and arrays:
typedef Vtr::internal::Level Level;
typedef Far::Index Index;
typedef ConstIndexArray IArray;
typedef ConstLocalIndexArray LIArray;
// Avoid repeated integer modulo N operations:
inline int _mod3(int x) { return (x < 3) ? x : (x - 3); }
inline int _mod4(int x) { return (x & 3); }
inline int _mod6(int x) { return (x < 6) ? x : (x - 6); }
//
// Retrieval of the 16-point patch for quad schemes:
//
template <typename POINT>
int
gatherPatchPoints4(Level const & level, Index face, IArray const & fVerts,
POINT P[], int fvar) {
static int const pointsPerCorner[4][4] = { { 5, 4, 0, 1 },
{ 6, 2, 3, 7 },
{ 10, 11, 15, 14 },
{ 9, 13, 12, 8 } };
for (int i = 0; i < 4; ++i) {
int const * corner = pointsPerCorner[i];
int vIndex = fVerts[i];
IArray vFaces = level.getVertexFaces(vIndex);
LIArray vInFace = level.getVertexFaceLocalIndices(vIndex);
if (vFaces.size() == 4) {
int iOpposite = _mod4(vFaces.FindIndexIn4Tuple(face) + 2);
Index fj = vFaces[iOpposite];
int j = vInFace[iOpposite];
IArray FV = (fvar < 0) ? level.getFaceVertices(fj) :
level.getFaceFVarValues(fj, fvar);
P[corner[0]] = FV[j];
P[corner[1]] = FV[_mod4(j + 1)];
P[corner[2]] = FV[_mod4(j + 2)];
P[corner[3]] = FV[_mod4(j + 3)];
} else if (vFaces.size() == 1) {
Index FVcorner = (fvar < 0) ? vIndex :
level.getFaceFVarValues(vFaces[0], fvar)[vInFace[0]];
P[corner[0]] = FVcorner;
P[corner[1]] = -1;
P[corner[2]] = -1;
P[corner[3]] = -1;
} else if (vFaces[0] == face) {
Index f1 = vFaces[1];
int j1 = vInFace[1];
IArray FV = (fvar < 0) ? level.getFaceVertices(f1) :
level.getFaceFVarValues(f1, fvar);
P[corner[0]] = FV[j1];
P[corner[1]] = FV[_mod4(j1 + 3)];
P[corner[2]] = -1;
P[corner[3]] = -1;
} else {
Index f0 = vFaces[0];
int j0 = vInFace[0];
IArray FV = (fvar < 0) ? level.getFaceVertices(f0) :
level.getFaceFVarValues(f0, fvar);
P[corner[0]] = FV[j0];
P[corner[1]] = -1;
P[corner[2]] = -1;
P[corner[3]] = FV[_mod4(j0 + 1)];
}
}
return 16;
}
//
// Retrieval of the 12-point patch for triangular schemes:
//
template <typename POINT>
int
gatherPatchPoints3(Level const & level, Index face, IArray const & fVerts,
POINT P[], int fvar) {
static int const pointsPerCorner[3][4] = { { 4, 3, 0, 1 },
{ 5, 2, 6, 9 },
{ 8, 11, 10, 7 } };
for (int i = 0; i < 3; ++i) {
int const * corner = pointsPerCorner[i];
int vIndex = fVerts[i];
IArray vFaces = level.getVertexFaces(vIndex);
LIArray vInFace = level.getVertexFaceLocalIndices(vIndex);
if (vFaces.size() == 6) {
int iOpposite = _mod6(vFaces.FindIndex(face) + 3);
Index f0 = vFaces[iOpposite];
int j0 = vInFace[iOpposite];
IArray FV0 = (fvar < 0) ? level.getFaceVertices(f0) :
level.getFaceFVarValues(f0, fvar);
Index f1 = vFaces[_mod6(iOpposite + 1)];
int j1 = vInFace[_mod6(iOpposite + 1)];
IArray FV1 = (fvar < 0) ? level.getFaceVertices(f1) :
level.getFaceFVarValues(f1, fvar);
P[corner[0]] = FV0[j0];
P[corner[1]] = FV0[_mod3(j0 + 1)];
P[corner[2]] = FV0[_mod3(j0 + 2)];
P[corner[3]] = FV1[_mod3(j1 + 2)];
} else if (vFaces.size() == 1) {
Index FVcorner = (fvar < 0) ? vIndex :
level.getFaceFVarValues(vFaces[0], fvar)[vInFace[0]];
P[corner[0]] = FVcorner;
P[corner[1]] = -1;
P[corner[2]] = -1;
P[corner[3]] = -1;
} else if (vFaces[0] == face) {
Index f2 = vFaces[2];
int j2 = vInFace[2];
IArray FV = (fvar < 0) ? level.getFaceVertices(f2) :
level.getFaceFVarValues(f2, fvar);
P[corner[0]] = FV[j2];
P[corner[1]] = FV[_mod3(j2 + 2)];
P[corner[2]] = -1;
P[corner[3]] = -1;
} else if (vFaces[1] == face) {
Index f0 = vFaces[0];
int j0 = vInFace[0];
IArray FV = (fvar < 0) ? level.getFaceVertices(f0) :
level.getFaceFVarValues(f0, fvar);
P[corner[0]] = FV[j0];
P[corner[1]] = -1;
P[corner[2]] = -1;
P[corner[3]] = FV[_mod3(j0 + 1)];
} else { // (vFaces[2] == face)
Index f0 = vFaces[0];
int j0 = vInFace[0];
IArray FV = (fvar < 0) ? level.getFaceVertices(f0) :
level.getFaceFVarValues(f0, fvar);
P[corner[0]] = FV[j0];
P[corner[1]] = -1;
P[corner[2]] = FV[_mod3(j0 + 1)];
P[corner[3]] = FV[_mod3(j0 + 2)];
}
}
return 12;
}
}
//
// Private method to dispatch the above patch point retrieval functions:
//
int
RefinerSurfaceFactoryBase::getFacePatchPointIndices(Index baseFace,
Index indices[], int vtxOrFVarChannel) const {
Vtr::internal::Level const & baseLevel = _mesh.getLevel(0);
ConstIndexArray baseFaceVerts = baseLevel.getFaceVertices(baseFace);
if (baseFaceVerts.size() == 4) {
return gatherPatchPoints4(baseLevel, baseFace, baseFaceVerts,
indices, vtxOrFVarChannel);
} else {
return gatherPatchPoints3(baseLevel, baseFace, baseFaceVerts,
indices, vtxOrFVarChannel);
}
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,179 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_REFINER_SURFACE_FACTORY_H
#define OPENSUBDIV3_BFR_REFINER_SURFACE_FACTORY_H
#include "../version.h"
#include "../bfr/surfaceFactory.h"
#include "../bfr/surfaceFactoryCache.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
class TopologyRefiner;
}
namespace Bfr {
///
/// @brief Intermediate subclass of SurfaceFactory with Far::TopologyRefiner
/// as the mesh
///
/// RefinerSurfaceFactoryBase is an intermediate subclass of SurfaceFactory
/// using Far::TopologyRefiner as the connected mesh representation.
///
/// The SurfaceFactoryMeshAdapter interface for TopologyRefiner is provided
/// in full, along with some public extensions specific to TopologyRefiner.
///
/// Additional caching expectations of SurfaceFactory are NOT specified
/// here. These are deferred to subclasses to implement different behaviors
/// of the factory's internal caching. A template for such subclasses is
/// additionally provided -- allowing clients desiring a thread-safe cache
/// to simply declare a subclass for a preferred thread-safe type.
///
class RefinerSurfaceFactoryBase : public SurfaceFactory {
public:
//@{
/// @name Construction and initialization
///
/// Construction and initialization
///
RefinerSurfaceFactoryBase(Far::TopologyRefiner const & mesh,
Options const & options);
~RefinerSurfaceFactoryBase() override = default;
//@}
//@{
/// @name Simple queries related to Far::TopologyRefiner
///
/// Simple queries related to Far::TopologyRefiner
///
/// @brief Return the instance of the mesh
Far::TopologyRefiner const & GetMesh() const { return _mesh; }
/// @brief Return the number of faces
int GetNumFaces() const { return _numFaces; }
/// @brief Return the number of face-varying channels
int GetNumFVarChannels() const { return _numFVarChannels; }
//@}
protected:
/// @cond PROTECTED
//
// Virtual overrides to satisfy the SurfaceFactoryMeshAdapter interface:
//
bool isFaceHole( Index faceIndex) const override;
int getFaceSize(Index faceIndex) const override;
int getFaceVertexIndices(Index faceIndex,
Index vertexIndices[]) const override;
int getFaceFVarValueIndices(Index faceIndex,
FVarID fvarID, Index fvarValueIndices[]) const override;
int populateFaceVertexDescriptor(Index faceIndex, int faceVertex,
VertexDescriptor * vertexDescriptor) const override;
int getFaceVertexIncidentFaceVertexIndices(
Index faceIndex, int faceVertex,
Index vertexIndices[]) const override;
int getFaceVertexIncidentFaceFVarValueIndices(
Index faceIndex, int faceVertex,
FVarID fvarID, Index fvarValueIndices[]) const override;
// Optional SurfaceFactoryMeshAdapter overrides for regular patches:
bool getFaceNeighborhoodVertexIndicesIfRegular(
Index faceIndex,
Index vertexIndices[]) const override;
bool getFaceNeighborhoodFVarValueIndicesIfRegular(
Index faceIndex,
FVarID fvarID, Index fvarValueIndices[]) const override;
/// @endcond
private:
//
// Internal supporting methods:
//
int getFaceVaryingChannel(FVarID fvarID) const;
int getFaceVertexPointIndices(Index faceIndex, int faceVertex,
Index indices[], int vtxOrFVarChannel) const;
int getFacePatchPointIndices(Index faceIndex,
Index indices[], int vtxOrFVarChannel) const;
private:
// Additional members for the subclass:
Far::TopologyRefiner const & _mesh;
int _numFaces;
int _numFVarChannels;
};
//
/// @brief Template for concrete subclasses of RefinerSurfaceFactoryBase
///
/// This class template is used to declare concrete subclasses of
/// RefinerSurfaceFactoryBase with the additional support of an internal
/// cache used by the base class. With an instance of a thread-safe
/// subclass of SurfaceFactoryCache declared as a member, the resulting
/// factory will be thread-safe.
///
/// @tparam CACHE_TYPE A subclass of SurfaceFactoryCache
///
/// Note a default template parameter uses the base SurfaceFactoryCache
/// for convenience, but which is not thread-safe.
///
template <class CACHE_TYPE = SurfaceFactoryCache>
class RefinerSurfaceFactory : public RefinerSurfaceFactoryBase {
public:
RefinerSurfaceFactory(Far::TopologyRefiner const & mesh,
Options const & options = Options()) :
RefinerSurfaceFactoryBase(mesh, options),
_localCache() {
SurfaceFactory::setInternalCache(&_localCache);
}
~RefinerSurfaceFactory() override = default;
private:
CACHE_TYPE _localCache;
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_REFINER_SURFACE_FACTORY_H */

View File

@ -0,0 +1,441 @@
//
// Copyright 2021 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.
//
#include "../bfr/regularPatchBuilder.h"
#include <cstring>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Internal helper functions:
//
namespace {
inline int
encodeTriBoundaryMask(int eBits, int vBits) {
int upperBits = 0;
int lowerBits = eBits;
if (vBits) {
if (eBits == 0) {
upperBits = 1;
lowerBits = vBits;
} else if ((vBits == 7) &&
((eBits == 1) || (eBits == 2) || (eBits == 4))) {
upperBits = 2;
lowerBits = eBits;
}
}
return (upperBits << 3) | lowerBits;
}
}
//
// Static methods:
//
int
RegularPatchBuilder::GetBoundaryMask(int faceSize, Index const cvs[]) {
bool isQuad = (faceSize == 4);
if (isQuad) {
int eMask = ((cvs[ 1] < 0) << 0) |
((cvs[ 7] < 0) << 1) |
((cvs[14] < 0) << 2) |
((cvs[ 8] < 0) << 3);
return eMask;
} else {
int eMask = ((cvs[ 1] < 0) << 0) |
((cvs[ 9] < 0) << 1) |
((cvs[ 7] < 0) << 2) ;
int vMask = (((cvs[ 0] < 0) | (cvs[ 3] < 0)) << 0) |
(((cvs[ 2] < 0) | (cvs[ 6] < 0)) << 1) |
(((cvs[10] < 0) | (cvs[11] < 0)) << 2) ;
return encodeTriBoundaryMask(eMask, vMask);
}
}
//
// Constructor (empty destructor is inline):
//
RegularPatchBuilder::RegularPatchBuilder(FaceSurface const & surface) :
_surface(surface) {
_isQuad = (_surface.GetTopology()._faceSize == 4);
if (_isQuad) {
assert(_surface.GetTopology()._regFaceSize == 4);
_patchType = Far::PatchDescriptor::REGULAR;
_patchSize = 16;
} else {
assert(_surface.GetTopology()._faceSize == 3);
assert(_surface.GetTopology()._regFaceSize == 3);
_patchType = Far::PatchDescriptor::LOOP;
_patchSize = 12;
}
_isBoundary = _surface.GetTag().HasBoundaryVertices();
if (!_isBoundary) {
_boundaryMask = 0;
} else if (_isQuad) {
// Boundary mask for quad trivial -- bit for each boundary edge:
FaceVertexSubset const * C = _surface.GetSubsets();
int eMask = ((C[0].IsBoundary() & (C[0]._numFacesBefore == 0)) << 0) |
((C[1].IsBoundary() & (C[1]._numFacesBefore == 0)) << 1) |
((C[2].IsBoundary() & (C[2]._numFacesBefore == 0)) << 2) |
((C[3].IsBoundary() & (C[3]._numFacesBefore == 0)) << 3);
_boundaryMask = eMask;
} else {
// Boundary mask for tris not so trivial -- boundary verts can exist
// on tris without boundary edges, so bits for both are combined:
FaceVertexSubset const * C = _surface.GetSubsets();
int eMask = ((C[0].IsBoundary() & (C[0]._numFacesBefore == 0)) << 0) |
((C[1].IsBoundary() & (C[1]._numFacesBefore == 0)) << 1) |
((C[2].IsBoundary() & (C[2]._numFacesBefore == 0)) << 2);
int vMask = (C[0].IsBoundary() << 0) |
(C[1].IsBoundary() << 1) |
(C[2].IsBoundary() << 2);
_boundaryMask = encodeTriBoundaryMask(eMask, vMask);
}
}
//
// Methods for gathering control vertices:
//
void
RegularPatchBuilder::gatherInteriorPatchPoints4(Index P[]) const {
Index const * fvIndices = &_surface.GetIndices()[0];
Index const * fvOpposite = 0;
//
// For each of the 4 corners, identify the opposite face in the ring
// and assign its 4 indices to the corresponding quadrant of the patch:
//
FaceVertex const & cTop0 = _surface.GetCornerTopology(0);
fvOpposite = fvIndices + cTop0.GetFaceIndexOffset(cTop0.GetFaceAfter(2));
P[ 5] = fvOpposite[0];
P[ 4] = fvOpposite[1];
P[ 0] = fvOpposite[2];
P[ 1] = fvOpposite[3];
fvIndices += cTop0.GetNumFaceVertices();
FaceVertex const & cTop1 = _surface.GetCornerTopology(1);
fvOpposite = fvIndices + cTop1.GetFaceIndexOffset(cTop1.GetFaceAfter(2));
P[ 6] = fvOpposite[0];
P[ 2] = fvOpposite[1];
P[ 3] = fvOpposite[2];
P[ 7] = fvOpposite[3];
fvIndices += cTop1.GetNumFaceVertices();
FaceVertex const & cTop2 = _surface.GetCornerTopology(2);
fvOpposite = fvIndices + cTop2.GetFaceIndexOffset(cTop2.GetFaceAfter(2));
P[10] = fvOpposite[0];
P[11] = fvOpposite[1];
P[15] = fvOpposite[2];
P[14] = fvOpposite[3];
fvIndices += cTop2.GetNumFaceVertices();
FaceVertex const & cTop3 = _surface.GetCornerTopology(3);
fvOpposite = fvIndices + cTop3.GetFaceIndexOffset(cTop3.GetFaceAfter(2));
P[ 9] = fvOpposite[0];
P[13] = fvOpposite[1];
P[12] = fvOpposite[2];
P[ 8] = fvOpposite[3];
}
void
RegularPatchBuilder::gatherBoundaryPatchPoints4(Index P[]) const {
Index const * fvIndices = &_surface.GetIndices()[0];
//
// For each of the 4 corners -- whether boundary or interior -- one
// incident face contains all indices that will contribute to the points
// of the corresponding patch. Identify it first and then retrieve and
// assign the indices accordingly:
//
for (int i = 0; i < 4; ++i) {
FaceVertex const & cTop = _surface.GetCornerTopology(i);
FaceVertexSubset const & cSub = _surface.GetCornerSubset(i);
int faceCorner = cTop.GetFace();
int faceOther = faceCorner;
if (!cSub.IsBoundary()) {
faceOther = cTop.GetFaceAfter(2);
} else if (cSub._numFacesAfter) {
faceOther = cTop.GetFaceNext(faceCorner);
} else if (cSub._numFacesBefore) {
faceOther = cTop.GetFacePrevious(faceCorner);
}
Index const * fvOther = fvIndices + cTop.GetFaceIndexOffset(faceOther);
Index fvPhantom = fvOther[0];
switch (i) {
case 0:
P[5] = fvOther[0];
if (!cSub.IsBoundary()) {
P[4] = fvOther[1];
P[0] = fvOther[2];
P[1] = fvOther[3];
} else {
P[4] = cSub._numFacesAfter ? fvOther[3] : fvPhantom;
P[0] = fvPhantom;
P[1] = cSub._numFacesBefore ? fvOther[1] : fvPhantom;
}
break;
case 1:
P[6] = fvOther[0];
if (!cSub.IsBoundary()) {
P[2] = fvOther[1];
P[3] = fvOther[2];
P[7] = fvOther[3];
} else {
P[2] = cSub._numFacesAfter ? fvOther[3] : fvPhantom;
P[3] = fvPhantom;
P[7] = cSub._numFacesBefore ? fvOther[1] : fvPhantom;
}
break;
case 2:
P[10] = fvOther[0];
if (!cSub.IsBoundary()) {
P[11] = fvOther[1];
P[15] = fvOther[2];
P[14] = fvOther[3];
} else {
P[11] = cSub._numFacesAfter ? fvOther[3] : fvPhantom;
P[15] = fvPhantom;
P[14] = cSub._numFacesBefore ? fvOther[1] : fvPhantom;
}
break;
case 3:
P[ 9] = fvOther[0];
if (!cSub.IsBoundary()) {
P[13] = fvOther[1];
P[12] = fvOther[2];
P[ 8] = fvOther[3];
} else {
P[13] = cSub._numFacesAfter ? fvOther[3] : fvPhantom;
P[12] = fvPhantom;
P[ 8] = cSub._numFacesBefore ? fvOther[1] : fvPhantom;
}
break;
}
fvIndices += cTop.GetNumFaceVertices();
}
}
void
RegularPatchBuilder::gatherInteriorPatchPoints3(Index P[]) const {
Index const * fvIndices = &_surface.GetIndices()[0];
//
// For each of the 3 corners, the indices for the four contributing
// points come from the 2nd and 3rd faces following the corner face:
//
Index const * fvNext2 = 0;
Index const * fvNext3 = 0;
FaceVertex const & cTop0 = _surface.GetCornerTopology(0);
fvNext2 = fvIndices + cTop0.GetFaceIndexOffset(cTop0.GetFaceAfter(2));
fvNext3 = fvIndices + cTop0.GetFaceIndexOffset(cTop0.GetFaceAfter(3));
P[ 4] = fvNext2[0];
P[ 7] = fvNext2[1];
P[ 3] = fvNext2[2];
P[ 0] = fvNext3[2];
fvIndices += cTop0.GetNumFaceVertices();
FaceVertex const & cTop1 = _surface.GetCornerTopology(1);
fvNext2 = fvIndices + cTop1.GetFaceIndexOffset(cTop1.GetFaceAfter(2));
fvNext3 = fvIndices + cTop1.GetFaceIndexOffset(cTop1.GetFaceAfter(3));
P[ 5] = fvNext2[0];
P[ 1] = fvNext2[1];
P[ 2] = fvNext2[2];
P[ 6] = fvNext3[2];
fvIndices += cTop1.GetNumFaceVertices();
FaceVertex const & cTop2 = _surface.GetCornerTopology(2);
fvNext2 = fvIndices + cTop2.GetFaceIndexOffset(cTop2.GetFaceAfter(2));
fvNext3 = fvIndices + cTop2.GetFaceIndexOffset(cTop2.GetFaceAfter(3));
P[ 8] = fvNext2[0];
P[ 9] = fvNext2[1];
P[11] = fvNext2[2];
P[10] = fvNext3[2];
}
void
RegularPatchBuilder::gatherBoundaryPatchPoints3(Index P[]) const {
Index const * fvIndices = &_surface.GetIndices()[0];
//
// For each of the 3 corners, one incident face contains all indices
// that will contribute to the points of the corresponding patch, but
// interior vertices require two:
//
for (int i = 0; i < 3; ++i) {
FaceVertex const & cTop = _surface.GetCornerTopology(i);
FaceVertexSubset const & cSub = _surface.GetCornerSubset(i);
int faceCorner = cTop.GetFace();
int faceOther = -1;
if (!cSub.IsBoundary()) {
faceOther = cTop.GetFaceAfter(2);
} else if (cSub._numFacesTotal == 1) {
faceOther = faceCorner;
} else if (cSub._numFacesBefore == 0) {
faceOther = cTop.GetFaceAfter(2);
} else if (cSub._numFacesAfter == 0) {
faceOther = cTop.GetFaceBefore(2);
} else {
faceOther = cTop.GetFaceNext(faceCorner);
}
assert(faceOther >= 0);
Index const * fvOther = fvIndices + cTop.GetFaceIndexOffset(faceOther);
Index fvPhantom = fvOther[0];
switch (i) {
case 0:
P[4] = fvOther[0];
if (!cSub.IsBoundary()) {
P[7] = fvOther[1];
P[3] = fvOther[2];
fvOther = fvIndices +
cTop.GetFaceIndexOffset(cTop.GetFaceNext(faceOther));
P[0] = fvOther[2];
} else {
P[7] = (cSub._numFacesAfter) ? fvOther[3 - cSub._numFacesAfter]
: fvPhantom;
P[3] = (cSub._numFacesAfter == 2) ? fvOther[2] : fvPhantom;
P[0] = (cSub._numFacesBefore == 2) ? fvOther[1] : fvPhantom;
}
break;
case 1:
P[5] = fvOther[0];
if (!cSub.IsBoundary()) {
P[1] = fvOther[1];
P[2] = fvOther[2];
fvOther = fvIndices +
cTop.GetFaceIndexOffset(cTop.GetFaceNext(faceOther));
P[6] = fvOther[2];
} else {
P[1] = (cSub._numFacesAfter) ? fvOther[3 - cSub._numFacesAfter]
: fvPhantom;
P[2] = (cSub._numFacesAfter == 2) ? fvOther[2] : fvPhantom;
P[6] = (cSub._numFacesBefore == 2) ? fvOther[1] : fvPhantom;
}
break;
case 2:
P[8] = fvOther[0];
if (!cSub.IsBoundary()) {
P[ 9] = fvOther[1];
P[11] = fvOther[2];
fvOther = fvIndices +
cTop.GetFaceIndexOffset(cTop.GetFaceNext(faceOther));
P[10] = fvOther[2];
} else {
P[ 9] = (cSub._numFacesAfter) ? fvOther[3 - cSub._numFacesAfter]
: fvPhantom;
P[11] = (cSub._numFacesAfter == 2) ? fvOther[2] : fvPhantom;
P[10] = (cSub._numFacesBefore == 2) ? fvOther[1] : fvPhantom;
}
break;
}
fvIndices += cTop.GetNumFaceVertices();
}
}
int
RegularPatchBuilder::GatherControlVertexIndices(Index cvIndices[]) const {
if (_isQuad) {
if (_isBoundary) {
gatherBoundaryPatchPoints4(cvIndices);
} else {
gatherInteriorPatchPoints4(cvIndices);
}
} else {
if (_isBoundary) {
gatherBoundaryPatchPoints3(cvIndices);
} else {
gatherInteriorPatchPoints3(cvIndices);
}
}
return _patchSize;
}
//
// Methods for debugging...
//
void
RegularPatchBuilder::print(Index const P[]) const {
printf("RegularPatchBuilder:\n");
if (_patchType == Far::PatchDescriptor::REGULAR) {
printf(" patch type = REGULAR (B-Spline, quad)\n");
} else if (_patchType == Far::PatchDescriptor::LOOP) {
printf(" patch type = LOOP (Box-Spline, tri)\n");
} else {
assert("Unknown _patchType for RegularPatchBuilder" == 0);
}
printf(" patch size = %d\n", _patchSize);
printf(" is quad = %d\n", _isQuad);
printf(" is boundary = %d\n", _isBoundary);
if (P) {
const char * label = " patch points:";
const char * indent = " ";
if (_isQuad) {
printf("%s %4d %4d %4d %4d\n", label, P[12], P[13], P[14], P[15]);
printf("%s %4d %4d %4d %4d\n", indent, P[ 8], P[ 9], P[10], P[11]);
printf("%s %4d %4d %4d %4d\n", indent, P[ 4], P[ 5], P[ 6], P[ 7]);
printf("%s %4d %4d %4d %4d\n", indent, P[ 0], P[ 1], P[ 2], P[ 3]);
} else {
printf("%s %4d %4d\n", label, P[10], P[11]);
printf("%s %4d %4d %4d\n", indent, P[7], P[8], P[9]);
printf("%s %4d %4d %4d %4d\n", indent, P[3], P[4], P[5], P[6]);
printf("%s %4d %4d %4d\n", indent, P[0], P[1], P[2]);
}
}
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,106 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_REGULAR_PATCH_BUILDER_H
#define OPENSUBDIV3_REGULAR_PATCH_BUILDER_H
#include "../version.h"
#include "../bfr/faceSurface.h"
#include "../far/patchDescriptor.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// RegularPatchBuilder ...
//
class RegularPatchBuilder {
public:
typedef FaceSurface::Index Index;
public:
RegularPatchBuilder(FaceSurface const & surfaceDescription);
~RegularPatchBuilder() { }
// Debugging...
void print(Index const cvIndices[] = 0) const;
public:
// Methods to query the number and indices of control vertices:
int GetNumControlVertices() const { return _patchSize; }
int GatherControlVertexIndices(Index cvIndices[]) const;
public:
// Methods to query patch properties:
bool IsQuadPatch() const { return _isQuad; }
bool IsBoundaryPatch() const { return _isBoundary; }
Far::PatchDescriptor::Type GetPatchType() const { return _patchType; }
// Note the bit-mask here is specific for use with Far::PatchParam
int GetPatchParamBoundaryMask() const { return _boundaryMask; }
public:
// Static methods for use without a FaceSurface:
static int GetPatchSize(int regFaceSize) {
return (regFaceSize == 4) ? 16 : 12;
}
static Far::PatchDescriptor::Type GetPatchType(int regFaceSize) {
return (regFaceSize == 4) ? Far::PatchDescriptor::REGULAR :
Far::PatchDescriptor::LOOP;
}
static int GetBoundaryMask(int regFaceSize, Index const patchPoints[]);
private:
// Internal methods for assembling quad and tri patches:
void gatherInteriorPatchPoints4(Index cvIndices[]) const;
void gatherBoundaryPatchPoints4(Index cvIndices[]) const;
void gatherInteriorPatchPoints3(Index cvIndices[]) const;
void gatherBoundaryPatchPoints3(Index cvIndices[]) const;
private:
// Private members:
FaceSurface const & _surface;
unsigned int _isQuad : 1;
unsigned int _isBoundary : 1;
int _boundaryMask;
int _patchSize;
Far::PatchDescriptor::Type _patchType;
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_REGULAR_PATCH_BUILDER_H */

697
opensubdiv/bfr/surface.cpp Normal file
View File

@ -0,0 +1,697 @@
//
// Copyright 2021 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.
//
#include "../bfr/surface.h"
#include "../bfr/surfaceData.h"
#include "../bfr/pointOperations.h"
#include "../bfr/patchTree.h"
#include "../far/patchParam.h"
#include "../far/patchDescriptor.h"
#include "../far/patchBasis.h"
#include <algorithm>
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Constructor for the Surface -- defers to the constructor for its full
// set of member variables, but marks the precision as double when needed
// (as a specialization here):
//
template <typename REAL>
Surface<REAL>::Surface() : _data() {
// Surface<> should not be adding members outside its SurfaceData:
assert(sizeof(*this) == sizeof(internal::SurfaceData));
}
template <>
Surface<double>::Surface() : _data() {
_data.setDouble(true);
}
//
// Simple internal utilities:
//
template <typename REAL>
inline internal::IrregularPatchType const &
Surface<REAL>::getIrregPatch() const {
return _data.getIrregPatch();
}
template <typename REAL>
int
Surface<REAL>::GetNumPatchPoints() const {
if (IsRegular()) {
return GetNumControlPoints();
} else if (IsLinear()) {
return 2 * GetNumControlPoints() + 1;
} else {
return getIrregPatch().GetNumPointsTotal();
}
}
template <typename REAL>
int
Surface<REAL>::GetControlPointIndices(Index cvs[]) const {
std::memcpy(cvs, _data.getCVIndices(), _data.getNumCVs() * sizeof(Index));
return _data.getNumCVs();
}
//
// Methods for gathering and computing control and patch points:
//
template <typename REAL>
template <typename REAL_MESH>
void
Surface<REAL>::GatherControlPoints(
REAL_MESH const meshPoints[], PointDescriptor const & meshDesc,
REAL controlPoints[], PointDescriptor const & controlDesc) const {
//
// Assemble parameters of the point copy operation and apply:
//
typedef points::CopyConsecutive<REAL,REAL_MESH> PointCopier;
typename PointCopier::Parameters copyParams;
copyParams.pointData = meshPoints;
copyParams.pointSize = meshDesc.size;
copyParams.pointStride = meshDesc.stride;
copyParams.srcCount = GetNumControlPoints();
copyParams.srcIndices = _data.getCVIndices();
copyParams.resultData = controlPoints;
copyParams.resultStride = controlDesc.stride;
PointCopier::Apply(copyParams);
}
template <typename REAL>
void
Surface<REAL>::computeLinearPatchPoints(REAL pointData[],
PointDescriptor const & pointDesc) const {
//
// The initial control points of the N-sided face will be followed
// by the midpoint of the face and the midpoint of the N edges.
//
// Assemble parameters of the face splitting operation and apply:
//
int N = GetNumControlPoints();
typedef points::SplitFace<REAL> PointSplitter;
typename PointSplitter::Parameters splitParams;
splitParams.pointData = pointData;
splitParams.pointSize = pointDesc.size;
splitParams.pointStride = pointDesc.stride;
splitParams.srcCount = N;
splitParams.resultData = pointData + pointDesc.stride * N;
PointSplitter::Apply(splitParams);
}
template <typename REAL>
void
Surface<REAL>::computeIrregularPatchPoints(REAL pointData[],
PointDescriptor const & pointDesc) const {
//
// An "irregular patch" may be represented by a regular patch in
// rare cases, so be sure there are patch points to compute:
//
internal::IrregularPatchType const & irregPatch = getIrregPatch();
int numControlPoints = GetNumControlPoints();
int numPatchPoints = irregPatch.GetNumPointsTotal();
if (numPatchPoints == numControlPoints) return;
//
// Assemble parameters of the point combination operation and apply:
//
typedef points::CombineConsecutive<REAL> PointCombiner;
typename PointCombiner::Parameters combParams;
combParams.pointData = pointData;
combParams.pointSize = pointDesc.size;
combParams.pointStride = pointDesc.stride;
combParams.srcCount = numControlPoints;
combParams.resultCount = numPatchPoints - numControlPoints;
combParams.resultData = pointData + pointDesc.stride * numControlPoints;
combParams.weightData = irregPatch.GetStencilMatrix<REAL>();
PointCombiner::Apply(combParams);
}
//
// Methods for computing the extent of the control points:
//
template <typename REAL>
void
Surface<REAL>::BoundControlPoints(
REAL const controlPoints[], PointDescriptor const & pointDesc,
REAL boundMin[], REAL boundMax[]) const {
int numPoints = GetNumControlPoints();
int pointSize = pointDesc.size;
REAL const * p = controlPoints;
std::memcpy(boundMin, p, pointSize * sizeof(REAL));
std::memcpy(boundMax, p, pointSize * sizeof(REAL));
for (int i = 1; i < numPoints; ++i) {
p += pointDesc.stride;
for (int j = 0; j < pointSize; ++j) {
boundMin[j] = std::min(boundMin[j], p[j]);
boundMax[j] = std::max(boundMax[j], p[j]);
}
}
}
template <typename REAL>
void
Surface<REAL>::BoundControlPointsFromMesh(
REAL const meshPoints[], PointDescriptor const & pointDesc,
REAL boundMin[], REAL boundMax[]) const {
int numPoints = GetNumControlPoints();
int pointSize = pointDesc.size;
int const * meshIndices = _data.getCVIndices();
REAL const * p = meshPoints + pointDesc.stride * meshIndices[0];
std::memcpy(boundMin, p, pointSize * sizeof(REAL));
std::memcpy(boundMax, p, pointSize * sizeof(REAL));
for (int i = 1; i < numPoints; ++i) {
p = meshPoints + pointDesc.stride * meshIndices[i];
for (int j = 0; j < pointSize; ++j) {
boundMin[j] = std::min(boundMin[j], p[j]);
boundMax[j] = std::max(boundMax[j], p[j]);
}
}
}
//
// Internal helper for evaluation:
//
namespace {
template <typename REAL>
inline int
assignWeightsPerDeriv(REAL * const deriv[6], int wSize, REAL wBuffer[],
REAL * wDeriv[6]) {
std::memset(wDeriv, 0, 6 * sizeof(REAL*));
wDeriv[0] = wBuffer;
if (deriv[1] && deriv[2]) {
wDeriv[1] = wDeriv[0] + wSize;
wDeriv[2] = wDeriv[1] + wSize;
if (deriv[3] && deriv[4] && deriv[5]) {
wDeriv[3] = wDeriv[2] + wSize;
wDeriv[4] = wDeriv[3] + wSize;
wDeriv[5] = wDeriv[4] + wSize;
return 6;
}
return 3;
}
return 1;
}
}
//
// Evaluation methods accessing the local data for a simple regular patch:
//
template <typename REAL>
void
Surface<REAL>::evalRegularBasis(REAL const uv[2], REAL * wDeriv[]) const {
Far::PatchParam patchParam;
patchParam.Set(0, 0, 0, 0, 0, getRegPatchMask(), 0, true);
Far::internal::EvaluatePatchBasisNormalized(
getRegPatchType(), patchParam, uv[0], uv[1],
wDeriv[0], wDeriv[1], wDeriv[2], wDeriv[3], wDeriv[4], wDeriv[5]);
}
template <typename REAL>
int
Surface<REAL>::evalRegularStencils(REAL const uv[2], REAL * sDeriv[]) const {
//
// The control points of a regular patch are always the full set
// of points required by a patch, i.e. phantom points will have an
// entry of some kind (a duplicate). For example, for an isolated
// quad, its regular patch still has 16 control points. So we can
// return the basis weights as stencil weights for all cases.
//
Far::PatchParam patchParam;
patchParam.Set(0, 0, 0, 0, 0, getRegPatchMask(), 0, true);
Far::internal::EvaluatePatchBasisNormalized(
getRegPatchType(), patchParam, uv[0], uv[1],
sDeriv[0], sDeriv[1], sDeriv[2], sDeriv[3], sDeriv[4], sDeriv[5]);
return GetNumControlPoints();
}
template <typename REAL>
void
Surface<REAL>::evalRegularDerivs(REAL const uv[2],
REAL const patchPoints[], PointDescriptor const & pointDesc,
REAL * deriv[]) const {
//
// Regular basis evaluation simply returns weights for use with
// the entire set of patch control points.
//
// Assign weights for requested derivatives and evaluate:
//
REAL wBuffer[6 * 20];
REAL * wDeriv[6];
int numDerivs = assignWeightsPerDeriv(deriv, 20, wBuffer, wDeriv);
evalRegularBasis(uv, wDeriv);
//
// Assemble parameters of the point combination operation and apply:
//
points::CommonCombinationParameters<REAL> combineParams;
combineParams.pointData = patchPoints;
combineParams.pointSize = pointDesc.size;
combineParams.pointStride = pointDesc.stride;
combineParams.srcCount = GetNumControlPoints();
combineParams.srcIndices = 0;
combineParams.resultCount = numDerivs;
combineParams.resultArray = deriv;
combineParams.weightArray = wDeriv;
if (numDerivs == 1) {
points::Combine1<REAL>::Apply(combineParams);
} else if (numDerivs == 3) {
points::Combine3<REAL>::Apply(combineParams);
} else {
points::CombineMultiple<REAL>::Apply(combineParams);
}
}
//
// Evaluation methods accessing the PatchTree for irregular patches:
//
template <typename REAL>
typename Surface<REAL>::IndexArray
Surface<REAL>::evalIrregularBasis(REAL const UV[2], REAL * wDeriv[]) const {
Parameterization param = GetParameterization();
REAL uv[2] = { UV[0], UV[1] };
int subFace = param.HasSubFaces() ?
param.ConvertCoordToNormalizedSubFace(uv, uv) : 0;
internal::IrregularPatchType const & irregPatch = getIrregPatch();
int subPatchIndex = irregPatch.FindSubPatch(uv[0], uv[1], subFace);
assert(subPatchIndex >= 0);
irregPatch.EvalSubPatchBasis(subPatchIndex, uv[0], uv[1],
wDeriv[0], wDeriv[1], wDeriv[2], wDeriv[3], wDeriv[4], wDeriv[5]);
return irregPatch.GetSubPatchPoints(subPatchIndex);
}
template <typename REAL>
int
Surface<REAL>::evalIrregularStencils(REAL const UV[2], REAL * sDeriv[]) const {
Parameterization param = GetParameterization();
REAL uv[2] = { UV[0], UV[1] };
int subFace = param.HasSubFaces() ?
param.ConvertCoordToNormalizedSubFace(uv, uv) : 0;
internal::IrregularPatchType const & irregPatch = getIrregPatch();
int subPatchIndex = irregPatch.FindSubPatch(uv[0], uv[1], subFace);
assert(subPatchIndex >= 0);
return irregPatch.EvalSubPatchStencils(
subPatchIndex, uv[0], uv[1],
sDeriv[0], sDeriv[1], sDeriv[2], sDeriv[3], sDeriv[4], sDeriv[5]);
}
template <typename REAL>
void
Surface<REAL>::evalIrregularDerivs(REAL const uv[2],
REAL const patchPoints[], PointDescriptor const & pointDesc,
REAL * deriv[]) const {
//
// Non-linear irregular basis evaluation returns both the weights
// and the corresponding points of a sub-patch defined by a subset
// of the given patch points.
//
// Assign weights for requested derivatives and evaluate:
//
REAL wBuffer[6 * 20];
REAL * wDeriv[6];
int numDerivs = assignWeightsPerDeriv(deriv, 20, wBuffer, wDeriv);
IndexArray indices = evalIrregularBasis(uv, wDeriv);
//
// Assemble parameters of the point combination operation and apply:
//
points::CommonCombinationParameters<REAL> combineParams;
combineParams.pointData = patchPoints;
combineParams.pointSize = pointDesc.size;
combineParams.pointStride = pointDesc.stride;
combineParams.srcCount = indices.size();
combineParams.srcIndices = &indices[0];
combineParams.resultCount = numDerivs;
combineParams.resultArray = deriv;
combineParams.weightArray = wDeriv;
if (numDerivs == 1) {
points::Combine1<REAL>::Apply(combineParams);
} else if (numDerivs == 3) {
points::Combine3<REAL>::Apply(combineParams);
} else {
points::CombineMultiple<REAL>::Apply(combineParams);
}
}
//
// Supporting methods for the N-sided quadrangulated linear patch:
//
namespace {
//
// For stencils, there are four unique weights derived from the
// four bilinear weights of the sub-face. Given these weights as
// input for a sub-face with origin at base point P, the resulting
// weights are associated with the N base points as follows:
//
// w[0] = the point at the origin (P)
// w[1] = the point following P
// w[2] = the N-3 points not adjacent to P (contributing to center)
// w[3] = the point preceding P
//
template <typename REAL>
inline void
transformLinearQuadWeightsToStencil(REAL w[4], int N) {
REAL wOrigin = w[0];
REAL wNext = w[1] * 0.5f;
REAL wCenter = w[2] / (REAL)N;
REAL wPrev = w[3] * 0.5f;
w[0] = wCenter + wNext + wPrev + wOrigin;
w[1] = wCenter + wNext;
w[2] = wCenter;
w[3] = wCenter + wPrev;
}
template <typename REAL>
inline void
scaleWeights4(REAL w[4], REAL derivScale) {
if (w) {
w[0] *= derivScale;
w[1] *= derivScale;
w[2] *= derivScale;
w[3] *= derivScale;
}
}
}
template <typename REAL>
int
Surface<REAL>::evalMultiLinearBasis(REAL const UV[2], REAL *wDeriv[]) const {
Parameterization param = GetParameterization();
assert(param.GetType() == Parameterization::QUAD_SUBFACES);
REAL uv[2];
int subFace = param.ConvertCoordToNormalizedSubFace(UV, uv);
// WIP - Prefer to eval Linear basis directly, i.e.:
//
// Far::internal::EvalBasisLinear(u, v, wP, wDu, wDv);
//
// but this internal Far function is sometimes optimized out, causing
// link errors. Need to fix in Far with explicit instantiation...
Far::internal::EvaluatePatchBasisNormalized(Far::PatchDescriptor::QUADS,
Far::PatchParam(), uv[0], uv[1],
wDeriv[0], wDeriv[1], wDeriv[2], wDeriv[3], wDeriv[4], wDeriv[5]);
// Scale weights for derivatives (only mixed partial of 2nd is non-zero):
scaleWeights4<REAL>(wDeriv[1], 2.0f);
scaleWeights4<REAL>(wDeriv[2], 2.0f);
scaleWeights4<REAL>(wDeriv[4], 4.0f);
return subFace;
}
template <typename REAL>
int
Surface<REAL>::evalMultiLinearStencils(REAL const uv[2], REAL *sDeriv[]) const {
//
// Linear evaluation of irregular N-sided faces evaluates one of N
// locally subdivided quad faces and identifies that sub-face -- also
// the origin vertex of the quad. The basis weights are subsequently
// transformed into the four unique values that are then assigned to
// the N vertices of the face.
//
// Assign weights for requested stencils and evaluate:
//
REAL wBuffer[6 * 4];
REAL * wDeriv[6];
int numDerivs = assignWeightsPerDeriv(sDeriv, 4, wBuffer, wDeriv);
int iOrigin = evalMultiLinearBasis(uv, wDeriv);
//
// Transform the four linear weights to four unique stencil weights:
//
int numControlPoints = GetNumControlPoints();
transformLinearQuadWeightsToStencil(wDeriv[0], numControlPoints);
if (numDerivs > 1) {
transformLinearQuadWeightsToStencil(wDeriv[1], numControlPoints);
transformLinearQuadWeightsToStencil(wDeriv[2], numControlPoints);
if (numDerivs > 3) {
transformLinearQuadWeightsToStencil(wDeriv[4], numControlPoints);
}
}
//
// Assign the N stencil weights from the four unique values:
//
int iNext = (iOrigin + 1) % numControlPoints;
int iPrev = (iOrigin + numControlPoints - 1) % numControlPoints;
for (int i = 0; i < numControlPoints; ++i) {
int wIndex = 2;
if (i == iOrigin) {
wIndex = 0;
} else if (i == iNext) {
wIndex = 1;
} else if (i == iPrev) {
wIndex = 3;
}
sDeriv[0][i] = wDeriv[0][wIndex];
if (numDerivs > 1) {
sDeriv[1][i] = wDeriv[1][wIndex];
sDeriv[2][i] = wDeriv[2][wIndex];
if (numDerivs > 3) {
sDeriv[3][i] = 0.0f;
sDeriv[4][i] = wDeriv[4][wIndex];
sDeriv[5][i] = 0.0f;
}
}
}
return numControlPoints;
}
template <typename REAL>
void
Surface<REAL>::evalMultiLinearDerivs(REAL const uv[],
REAL const patchPoints[], PointDescriptor const & pointDesc,
REAL * deriv[]) const {
//
// Linear evaluation of irregular N-sided faces evaluates one of N
// locally subdivided quad faces and identifies that sub-face.
//
// Assign weights for requested derivatives and evaluate:
//
REAL wBuffer[6 * 4];
REAL * wDeriv[6];
int numDerivs = assignWeightsPerDeriv(deriv, 4, wBuffer, wDeriv);
int subQuad = evalMultiLinearBasis(uv, wDeriv);
//
// Identify the patch points for the sub-face and interpolate:
//
int N = GetNumControlPoints();
int quadIndices[4];
quadIndices[0] = subQuad;
quadIndices[1] = N + 1 + subQuad;
quadIndices[2] = N;
quadIndices[3] = N + 1 + (subQuad + N - 1) % N;
//
// Assemble parameters of the point combination operation and apply:
//
points::CommonCombinationParameters<REAL> combineParams;
combineParams.pointData = patchPoints;
combineParams.pointSize = pointDesc.size;
combineParams.pointStride = pointDesc.stride;
combineParams.srcCount = 4;
combineParams.srcIndices = quadIndices;
combineParams.resultCount = numDerivs;
combineParams.resultArray = deriv;
combineParams.weightArray = wDeriv;
if (numDerivs == 1) {
points::Combine1<REAL>::Apply(combineParams);
} else if (numDerivs == 3) {
points::Combine3<REAL>::Apply(combineParams);
} else {
points::CombineMultiple<REAL>::Apply(combineParams);
}
}
//
// Public methods to apply stencils:
//
template <typename REAL>
void
Surface<REAL>::ApplyStencilFromMesh(REAL const stencil[],
REAL const meshPoints[], PointDescriptor const & pointDesc,
REAL result[]) const {
//
// Assemble parameters of the point combination operation and apply:
//
typedef points::Combine1<REAL> PointCombiner;
typename PointCombiner::Parameters combParams;
combParams.pointData = meshPoints;
combParams.pointSize = pointDesc.size;
combParams.pointStride = pointDesc.stride;
combParams.srcCount = GetNumControlPoints();
combParams.srcIndices = _data.getCVIndices();
combParams.resultCount = 1;
combParams.resultArray = &result;
combParams.weightArray = &stencil;
PointCombiner::Apply(combParams);
}
template <typename REAL>
void
Surface<REAL>::ApplyStencil(REAL const stencil[],
REAL const controlPoints[], PointDescriptor const & pointDesc,
REAL result[]) const {
//
// Assemble parameters of the point combination operation and apply:
//
typedef points::Combine1<REAL> PointCombiner;
typename PointCombiner::Parameters combParams;
combParams.pointData = controlPoints;
combParams.pointSize = pointDesc.size;
combParams.pointStride = pointDesc.stride;
combParams.srcCount = GetNumControlPoints();
combParams.srcIndices = 0;
combParams.resultCount = 1;
combParams.resultArray = &result;
combParams.weightArray = &stencil;
PointCombiner::Apply(combParams);
}
//
// Explicitly instantiate Surface<> implementations for float and double:
//
template class Surface<float>;
template class Surface<double>;
//
// Explicitly instantiate template methods for converting precision:
//
template void Surface<float>::GatherControlPoints(
float const [], PointDescriptor const &,
float [], PointDescriptor const &) const;
template void Surface<float>::GatherControlPoints(
double const [], PointDescriptor const &,
float [], PointDescriptor const &) const;
template void Surface<double>::GatherControlPoints(
double const [], PointDescriptor const &,
double [], PointDescriptor const &) const;
template void Surface<double>::GatherControlPoints(
float const [], PointDescriptor const &,
double [], PointDescriptor const &) const;
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

446
opensubdiv/bfr/surface.h Normal file
View File

@ -0,0 +1,446 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_SURFACE_H
#define OPENSUBDIV3_BFR_SURFACE_H
#include "../version.h"
#include "../bfr/surfaceData.h"
#include "../bfr/parameterization.h"
#include "../vtr/array.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
///
/// @brief Encapsulates the limit surface for a face of a mesh
///
/// The Surface class encapsulates the limit surface for a face of a mesh
/// for any data interpolation type (vertex, varying and face-varying) and
/// provides the public interface for its evaluation. Surface is a class
/// template parameterized to support evaluation in single or double
/// precision.
///
/// @tparam REAL Floating point precision (float or double only)
///
/// Instances of Surface are created or initialized by a subclass of the
/// SurfaceFactory. Since existing instances can be re-initialized, they
/// should be tested for validity after such re-initialization.
///
/// All Surfaces are assigned a Parameterization based on the subdivision
/// scheme and the size of the face, which can then be used for evaluation
/// and tessellation of the surface.
///
template <typename REAL>
class Surface {
public:
/// @brief Simple struct defining the size and stride of points in
/// arrays.
struct PointDescriptor {
PointDescriptor() : size(0), stride(0) { }
PointDescriptor(int n) : size(n), stride(n) { }
PointDescriptor(int n, int m) : size(n), stride(m) { }
int size, stride;
};
/// @brief Integer type representing a mesh index
typedef int Index;
public:
//@{
/// @name Construction and initialization
///
/// Instances of Surface may be explicitly constructed, but are
/// initialized by SurfaceFactory and so only default construction
/// is provided. An instance will be invalid (and so unusable) if
/// default constructed, or if the factory that initialized it
/// determined that the face associated with it has no limit surface.
///
/// @brief Return true if successfully initialized
bool IsValid() const { return _data.isValid(); }
/// @brief Clear a previously initialized Surface
void Clear() { _data.reinitialize(); }
/// @brief Default construction produces an invalid instance
Surface();
Surface(Surface const & src) = default;
Surface& operator=(Surface const & src) = default;
~Surface() = default;
//@}
//@{
/// @name Simple queries
///
/// Simple queries of valid Surface.
///
/// @brief Return the Parameterization
Parameterization GetParameterization() const { return _data.getParam(); }
/// @brief Return the size of the face
int GetFaceSize() const { return GetParameterization().GetFaceSize(); }
/// @brief Return if the Surface is a single regular patch
bool IsRegular() const { return _data.isRegular(); }
/// @brief Return if the Surface is linear
bool IsLinear() const { return _data.isLinear(); }
//@}
//@{
/// @name Methods to manage control points
///
/// Control points are the subset of points in the mesh that influence
/// a Surface. They can be identified as part of the mesh data by their
/// indices, or gathered into an array for other purposes.
///
/// It is not necessary to deal directly with control points for
/// evaluation, but they are useful with limit stencils and other
/// purposes, e.g. computing a bounding box of the control hull of
/// the Surface.
///
/// Note that methods that access control points from the array of
/// mesh data require that the array be contiguous. If a large data
/// set is fragmented into blocks or pages, these methods cannot be
/// used and control points will need to be gathered explicitly.
///
/// @brief Return the number of control points affecting the Surface
int GetNumControlPoints() const { return _data.getNumCVs(); }
/// @brief Identify indices of control points in the mesh
int GetControlPointIndices(Index meshPointIndices[]) const;
/// @brief Gather control points in a local array
///
/// @tparam REAL_MESH Floating point precision of mesh points
///
/// @param meshPoints Input array of mesh point data
/// @param meshPointDesc The size and stride of mesh point data
/// @param controlPoints Output array of control point data
/// @param controlPointDesc The size and stride of control point data
///
template <typename REAL_MESH>
void GatherControlPoints(REAL_MESH const meshPoints[],
PointDescriptor const & meshPointDesc,
REAL controlPoints[],
PointDescriptor const & controlPointDesc) const;
/// @brief Compute bounds of control points from a local array
void BoundControlPoints(REAL const controlPoints[],
PointDescriptor const & controlPointDesc,
REAL minExtent[],
REAL maxExtent[]) const;
/// @brief Compute bounds of control points from the mesh data
void BoundControlPointsFromMesh(REAL const meshPoints[],
PointDescriptor const & meshPointDesc,
REAL minExtent[],
REAL maxExtent[]) const;
//@}
//@{
/// @name Methods to manage patch points
///
/// Patch points are derived from the control points and are used to
/// evaluate the Surface. The patch points always include the control
/// points as a subset.
///
/// @brief Return the number of patch points representing the Surface
int GetNumPatchPoints() const;
///
/// @brief Prepare patch points in a local array for evaluation
///
/// The patch points consist of the control points plus any additional
/// points derived from them that may be required to represent the
/// limit surface as one or more parametric patches.
///
/// @param meshPoints Input array of mesh point data
/// @param meshPointDesc The size and stride of mesh point data
/// @param patchPoints Output array of patch point data
/// @param patchPointDesc The size and stride of patch point data
///
/// Note that this method requires the mesh data be in a contiguous
/// array. If a large data set is fragmented into blocks or pages, this
/// method cannot be used. The control points will need to be gathered
/// explicitly as the subset of patch points, after which the method to
/// compute the remaining patch points can be used.
///
void PreparePatchPoints(REAL const meshPoints[],
PointDescriptor const & meshPointDesc,
REAL patchPoints[],
PointDescriptor const & patchPointDesc) const;
/// @brief Compute all patch points following the control points
///
/// For cases where the control points have already been gathered into
/// an array allocated for the patch points, the remaining patch points
/// will be computed.
///
/// @param patchPoints Array of patch point data to be modified
/// @param patchPointDesc The size and stride of patch point data
///
void ComputePatchPoints(REAL patchPoints[],
PointDescriptor const & patchPointDesc) const;
//@}
//@{
/// @name Evaluation of positions and derivatives
///
/// Evaluation methods use the patch points to compute position, 1st and
/// 2nd derivatives of the Surface at a given (u,v) coordinate within
/// the domain of the Surface's Parameterization. All parameters of the
/// different overloads are required.
///
/// @brief Evaluation of position
void Evaluate(REAL const uv[2],
REAL const patchPoints[], PointDescriptor const & pointDesc,
REAL P[]) const;
/// @brief Overload of evaluation for 1st derivatives
void Evaluate(REAL const uv[2],
REAL const patchPoints[], PointDescriptor const & pointDesc,
REAL P[], REAL Du[], REAL Dv[]) const;
/// @brief Overload of evaluation for 2nd derivatives
void Evaluate(REAL const uv[2],
REAL const patchPoints[], PointDescriptor const & pointDesc,
REAL P[], REAL Du[], REAL Dv[],
REAL Duu[], REAL Duv[], REAL Dvv[]) const;
//@}
//@{
/// @name Evaluation and application of limit stencils
///
/// Limit stencils are sets of coefficients that express an evaluation
/// as a linear combination of the control points. As with the direct
/// evaluation methods, they are overloaded to optionally provide
/// evaluation for 1st and 2nd derivatives.
///
/// In addition to methods to provide limit stencils, methods are also
/// provided to apply them to the control points. Since application of
/// stencils is identical for each (i.e. the same for position and any
/// derivative) no overloads are provided for derivatives.
///
/// @brief Evaluation of the limit stencil for position
int EvaluateStencil(REAL const uv[2], REAL sP[]) const;
/// @brief Overload of limit stencil evaluation for 1st derivatives
int EvaluateStencil(REAL const uv[2], REAL sP[],
REAL sDu[], REAL sDv[]) const;
/// @brief Overload of limit stencil evaluation for 2nd derivatives
int EvaluateStencil(REAL const uv[2], REAL sP[],
REAL sDu[], REAL sDv[],
REAL sDuu[], REAL sDuv[], REAL sDvv[]) const;
/// @brief Apply a single stencil to control points from a local array
void ApplyStencil(REAL const stencil[],
REAL const controlPoints[], PointDescriptor const &,
REAL result[]) const;
/// @brief Apply a single stencil to control points from the mesh data
void ApplyStencilFromMesh(REAL const stencil[],
REAL const meshPoints[], PointDescriptor const &,
REAL result[]) const;
//@}
private:
// Internal methods for evaluating derivatives, basis weights and
// stencils for regular, irregular and irregular linear patches:
typedef Vtr::ConstArray<int> IndexArray;
void evaluateDerivs(REAL const uv[2], REAL const patchPoints[],
PointDescriptor const &, REAL * derivs[]) const;
void evalRegularDerivs(REAL const uv[2], REAL const patchPoints[],
PointDescriptor const &, REAL * derivs[]) const;
void evalIrregularDerivs(REAL const uv[2], REAL const patchPoints[],
PointDescriptor const &, REAL * derivs[]) const;
void evalMultiLinearDerivs(REAL const uv[2], REAL const patchPoints[],
PointDescriptor const &, REAL * derivs[]) const;
void evalRegularBasis(REAL const uv[2], REAL * wDeriv[]) const;
IndexArray evalIrregularBasis(REAL const uv[2], REAL * wDeriv[]) const;
int evalMultiLinearBasis(REAL const uv[2], REAL * wDeriv[]) const;
int evaluateStencils(REAL const uv[2], REAL * sDeriv[]) const;
int evalRegularStencils(REAL const uv[2], REAL * sDeriv[]) const;
int evalIrregularStencils(REAL const uv[2], REAL * sDeriv[]) const;
int evalMultiLinearStencils(REAL const uv[2], REAL * sDeriv[]) const;
// Internal methods to compute patch points:
void computeLinearPatchPoints(REAL p[], PointDescriptor const &) const;
void computeIrregularPatchPoints(REAL p[], PointDescriptor const &) const;
// Internal methods specific to regular or irregular patches:
unsigned char getRegPatchType() const { return _data.getRegPatchType(); }
unsigned char getRegPatchMask() const { return _data.getRegPatchMask(); }
internal::IrregularPatchType const & getIrregPatch() const;
private:
// Access to the set of member variables - provided to the Factory:
friend class SurfaceFactory;
internal::SurfaceData & getSurfaceData() { return _data; }
internal::SurfaceData const & getSurfaceData() const { return _data; }
private:
// All member variables encapsulated in a single class:
internal::SurfaceData _data;
};
//
// Simple inline methods composed of other methods:
//
template <typename REAL>
inline void
Surface<REAL>::ComputePatchPoints(REAL points[],
PointDescriptor const & pointDesc) const {
if (!IsRegular()) {
if (IsLinear()) {
computeLinearPatchPoints(points, pointDesc);
} else {
computeIrregularPatchPoints(points, pointDesc);
}
}
}
template <typename REAL>
inline void
Surface<REAL>::PreparePatchPoints(
REAL const meshPoints[], PointDescriptor const & meshPointDesc,
REAL patchPoints[], PointDescriptor const & patchPointDesc) const {
GatherControlPoints(meshPoints, meshPointDesc, patchPoints, patchPointDesc);
ComputePatchPoints(patchPoints, patchPointDesc);
}
//
// Inline invocations of more general methods for derivative overloads:
//
template <typename REAL>
inline void
Surface<REAL>::evaluateDerivs(REAL const uv[2],
REAL const patchPoints[],
PointDescriptor const & pointDesc,
REAL * derivatives[]) const {
if (IsRegular()) {
evalRegularDerivs(uv, patchPoints, pointDesc, derivatives);
} else if (IsLinear()) {
evalMultiLinearDerivs(uv, patchPoints, pointDesc, derivatives);
} else {
evalIrregularDerivs(uv, patchPoints, pointDesc, derivatives);
}
}
template <typename REAL>
inline void
Surface<REAL>::Evaluate(REAL const uv[2],
REAL const patchPoints[],
PointDescriptor const & pointDesc,
REAL P[]) const {
REAL * derivatives[6] = { P, 0, 0, 0, 0, 0 };
evaluateDerivs(uv, patchPoints, pointDesc, derivatives);
}
template <typename REAL>
inline void
Surface<REAL>::Evaluate(REAL const uv[2],
REAL const patchPoints[],
PointDescriptor const & pointDesc,
REAL P[], REAL Du[], REAL Dv[]) const {
REAL * derivatives[6] = { P, Du, Dv, 0, 0, 0 };
evaluateDerivs(uv, patchPoints, pointDesc, derivatives);
}
template <typename REAL>
inline void
Surface<REAL>::Evaluate(REAL const uv[2],
REAL const patchPoints[],
PointDescriptor const & pointDesc,
REAL P[], REAL Du[], REAL Dv[],
REAL Duu[], REAL Duv[], REAL Dvv[]) const {
REAL * derivatives[6] = { P, Du, Dv, Duu, Duv, Dvv };
evaluateDerivs(uv, patchPoints, pointDesc, derivatives);
}
template <typename REAL>
inline int
Surface<REAL>::evaluateStencils(REAL const uv[2], REAL * sDeriv[]) const {
if (IsRegular()) {
return evalRegularStencils(uv, sDeriv);
} else if (IsLinear()) {
return evalMultiLinearStencils(uv, sDeriv);
} else {
return evalIrregularStencils(uv, sDeriv);
}
}
template <typename REAL>
inline int
Surface<REAL>::EvaluateStencil(REAL const uv[2], REAL sP[]) const {
REAL * derivativeStencils[6] = { sP, 0, 0, 0, 0, 0 };
return evaluateStencils(uv, derivativeStencils);
}
template <typename REAL>
inline int
Surface<REAL>::EvaluateStencil(REAL const uv[2],
REAL sP[], REAL sDu[], REAL sDv[]) const {
REAL * derivativeStencils[6] = { sP, sDu, sDv, 0, 0, 0 };
return evaluateStencils(uv, derivativeStencils);
}
template <typename REAL>
inline int
Surface<REAL>::EvaluateStencil(REAL const uv[2],
REAL sP[], REAL sDu[], REAL sDv[],
REAL sDuu[], REAL sDuv[], REAL sDvv[]) const {
REAL * derivativeStencils[6] = { sP, sDu, sDv, sDuu, sDuv, sDvv };
return evaluateStencils(uv, derivativeStencils);
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_SURFACE */

View File

@ -0,0 +1,85 @@
//
// Copyright 2021 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.
//
#include "../bfr/surfaceData.h"
#include "../bfr/patchTree.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
namespace internal {
//
// Constructors and other methods to manage data members for copy and
// destruction:
//
SurfaceData::SurfaceData() : _cvIndices(), _param(),
_isValid(false),
_isDouble(false),
_isRegular(true),
_isLinear(false),
_regPatchType(0),
_regPatchMask(0),
_irregPatch() {
}
SurfaceData &
SurfaceData::operator=(SurfaceData const & src) {
// No need to explicitly manage pre-existing resources in destination
// as they will be either re-used or released when re-assigned
// No copy/operator= supported by StackBuffer so resize and copy:
_cvIndices.SetSize(src._cvIndices.GetSize());
std::memcpy(&_cvIndices[0],
&src._cvIndices[0], src._cvIndices.GetSize() * sizeof(Index));
_param = src._param;
_isValid = src._isValid;
_isDouble = src._isDouble;
_isRegular = src._isRegular;
_isLinear = src._isLinear;
_regPatchType = src._regPatchType;
_regPatchMask = src._regPatchMask;
_irregPatch = src._irregPatch;
return *this;
}
void
SurfaceData::invalidate() {
// Release any attached memory before marking as invalid:
_irregPatch = 0;
_isValid = false;
}
} // end namespace internal
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,129 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_SURFACE_DATA_H
#define OPENSUBDIV3_BFR_SURFACE_DATA_H
#include "../version.h"
#include "../bfr/parameterization.h"
#include "../bfr/irregularPatchType.h"
#include "../vtr/stackBuffer.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
namespace internal {
//
// SurfaceData is a simple internal class that encapsulates all member
// variables of a Surface -- allowing the SurfaceFactory to initialize
// a Surface independent of its final type.
//
// Since internal, and access to instances of SurfaceData is restricted
// by other means, all accessors and modifiers are made public (though
// only the SurfaceFactory modifies an instance).
//
class SurfaceData {
public:
SurfaceData();
SurfaceData(SurfaceData const & src) { *this = src; }
SurfaceData & operator=(SurfaceData const & src);
~SurfaceData() { invalidate(); }
public:
// Simple accessors used by both Surface and SurfaceFactory:
typedef int Index;
int getNumCVs() const { return (int)_cvIndices.GetSize(); }
Index const * getCVIndices() const { return &_cvIndices[0]; }
Parameterization getParam() const { return _param; }
bool isValid() const { return _isValid; }
bool isDouble() const { return _isDouble; }
bool isRegular() const { return _isRegular; }
bool isLinear() const { return _isLinear; }
unsigned char getRegPatchType() const { return _regPatchType; }
unsigned char getRegPatchMask() const { return _regPatchMask; }
// Local types and accessors for references to irregular patches:
typedef internal::IrregularPatchType IrregPatchType;
typedef internal::IrregularPatchSharedPtr IrregPatchPtr;
bool hasIrregPatch() const { return _irregPatch != 0; }
IrregPatchType const & getIrregPatch() const { return *_irregPatch; }
IrregPatchPtr getIrregPatchPtr() const { return _irregPatch; }
public:
// Modifiers used by SurfaceFactory to assemble a Surface:
void invalidate();
void reinitialize() { if (isValid()) invalidate(); }
Index * getCVIndices() { return &_cvIndices[0]; }
Index * resizeCVs(int size) {
_cvIndices.SetSize(size);
return &_cvIndices[0];
}
void setParam(Parameterization p) { _param = p; }
void setValid(bool on) { _isValid = on; }
void setDouble(bool on) { _isDouble = on; }
void setRegular(bool on) { _isRegular = on; }
void setLinear(bool on) { _isLinear = on; }
void setRegPatchType(int t) { _regPatchType = (unsigned char) t; }
void setRegPatchMask(int m) { _regPatchMask = (unsigned char) m; }
void setIrregPatchPtr(IrregPatchPtr const & ptr) { _irregPatch = ptr; }
private:
// Member variables -- try to avoid redundancy and/or wasted space
// here as some may choose to cache all Surfaces of a mesh:
typedef Vtr::internal::StackBuffer<Index,20,true> CVIndexArray;
CVIndexArray _cvIndices;
Parameterization _param;
unsigned char _isValid : 1;
unsigned char _isDouble : 1;
unsigned char _isRegular : 1;
unsigned char _isLinear : 1;
unsigned char _regPatchType;
unsigned char _regPatchMask;
IrregPatchPtr _irregPatch;
};
} // end namespace internal
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_SURFACE_DATA */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,563 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_SURFACE_FACTORY_H
#define OPENSUBDIV3_BFR_SURFACE_FACTORY_H
#include "../version.h"
#include "../bfr/surface.h"
#include "../bfr/surfaceFactoryMeshAdapter.h"
#include "../sdc/options.h"
#include "../sdc/types.h"
#include <cstdint>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Forward declarations of public and internal classes used by factories:
//
class SurfaceFactoryCache;
class FaceTopology;
class FaceSurface;
///
/// @brief Base class providing initialization of a Surface for each face
/// of a mesh
///
/// SurfaceFactory is an abstract class that provides the majority of
/// the implementation and the interface for a factory that initializes
/// instances of Surface for the faces of a mesh.
///
/// A subclass of SurfaceFactory is written to support a specific type
/// of connected mesh. The public interface of SurfaceFactory is both
/// inherited by and extended by the subclasses. Expected extensions to
/// the interface include one or more constructors (i.e. given a specific
/// instance of the subclass' mesh type) as well as other methods that
/// may involve the mesh's data types (primvars) in their native form.
///
/// By inheriting the SurfaceFactoryMeshAdapter interface, SurfaceFactory
/// requires its subclasses to implement the small suite of pure
/// virtual methods to complete the factory's implementation for the
/// subclass' mesh type. These methods provide the base factory with
/// topological information about faces of that mesh -- from which it
/// creates instances of Surface defining their limit surface.
///
/// The SurfaceFactory inherits rather than contains SurfaceFactoryMeshAdapter
/// as instances of SurfaceFactoryMeshAdapter serve no purpose on their own,
/// and the interface between the two is designed with the specific needs
/// of the SurfaceFactory. When customizing a subclass of SurfaceFactory
/// for a particular mesh type, this inheritance also avoids the need to
/// coordinate the subclass of SurfaceFactory with the separate subclass
/// of SurfaceFactoryMeshAdapter.
///
/// It must be emphasized that a subclass of SurfaceFactory is written to
/// support a specific type of "connected" mesh -- not simply a container
/// of data defining a mesh. The SurfaceFactoryMeshAdapter interface describes
/// the complete topological neighborhood around a specific face, and
/// without any connectivity between mesh components (e.g. given a vertex,
/// what are its incident faces?), satisfying these methods will be
/// impossible, or, at best, extremely inefficient.
///
/// Ultimately a subclass of SurfaceFactory is expected to be a lightweight
/// interface to a connected mesh -- lightweight in terms of both time and
/// memory usage. It's construction is expected to be trivial, after which
/// it can quickly and efficiently provide a Surface for one or more faces
/// of a mesh for immediate evaluation. So construction of an instance of
/// a subclass should involve no heavy pre-processing -- the greater the
/// overhead of a subclass constructor, the more it violates the intention
/// of the base class as a lightweight interface.
///
/// Instances of SurfaceFactory are initialized with a set of Options that
/// form part of the state of the factory and remain fixed for its lifetime.
/// Such options are intended to ensure that the instances of Surface that
/// it creates are consistent, as well as to enable/disable or otherwise
/// manage caching for construction efficiency -- either internally or
/// between itself and other factories (advanced).
///
class SurfaceFactory : public SurfaceFactoryMeshAdapter {
public:
///
/// @brief Simple set of options assigned to instances of SurfaceFactory
///
/// The Options class is a simple container specifying options for the
/// construction of the SurfaceFactory to be applied during its lifetime.
///
/// These options currently include choices to identify a default
/// face-varying ID, to control caching behavior (on or off, use of
/// external vs internal cache), and to control the accuracy of the
/// resulting limit surface representations.
///
class Options {
public:
Options() : _dfltFVarID(-1), _externCache(0), _enableCache(true),
_approxLevelSmooth(2), _approxLevelSharp(6) { }
/// @brief Assign the default face-varying ID (none assigned by
/// default)
Options & SetDefaultFVarID(FVarID id);
/// @brief Return the default face-varying ID
FVarID GetDefaultFVarID() const { return _dfltFVarID; }
/// @brief Enable or disable caching (default is true):
Options & EnableCaching(bool on);
/// @brief Return if caching is enable
bool IsCachingEnabled() const { return _enableCache; }
/// @brief Assign an external cache to override the internal
Options & SetExternalCache(SurfaceFactoryCache * c);
/// @brief Return any assigned external cache
SurfaceFactoryCache * GetExternalCache() const { return _externCache; }
// Set refinement levels used to approximate the limit surface
// for smooth and sharp features (reasonable defaults assigned):
/// @brief Assign maximum refinement level for smooth features
Options & SetApproxLevelSmooth(int level);
/// @brief Return maximum refinement level for smooth features
int GetApproxLevelSmooth() const { return _approxLevelSmooth; }
/// @brief Assign maximum refinement level for sharp features
Options & SetApproxLevelSharp(int level);
/// @brief Return maximum refinement level for sharp features
int GetApproxLevelSharp() const { return _approxLevelSharp; }
private:
// Member variables:
FVarID _dfltFVarID;
SurfaceFactoryCache * _externCache;
unsigned char _enableCache : 1;
unsigned char _approxLevelSmooth;
unsigned char _approxLevelSharp;
};
public:
~SurfaceFactory() override;
//@{
/// @name Simple queries of subdivision properties
///
/// Simple queries to inspect subdivision properties.
///
/// @brief Return the subdivision scheme
Sdc::SchemeType GetSchemeType() const { return _subdivScheme; }
/// @brief Return the set of subdivision options
Sdc::Options GetSchemeOptions() const { return _subdivOptions; }
//@}
public:
//@{
/// @name Simple queries influencing Surface construction
///
/// Methods to quickly inspect faces that influence Surface construction.
///
/// A small set of methods is useful to inspect faces in order to
/// determine if their corresponding Surfaces should be initialized.
/// The Surface initialization methods will fail when a limit surface
/// does exist, so the methods here are intended for purposes when
/// that simple failure on initialization is not suitable, e.g. to
/// address some kind of pre-processing need prior to the initialization
/// of any Surfaces.
///
/// @brief Return if a specified face has a limit surface
///
/// This method determines if a face has an associated limit surface,
/// and so supports initialization of Surface for evaluation. This
/// is usually the case, except when the face is tagged as a hole, or
/// due to the use of uncommon boundary interpolation options (i.e.
/// Sdc::Options::VTX_BOUNDARY_NONE). The test of a hole is trivial,
/// but the boundary test is not when such uncommon options are used.
///
bool FaceHasLimitSurface(Index faceIndex) const;
/// @brief Return the Parameterization of a face with a limit surface
///
/// This method simply returns the Parameterization of the specified
/// face. It is presumed the face has an existing limit surface and
/// so is a quick and simple accessor.
///
Parameterization GetFaceParameterization(Index faceIndex) const;
//@}
public:
//@{
/// @name Methods to initialize Surfaces
///
/// Methods to initialize instances of Surface for a specific face.
///
/// Given the different interpolation types for data associated with
/// mesh vertices (i.e. vertex, varying and face-varying data), the
/// topology of the limit surface potentially (likely) differs between
/// them. So it is necessary to specify the type of data to be associated
/// with the Surface. Methods exist to initialize a single surface for
/// each of the three data types, and to initialize multiple surfaces
/// for different data types at once (which will avoid repeated effort
/// in a single-threaded context).
///
/// Failure of these initialization methods is expected (and so to be
/// tested) when a face has no limit surface -- either due to it being
/// a hole or through the use of less common boundary interpolation
/// options. Failure is also possible if the subclass fails to provide
/// a valid topological description of the face. (WIP - consider more
/// extreme failure for these cases, e.g. possible assertions.)
///
/// @brief Initialize a Surface for vertex data
///
/// @param faceIndex Index of face with limit surface of interest
/// @param surface Surface to initialize for vertex data
/// @return True if the face has a limit surface and it was
/// successfully constructed
///
template <typename REAL>
bool InitVertexSurface(Index faceIndex, Surface<REAL> * surface) const;
/// @brief Initialize a Surface for varying data
///
/// @param faceIndex Index of face with limit surface of interest
/// @param surface Surface to initialize for varying data
/// @return True if the face has a limit surface and it was
/// successfully constructed
///
template <typename REAL>
bool InitVaryingSurface(Index faceIndex, Surface<REAL> * surface) const;
/// @brief Initialize a Surface for the default face-varying data
///
/// For this variant, no explicit face-varying ID is specified. The
/// default is determined from the Options with which the SurfaceFactory
/// was created (assignment of that default is required).
///
/// @param faceIndex Index of face with limit surface of interest
/// @param surface Surface to initialize for face-varying data
/// @return True if the face has a limit surface, the default
/// face-varying ID was valid, and its Surface was
/// successfully constructed
///
template <typename REAL>
bool InitFaceVaryingSurface(Index faceIndex, Surface<REAL> * surface) const;
/// @brief Initialize a Surface for specified face-varying data
///
/// @param faceIndex Index of face with limit surface of interest
/// @param surface Surface to initialize for face-varying data
/// @param fvarID Identifier of a specific set of face-varying data
/// @return True if the face has a limit surface, the given
/// face-varying ID was valid, and its Surface was
/// successfully constructed
///
template <typename REAL>
bool InitFaceVaryingSurface(Index faceIndex, Surface<REAL> * surface,
FVarID fvarID) const;
///
/// @brief Initialize multiple Surfaces at once
///
/// This method initializes multiple Surfaces at once -- for any
/// combination of the three different data interpolation types.
/// Its use is recommended when two are more surfaces are known to
/// be non-linear, which will avoid the repeated effort if each
/// Surface is individually initialized.
///
/// Arguments are ordered here to satisfy common cases easily with
/// the use of optional arguments for less common cases.
///
/// @param faceIndex Index of face with limit surfaces of interest
/// @param vtxSurface Surface to initialize for vertex data
/// @param fvarSurfaces Surface array to initialize for face-varying data
/// @param fvarIDs Array of face-varying IDs corresponding to the
/// face-varying Surfaces to be initialized
/// (optional -- defaults to an integer sequence
/// [0 .. fvarCount-1] if absent)
/// @param fvarCount Size of array of face-varying Surfaces (optional)
/// @param varSurface Surface to initialize for varying data (optional)
/// @return True if the face has a limit surface, any given
/// face-varying IDs were valid, and all Surfaces
/// were successfully constructed.
///
template <typename REAL>
bool InitSurfaces(Index faceIndex, Surface<REAL> * vtxSurface,
Surface<REAL> * fvarSurfaces,
FVarID const fvarIDs[] = 0,
int fvarCount = 0,
Surface<REAL> * varSurface = 0) const;
//@}
//@{
/// @name Methods to construct Surfaces
///
/// Methods to both allocate and initialize a single Surface.
//
// WIP - considering removing these since non-essential
//
/// @brief Construct a Surface for vertex data
template <typename REAL=float>
Surface<REAL> * CreateVertexSurface(Index faceIndex) const;
/// @brief Construct a Surface for varying data
template <typename REAL=float>
Surface<REAL> * CreateVaryingSurface(Index faceIndex) const;
/// @brief Construct a Surface for the default face-varying data
template <typename REAL=float>
Surface<REAL> * CreateFaceVaryingSurface(Index faceIndex) const;
/// @brief Construct a Surface for specified face-varying data
template <typename REAL=float>
Surface<REAL> * CreateFaceVaryingSurface(Index faceIndex, FVarID id) const;
//@}
protected:
//@{
/// @name Protected methods supporting subclass construction
///
/// Protected methods supporting subclass construction.
///
///
/// @brief Constructor to be used by subclasses
///
/// Construction requires specification of the subdivision scheme and
/// options associated with the mesh (as is the case with other classes
/// in Far). These will typically reflect the settings in the mesh but
/// can also be used to override them -- as determined by the subclass.
/// Common uses of overrides are to assign a subdivision scheme to a
/// simple polygonal mesh, or to change the face-varying interpolation
/// for the faster linear interpolation of UVs.
///
SurfaceFactory(Sdc::SchemeType schemeType,
Sdc::Options const & schemeOptions,
Options const & limitOptions);
/// @brief Subclass to identify an internal cache for use by base class
void setInternalCache(SurfaceFactoryCache * cache);
SurfaceFactory(SurfaceFactory const &) = delete;
SurfaceFactory & operator=(SurfaceFactory const &) = delete;
//@}
private:
// Supporting internal methods:
void setSubdivisionOptions(Sdc::SchemeType, Sdc::Options const & options);
void setFactoryOptions(Options const & factoryOptions);
bool faceHasLimitSimple(Index faceIndex, int faceSize) const;
bool faceHasLimitNeighborhood(Index faceIndex) const;
bool faceHasLimitNeighborhood(FaceTopology const & faceTopology) const;
class SurfaceSet;
bool populateAllSurfaces( Index faceIndex, SurfaceSet * sSetPtr) const;
bool populateLinearSurfaces( Index faceIndex, SurfaceSet * sSetPtr) const;
bool populateNonLinearSurfaces(Index faceIndex, SurfaceSet * sSetPtr) const;
bool initSurfaces(Index faceIndex, internal::SurfaceData * vtxSurface,
internal::SurfaceData * varSurface,
internal::SurfaceData * fvarSurfaces,
int fvarCount,
FVarID const fvarIDs[]) const;
// Methods to assemble topology and corresponding indices for entire face:
bool isFaceNeighborhoodRegular(Index faceIndex,
FVarID const * fvarPtrOrVtx,
Index indices[]) const;
bool initFaceNeighborhoodTopology(Index faceIndex,
FaceTopology * topology) const;
bool gatherFaceNeighborhoodTopology(Index faceIndex,
FaceTopology * topology) const;
int gatherFaceNeighborhoodIndices(Index faceIndex,
FaceTopology const & topology,
FVarID const * fvarPtrOrVtx,
Index indices[]) const;
// Methods to assemble Surfaces for the different categories of patch:
typedef internal::SurfaceData SurfaceType;
void assignLinearSurface(SurfaceType * surfacePtr,
Index faceIndex,
FVarID const * fvarPtrOrVtx) const;
void assignRegularSurface(SurfaceType * surfacePtr,
Index const surfacePatchPoints[]) const;
void assignRegularSurface(SurfaceType * surfacePtr,
FaceSurface const & surfaceDescription) const;
void assignIrregularSurface(SurfaceType * surfacePtr,
FaceSurface const & surfaceDescription) const;
void copyNonLinearSurface(SurfaceType * surfacePtr,
SurfaceType const & surfaceSource,
FaceSurface const & surfaceDescription) const;
private:
// Members describing options and subdivision properties (very little
// memory and low initialization cost)
Sdc::SchemeType _subdivScheme;
Sdc::Options _subdivOptions;
Options _factoryOptions;
// Members related to subdivision topology, options and limit tests:
unsigned int _linearScheme : 1;
unsigned int _linearFVarInterp : 1;
unsigned int _testNeighborhoodForLimit : 1;
unsigned int _rejectSmoothBoundariesForLimit : 1;
unsigned int _rejectIrregularFacesForLimit : 1;
int _regFaceSize;
// Members related to caching:
SurfaceFactoryCache mutable * _topologyCache;
};
//
// Inline methods for Options:
//
inline SurfaceFactory::Options &
SurfaceFactory::Options::SetDefaultFVarID(FVarID id) {
_dfltFVarID = id;
return *this;
}
inline SurfaceFactory::Options &
SurfaceFactory::Options::EnableCaching(bool on) {
_enableCache = on;
return *this;
}
inline SurfaceFactory::Options &
SurfaceFactory::Options::SetExternalCache(SurfaceFactoryCache * c) {
_externCache = c;
return *this;
}
inline SurfaceFactory::Options &
SurfaceFactory::Options::SetApproxLevelSmooth(int level) {
_approxLevelSmooth = (unsigned char) level;
return *this;
}
inline SurfaceFactory::Options &
SurfaceFactory::Options::SetApproxLevelSharp(int level) {
_approxLevelSharp = (unsigned char) level;
return *this;
}
//
// Inline methods to initializes Surfaces:
//
template <typename REAL>
inline bool
SurfaceFactory::InitVertexSurface(Index face, Surface<REAL> * s) const {
return initSurfaces(face, &s->getSurfaceData(), 0, 0, 0, 0);
}
template <typename REAL>
inline bool
SurfaceFactory::InitVaryingSurface(Index face, Surface<REAL> * s) const {
return initSurfaces(face, 0, &s->getSurfaceData(), 0, 0, 0);
}
template <typename REAL>
inline bool
SurfaceFactory::InitFaceVaryingSurface(Index face, Surface<REAL> * s,
FVarID fvarID) const {
return initSurfaces(face, 0, 0, &s->getSurfaceData(), 1, &fvarID);
}
template <typename REAL>
inline bool
SurfaceFactory::InitFaceVaryingSurface(Index face, Surface<REAL> * s) const {
FVarID dfltID = _factoryOptions.GetDefaultFVarID();
return initSurfaces(face, 0, 0, &s->getSurfaceData(), 1, &dfltID);
}
template <typename REAL>
inline bool
SurfaceFactory::InitSurfaces(Index faceIndex, Surface<REAL> * vtxSurface,
Surface<REAL> * fvarSurfaces, FVarID const fvarIDs[], int fvarCount,
Surface<REAL> * varSurface) const {
bool useDfltFVarID = fvarSurfaces && (fvarIDs == 0) && (fvarCount == 0);
FVarID dfltFVarID = useDfltFVarID ? _factoryOptions.GetDefaultFVarID() : 0;
return initSurfaces(faceIndex,
vtxSurface ? &vtxSurface->getSurfaceData() : 0,
varSurface ? &varSurface->getSurfaceData() : 0,
fvarSurfaces ? &fvarSurfaces->getSurfaceData() : 0,
fvarCount ? fvarCount : (fvarSurfaces != 0),
useDfltFVarID ? &dfltFVarID : fvarIDs);
}
//
// Inline methods to allocate and initialize Surfaces:
//
template <typename REAL>
inline Surface<REAL> *
SurfaceFactory::CreateVertexSurface(Index faceIndex) const {
Surface<REAL> * s = new Surface<REAL>();
if (InitVertexSurface<REAL>(faceIndex, s)) return s;
delete s;
return 0;
}
template <typename REAL>
inline Surface<REAL> *
SurfaceFactory::CreateVaryingSurface(Index faceIndex) const {
Surface<REAL> * s = new Surface<REAL>();
if (InitVaryingSurface<REAL>(faceIndex, s)) return s;
delete s;
return 0;
}
template <typename REAL>
inline Surface<REAL> *
SurfaceFactory::CreateFaceVaryingSurface(Index faceIndex, FVarID fvarID) const {
Surface<REAL> * s = new Surface<REAL>();
if (InitFaceVaryingSurface<REAL>(faceIndex, s, fvarID)) return s;
delete s;
return 0;
}
template <typename REAL>
inline Surface<REAL> *
SurfaceFactory::CreateFaceVaryingSurface(Index face) const {
FVarID dfltID = _factoryOptions.GetDefaultFVarID();
return CreateFaceVaryingSurface<REAL>(face, dfltID);
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_SURFACE_FACTORY_H */

View File

@ -0,0 +1,81 @@
//
// Copyright 2021 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.
//
#include "../bfr/surfaceFactoryCache.h"
#include "../bfr/patchTree.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Trivial constructor and destructor:
//
SurfaceFactoryCache::SurfaceFactoryCache() : _map() {
}
SurfaceFactoryCache::~SurfaceFactoryCache() {
// Potentially monitor usage on destruction
}
//
// Internal methods to find and add map entries:
//
SurfaceFactoryCache::DataType
SurfaceFactoryCache::find(KeyType const & key) const {
MapType::const_iterator itFound = _map.find(key);
return (itFound != _map.end()) ? itFound->second : DataType(0);
}
SurfaceFactoryCache::DataType
SurfaceFactoryCache::add(KeyType const & key, DataType const & data) {
MapType::const_iterator itFound = _map.find(key);
return (itFound != _map.end()) ? itFound->second : (_map[key] = data);
}
//
// Virtual method defaults -- intended to be overridden for thread-safety:
//
SurfaceFactoryCache::DataType
SurfaceFactoryCache::Find(KeyType const & key) const {
return find(key);
}
SurfaceFactoryCache::DataType
SurfaceFactoryCache::Add(KeyType const & key, DataType const & data) {
return add(key, data);
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv

View File

@ -0,0 +1,150 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_SURFACE_FACTORY_CACHE_H
#define OPENSUBDIV3_BFR_SURFACE_FACTORY_CACHE_H
#include "../version.h"
#include "../bfr/irregularPatchType.h"
#include <map>
#include <cstdint>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
///
/// @brief Container used internally by SurfaceFactory to store reusable
/// information
///
/// SurfaceFactoryCache is a container for storing/caching instances of
/// the internal representation of complex patches used by SurfaceFactory
/// so that they can be quickly identified and retrieved for reuse.
///
/// It is intended for internal use by SurfaceFactory. Public access is
/// available but limited to construction only -- allowing an instance to
/// be reused by assigning it to more than one SurfaceFactory.
///
//
// Initial/expected use requires simple searches of and additions to the
// cache by the SurfaceFactory or its Builders. Longer term, with the
// possibility of instances of caches being shared between meshes and
// factories, additional options and/or public methods may be warranted
// to limit what is cached or to prune the cache if it gets too large.
//
class SurfaceFactoryCache {
public:
SurfaceFactoryCache();
virtual ~SurfaceFactoryCache();
SurfaceFactoryCache(SurfaceFactoryCache const &) = delete;
SurfaceFactoryCache & operator=(SurfaceFactoryCache const &) = delete;
protected:
/// @cond PROTECTED
// Access restricted to the Factory, its Builders, etc.
friend class SurfaceFactory;
typedef std::uint64_t KeyType;
typedef internal::IrregularPatchSharedPtr DataType;
/// @endcond PROTECTED
protected:
/// @cond PROTECTED
size_t Size() const { return _map.size(); }
//
// Potential overrides by subclasses for thread-safety:
//
virtual DataType Find(KeyType const & key) const;
virtual DataType Add(KeyType const & key, DataType const & data);
//
// Common implementation used by all subclasses:
//
DataType find(KeyType const & key) const;
DataType add(KeyType const & key, DataType const & data);
/// @endcond PROTECTED
private:
typedef std::map<KeyType, DataType> MapType;
MapType _map;
};
///
/// @brief Template for declaring thread-safe subclasses of SurfaceFactoryCache
///
/// SurfaceFactoryCacheThreaded extends SurfaceFactoryCache by protecting
/// access to the cache to ensure thread-safe operation. A mutex type and
/// associated locks are specified to declare a subclass with appropriately
/// protected read and write access.
///
/// @tparam MUTEX_TYPE A mutex type with supported lock guards
/// @tparam READ_LOCK_GUARD_TYPE A scoped lock guard allowing potentially
/// shared access for read operations.
/// @tparam WRITE_LOCK_GUARD_TYPE A scoped lock guard allowing exclusive
/// access for write operations.
///
// Separate read and write locks are provided to support mutex types
// allowing shared (read) or exclusive (write) access.
//
template <class MUTEX_TYPE, class READ_LOCK_GUARD_TYPE,
class WRITE_LOCK_GUARD_TYPE>
class SurfaceFactoryCacheThreaded : public SurfaceFactoryCache {
public:
SurfaceFactoryCacheThreaded() : SurfaceFactoryCache() { }
~SurfaceFactoryCacheThreaded() override = default;
protected:
/// @cond PROTECTED
//
// Virtual overrides from base:
//
DataType Find(KeyType const & key) const override {
READ_LOCK_GUARD_TYPE lockGuard(_mutex);
return find(key);
}
DataType Add(KeyType const & key, DataType const & data) override {
WRITE_LOCK_GUARD_TYPE lockGuard(_mutex);
return add(key, data);
}
/// @endcond PROTECTED
private:
MUTEX_TYPE mutable _mutex;
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_SURFACE_FACTORY_CACHE_H */

View File

@ -0,0 +1,236 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_SURFACE_FACTORY_ADAPTER_H
#define OPENSUBDIV3_BFR_SURFACE_FACTORY_ADAPTER_H
#include "../version.h"
#include <cstdint>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
class VertexDescriptor;
///
/// @brief Abstract interface adapting SurfaceFactory to a connected mesh
/// representation
///
// SurfaceFactoryMeshAdapter is an abstract class that defines the interface
// through which subclasses of SurfaceFactory adapt to a connected mesh
// representation. The interface defines methods that describe the mesh
// topology and control indices in the neighborhood of a mesh -- from
// which the SurfaceFactory to identifies an appropriate limit surface.
//
// SurfaceFactoryMeshAdapter methods require a subclass to provide a complete
// description of the topology around a base face, as well as indices
// associated with it (both vertex and face-varying). The intent here is
// to keep the number of methods required to a minimum, and also to minimize
// the number of invocations required by the factory.
//
// With the need to support both linear and non-linear cases (for which
// linear is trivial by comparison) and the limit surface for both vertex
// and face-varying topologies, the result is a small set of methods
// covering this matrix of functionality.
//
// Since face-varying data may differ in topology from the vertex data --
// with each set of face-varying data potentially having its own unique
// topology -- sets of face-varying data are uniquely distinguished by an
// associated integer (a face-varying ID).
//
class SurfaceFactoryMeshAdapter {
public:
/// @brief Integer type representing a mesh index
typedef int Index;
/// @brief Type used to identify and specify face-varying primvars
///
/// A face-varying ID is used to specify face-varying primvars for
/// evaluation so that they can be identified by the subclass for
/// the mesh. It can be assigned as either a positive integer ID
/// or pointer, with the subclass determining its interpretation.
///
/// Often only one face-varying primvar is of interest, so a default
/// can be assigned to the factory to avoid repeated specification.
///
typedef std::intptr_t FVarID;
protected:
/// @cond PROTECTED
SurfaceFactoryMeshAdapter() { }
virtual ~SurfaceFactoryMeshAdapter() { }
/// @endcond
protected:
//@{
/// @name Methods to query simple face properties
///
/// Methods to return simple properties associated with a face.
///
/// @brief Returns if a face is a hole
virtual bool isFaceHole(Index faceIndex) const = 0;
/// @brief Returns the size of a face (number of vertices)
virtual int getFaceSize(Index faceIndex) const = 0;
//@}
//@{
/// @name Methods to gather indices for the face's vertices
///
/// These methods gather indices associated with the vertices of a
/// face, e.g. the indices of the vertices themselves, or the indices
/// of face-varying values associated with the vertices. These are
/// used to quickly deal with linear limit surfaces without any
/// inspection of the neighboring topology.
///
/// @brief Gather the indices of the face's vertices
virtual int getFaceVertexIndices(Index faceIndex,
Index vertexIndices[]) const = 0;
/// @brief Gather the face-varying indices of the face's vertices
virtual int getFaceFVarValueIndices(Index faceIndex,
FVarID fvarID, Index fvarValueIndices[]) const = 0;
//@}
protected:
//@{
/// @name Methods to identify the neighborhood of a face-vertex
///
/// These methods identify the topology and associated indices for
/// the complete set of incident faces surrounding a corner (or
/// face-vertex) of a face.
///
/// Methods here use "FaceVertex" in the name to emphasize that they
/// require information for a particular corner vertex of the face.
///
/// The topology around the face-vertex is described by populating a
/// given instance of a simple VertexDescriptor class -- which fully
/// describes the face-vertex, it incident faces and any sharpness
/// assigned at or around the face-vertex. (See the comments with
/// the VertexDescriptor definition for more details.)
///
/// Additional methods are then required to identify indices for the
/// incident faces around a face-vertex. One method gathers the
/// indices for control vertices of the mesh assigned to the incident
/// faces (their VertexIndices), while the other gathers indices for
/// a particular set of face-varying values assigned to them (their
/// FVarValueIndices).
///
/// Both methods expect the incident faces to be ordered consistent
/// with the specification in VertexDescriptor, and all indices for
/// all incident faces are required.
///
/// The order of indices assigned to each face for these methods must
/// also be specified relative to the face-vertex, rather than the
/// way the face is defined. For example, if a quad Q is defined by
/// the four vertices {A, B, C, D}, when gathering the indices for Q
/// as part of face-vertex C, the indices should be specified starting
/// with C, i.e. as {C, D, A, B}. Ordering indices this way makes it
/// much easier for the factory to identify when face-varying topology
/// differs from the vertex topology, and both the face-varying and
/// vertex indices are ordered this way for consistency.
///
/// @brief Describe the topology of incident faces around a face-vertex
virtual int populateFaceVertexDescriptor(
Index faceIndex, int faceVertex,
VertexDescriptor * vertexDescriptor) const = 0;
/// @brief Gather vertex indices of incident faces around a face-vertex
virtual int getFaceVertexIncidentFaceVertexIndices(
Index faceIndex, int faceVertex,
Index vertexIndices[]) const = 0;
/// @brief Gather face-varying indices of incident faces around a
/// face-vertex
virtual int getFaceVertexIncidentFaceFVarValueIndices(
Index faceIndex, int faceVertex,
FVarID fvarID, Index fvarValueIndices[]) const = 0;
//@}
protected:
//@{
/// @name Optional methods for purely regular topology
///
/// Optional methods for advanced use to accelerate the case of
/// purely regular topology around a face.
///
/// For cases when a mesh can quickly determine if the neighborhood
/// around a faces is purely regular, these methods can be used to
/// quickly identify the control point indices for the corresponding
/// regular patch defining its limit surface. In doing so, the more
/// tedious topological assembly requiring information about each
/// face-vertex can be avoided.
///
/// The indices returned must be ordered according to the regular
/// patch type corresponding to the subdivision scheme of the mesh.
/// Boundary vertices are allowed and indicated by an Index of -1.
///
/// The face-varying version will only be invoked if the vertex
/// version is purely regular, in which case, the face-varying
/// topology is expected to be similar.
///
/// Note that these methods allow the caller (the SurfaceFactory) to
/// pass nullptr (0) for the index arrays -- in which case only the
/// return value should be provided.
///
virtual bool getFaceNeighborhoodVertexIndicesIfRegular(
Index faceIndex, Index vertexIndices[]) const;
virtual bool getFaceNeighborhoodFVarValueIndicesIfRegular(
Index faceIndex, FVarID fvarID, Index fvarValueIndices[]) const;
//@}
private:
// No private members
};
//
// Inline defaults for optional methods:
//
inline bool
SurfaceFactoryMeshAdapter::getFaceNeighborhoodVertexIndicesIfRegular(
Index, Index[]) const {
return false;
}
inline bool
SurfaceFactoryMeshAdapter::getFaceNeighborhoodFVarValueIndicesIfRegular(
Index, FVarID, Index[]) const {
return false;
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_SURFACE_FACTORY_ADAPTER_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,385 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_TESSELLATION_H
#define OPENSUBDIV3_BFR_TESSELLATION_H
#include "../version.h"
#include "../bfr/parameterization.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
///
/// @brief Encapsulates a specific tessellation pattern of a Parameterization
///
/// Tessellation is a simple class that encapsulates a specified tessellation
/// pattern for a given Parameterization. Tessellation parameters are given
/// on construction and are fixed for its lifetime.
///
/// Methods allow inspection of the pattern in terms of the 2D coordinates of
/// the points comprising the pattern and the faces that connect them. The
/// 2D coordinates are referred to both in the documentation and the interface
/// as "coords" while the faces connecting them are referred to as "facets"
/// (to distinguish from the faces of the mesh, to which a Tessellation is
/// applied).
///
class Tessellation {
public:
///
/// @brief Options configure a Tessellation to specify the nature of
/// both its results and the structure of the coordinate and
/// facet index arrays that its methods will populate.
///
/// The sizes and strides of the target arrays should be specified
/// explicitly as they are not inferred by the presence of other
/// options.
///
/// Modifiers of Options return a reference to itself to facilitate
/// inline usage.
///
class Options {
public:
Options() : _preserveQuads(false), _facetSize4(false),
_coordStride(0), _facetStride(0) { }
/// @brief Select preservation of quads for quad-based subdivision
/// (requires 4-sided facets, default is off)
Options & PreserveQuads(bool on);
/// @brief Return if preservation of quads is set
bool PreserveQuads() const { return _preserveQuads; }
/// @brief Assign the number of indices per facet (must be 3 or 4,
/// default is 3)
Options & SetFacetSize(int numIndices);
// @brief Return the number of indices per facet
int GetFacetSize() const { return 3 + (int)_facetSize4; }
/// @brief Assign the stride between facets (default is facet size)
Options & SetFacetStride(int stride);
/// @brief Return the stride between facets
int GetFacetStride() const { return _facetStride; }
/// @brief Assign the stride between (u,v) pairs (default is 2)
Options & SetCoordStride(int stride);
/// @brief Return the stride between (u,v) pairs
int GetCoordStride() const { return _coordStride; }
private:
unsigned int _preserveQuads : 1;
unsigned int _facetSize4 : 1;
short _coordStride;
short _facetStride;
};
public:
//@{
/// @name Construction and initialization
///
/// Constructors require a Parameterization of a face, a set of one or
/// more tessellation rates, and a standard set of options.
///
/// As with other classes, constructors can produce invalid instances if
/// given obviously invalid arguments, e.g. an invalid Parameterization,
/// non-positive tessellation rate, etc.
///
/// @brief Simple constructor providing a single uniform tessellation rate.
///
/// @param p Parameterization of a face to be tessellated
/// @param uniformRate Integer tessellation rate (non-zero)
/// @param options Options describing tessellation results
///
Tessellation(Parameterization const & p, int uniformRate,
Options const & options = Options());
///
/// @brief General constructor providing multiple tessellation rates for
/// a non-uniform tessellation.
///
/// @param p Parameterization of a face to be tessellated
/// @param numRates The number of tessellation rates provided, which
/// usually includes one per edge of the face (more
/// details below)
/// @param rates The array of non-zero integer tessellation rates
/// @param options Options describing tessellation results
///
/// For a Parameterization of a face with N edges, the acceptable number
/// of tessellation rates can vary. Aside from N "outer" tessellation
/// rates (one for each edge), all faces can have at least one "inner"
/// rate additionally specified while quads can have two inner rates.
///
/// If inner rates are not specified in addition to the N outer rates,
/// they will be inferred (so it is not necessary to initialize quads
/// distinctly from other faces). Similarly -- though less useful -- the
/// smaller set of inner rates can be specified, leaving all outer rates
/// to be inferred.
///
/// For a face with N edges, the full set of acceptable rates and their
/// interpretations is as follows:
///
/// 1 - single explicit inner rate (uniform)
/// 2 - (quads only) two explicit inner rates, outer rates inferred
/// N - explicit edge rates, inner rates inferred
/// N+1 - explicit edge rates, explicit inner rate
/// N+2 - (quads only) explicit edge rates, two explicit inner rates
///
/// When associating rates with edges, note that rates[0] corresponds
/// to the edge between vertices 0 and 1. This is consistent with use
/// elsewhere in OpenSubdiv -- where edge i lies between vertices i and
/// i+1 -- but differs from the conventions used with many hardware
/// tessellation interfaces.
///
Tessellation(Parameterization const & p, int numRates, int const rates[],
Options const & options = Options());
/// @brief Return true if correctly initialized
bool IsValid() const { return _isValid; }
/// @brief Default construction is unavailable
Tessellation() = delete;
Tessellation(Tessellation const &) = delete;
Tessellation & operator=(Tessellation const &) = delete;
~Tessellation();
//@}
//@{
/// @name Simple queries
///
/// Simple queries of a valid Tessellation.
///
/// @brief Return the Parameterization
Parameterization GetParameterization() const { return _param; }
/// @brief Return the size of the face
int GetFaceSize() const { return _param.GetFaceSize(); }
/// @brief Retrieve the rates assigned
int GetRates(int rates[]) const;
/// @brief Return if the pattern is uniform
bool IsUniform() const { return _isUniform; }
//@}
//@{
/// @name Methods to inspect and gather coordinates
///
/// Methods to determine the number of sample points involved in the
/// tessellation pattern and their content are available for the entire
/// pattern, or for parts of the boundary or interior of the pattern.
///
/// The methods that assign the coordinate arrays also return the number
/// of coordinates assigned, so the methods that just return those sizes
/// are not necessary if arrays for the resulting coords have already
/// been sufficiently allocated.
///
/// @brief Return the number of coordinates in the entire pattern
int GetNumCoords() const { return _numInteriorPoints + _numBoundaryPoints; }
/// @brief Return the number of elements between each coordinate
int GetCoordStride() const { return _coordStride; }
/// @brief Return the number of boundary coordinates
int GetNumBoundaryCoords() const { return _numBoundaryPoints; }
/// @brief Return the number of interior coordinates
int GetNumInteriorCoords() const { return _numInteriorPoints; }
/// @brief Return the number of coordinates within a given edge
/// (excluding those at its end vertices)
int GetNumEdgeCoords(int edge) const { return _outerRates[edge] - 1; }
/// @brief Retrieve the coordinates for the entire pattern
template <typename REAL>
int GetCoords(REAL coordTuples[]) const;
/// @brief Retrieve the coordinates for the boundary
template <typename REAL>
int GetBoundaryCoords(REAL coordTuples[]) const;
/// @brief Retrieve the coordinates for the boundary
template <typename REAL>
int GetInteriorCoords(REAL coordTuples[]) const;
/// @brief Retrieve the coordinate for a given vertex of the face
template <typename REAL>
int GetVertexCoord(int vertex, REAL coordTuples[]) const;
/// @brief Retrieve the coordinates for a given edge of the face
/// (excluding those at its end vertices)
template <typename REAL>
int GetEdgeCoords(int edge, REAL coordTuples[]) const;
//@}
//@{
/// @name Methods to inspect and gather facets
///
/// Methods to inspect the number and values of facets. Facets are
/// simply integer tuples of size 3 or 4 which contain the indices
/// of the coordinates generated by the Tessellation.
///
/// Unlike the coordinates -- which can be separated into those on
/// the boundary or interior of the pattern -- the facets are not
/// distinguished in any way.
///
/// @brief Return the number of facets in the entire pattern
int GetNumFacets() const { return _numFacets; }
/// @brief Return the number of indices assigned to each facet
int GetFacetSize() const { return _facetSize; }
/// @brief Return the number of elements between each facet
int GetFacetStride() const { return _facetStride; }
/// @brief Retrieve the facet indices for the entire pattern
int GetFacets(int facetTuples[]) const;
//@}
//@{
/// @name Methods to modify the coordinate indices of facets
///
/// Methods to modify the coordinate indices of facets rely on the
/// coordinate indices being identifiable as those of boundary or
/// interior coordinates. The first N coordinates generated by
/// Tessellation will be on the boundary, while the remaining M
/// will be for the interior.
///
/// The boundary and interior coordinate indices can be transformed
/// collectively or separately by offsets or by explicit reassignment.
/// Both the boundary and interior indices must be modified at the
/// same time while the Tessellation can distinguish them, i.e. a
/// boundary coord is identified by index < N and an interior coord
/// by index >= N.
///
/// @brief Apply a common offset to all facet coordinate indices
void TransformFacetCoordIndices(int facetTuples[], int commonOffset);
/// @brief Reassign indices of boundary coordinates while offseting
/// those of interior coordinates
void TransformFacetCoordIndices(int facetTuples[],
int const boundaryIndices[],
int interiorOffset);
/// @brief Reassign all facet coordinate indices
void TransformFacetCoordIndices(int facetTuples[],
int const boundaryIndices[],
int const interiorIndices[]);
//@}
private:
// Private initialization methods:
bool validateArguments(Parameterization const & p,
int nRates, int const rates[], Options const & options);
void initialize(Parameterization const & p,
int nRates, int const rates[], Options const & options);
void initializeDefaults();
int initializeRates(int nRates, int const rates[]);
void initializeInventoryForParamTri(int sumOfOuterRates);
void initializeInventoryForParamQuad(int sumOfOuterRates);
void initializeInventoryForParamQPoly(int sumOfOuterRates);
private:
// Private members:
Parameterization _param;
unsigned short _isValid : 1;
unsigned short _isUniform : 1;
unsigned short _triangulate : 1;
unsigned short _singleFace : 1;
unsigned short _segmentedFace : 1;
unsigned short _triangleFan : 1;
unsigned short _splitQuad : 1;
short _facetSize;
int _facetStride;
int _coordStride;
int _numGivenRates;
int _numBoundaryPoints;
int _numInteriorPoints;
int _numFacets;
int _innerRates[2];
int* _outerRates;
int _outerRatesLocal[4];
};
//
// Inline implementations:
//
inline Tessellation::Options &
Tessellation::Options::PreserveQuads(bool on) {
_preserveQuads = on;
return *this;
}
inline Tessellation::Options &
Tessellation::Options::SetFacetSize(int numIndices) {
_facetSize4 = (numIndices == 4);
return *this;
}
inline Tessellation::Options &
Tessellation::Options::SetFacetStride(int stride) {
_facetStride = (short) stride;
return *this;
}
inline Tessellation::Options &
Tessellation::Options::SetCoordStride(int stride) {
_coordStride = (short) stride;
return *this;
}
template <typename REAL>
inline int
Tessellation::GetVertexCoord(int vertex, REAL coord[]) const {
_param.GetVertexCoord(vertex, coord);
return 1;
}
template <typename REAL>
inline int
Tessellation::GetCoords(REAL coordTuples[]) const {
int nCoords = GetBoundaryCoords(coordTuples);
nCoords += GetInteriorCoords(coordTuples + nCoords * _coordStride);
return nCoords;
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_TESSELLATION */

View File

@ -0,0 +1,122 @@
//
// Copyright 2021 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.
//
#include "../bfr/vertexDescriptor.h"
#include "../bfr/limits.h"
#include "../sdc/crease.h"
#include <cstring>
#include <cstdio>
#include <algorithm>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Main initialize/finalize methods used by clients to delimit the
// assignment (most work is now handled by the containing class):
//
bool
VertexDescriptor::Initialize(int numFaces) {
// Mark invalid if too many or too few incident faces specified:
_isValid = (numFaces > 0) && (numFaces <= Limits::MaxValence());
_numFaces = _isValid ? (short) numFaces : 0;
// Initialize all other members regardless of the above:
_vertSharpness = 0.0f;
_isManifold = false;
_isBoundary = false;
_hasFaceSizes = false;
_hasEdgeSharpness = false;
_isInitialized = _isValid;
_isFinalized = false;
return _isInitialized;
}
bool
VertexDescriptor::Finalize() {
// Fail if already invalid:
if (!_isValid) return false;
// Test for valid face size assignments while converting the sizes
// to offsets. Also detect if the faces are all the same size -- in
// which case, ignore the explicit assignments:
if (_hasFaceSizes) {
int size0 = _faceSizeOffsets[0];
bool sameSizes = true;
int sum = 0;
for (int i = 0; i < _numFaces; ++i) {
int faceSize = _faceSizeOffsets[i];
if ((faceSize < 3) || (faceSize > Limits::MaxFaceSize())) {
_isValid = false;
return false;
}
sameSizes &= (faceSize == size0);
_faceSizeOffsets[i] = sum;
sum += faceSize;
}
_faceSizeOffsets[_numFaces] = sum;
// No need to make use of explicit face sizes and offsets:
if (sameSizes) {
_hasFaceSizes = false;
}
}
_isFinalized = true;
return true;
}
//
// Internal methods for resizing local buffers:
//
void
VertexDescriptor::initFaceSizes() {
_faceSizeOffsets.SetSize(_numFaces + 1);
std::fill(&_faceSizeOffsets[0], &_faceSizeOffsets[_numFaces + 1], 0);
_hasFaceSizes = true;
}
void
VertexDescriptor::initEdgeSharpness() {
_faceEdgeSharpness.SetSize(_numFaces * 2);
std::fill(&_faceEdgeSharpness[0], &_faceEdgeSharpness[_numFaces * 2], 0.0f);
_hasEdgeSharpness = true;
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,449 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_VERTEX_DESCRIPTOR_H
#define OPENSUBDIV3_BFR_VERTEX_DESCRIPTOR_H
#include "../version.h"
#include "../vtr/stackBuffer.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
///
/// @brief Simple class used by subclasses of SurfaceFactory to describe a
/// vertex
///
/// VertexDescriptor is a simple class used by SurfaceFactory and its
/// subclasses to provide a complete topological description around the
/// vertex of a face, i.e. its valence, the sizes of its incident faces,
/// sharpness values, etc.
///
/// Instances are created and partially initialized by SurfaceFactory
/// before being passed to its subclasses to be fully populated. So
/// public construction is not available (or useful).
///
//
// WIP - need to migrate some of these comments into Doxygen
// - others will be moved to the external documentation
//
// It is used by subclasses of SurfaceFactory to provide a complete
// topological description for each vertex of a face, i.e. invoked via
// the virtual method:
//
// int populateFaceVertexDescriptor(Index baseFace,
// int cornerVertex,
// VertexDescriptor & v) const;
//
// Assignment of the full topology can be involved in the presence of
// irregular faces, non-manifold topology or creasing around a vertex, but
// many cases will be simple. For example, to specify a regular boundary
// vertex of a Catmark mesh without any optional sharpness:
//
// int numIncidentFaces = 2;
// bool vertexOnBoundary = true;
//
// vd.Initialize(numIncidentFaces);
// vd.SetManifold(true);
// vd.SetBoundary(vertexOnBoundary);
// vd.ClearIncidentFaceSizes();
// vd.Finalize();
//
// For a more general example, to assign a vertex of some valence whose
// incident faces are of different sizes (e.g. required when triangles
// appear around a vertex in an otherwise quad-dominant Catmark mesh):
//
// int numIncidentFaces = meshVertex.GetNumIncidentFaces();
// bool vertexOnBoundary = meshVertex.IsBoundar();
//
// vd.Initialize(numIncidentFaces);
// vd.SetManifold(true);
// vd.SetBoundary(vertexOnBoundary);
//
// for (int i = 0; i < numIncidentFaces; ++i) {
// vd.SetIncidentFaceSize(i, meshVertex.GetIncidentFaceSize(i));
// }
// vd.Finalize();
//
// These examples specify the incident faces as forming a manifold ring
// (or half-ring) around the vertex, i.e. they can be specified as a
// continuous, connected sequence in counter-clockwise order (and also
// without degeneracies). In the case of a boundary vertex, the first
// face must be on the leading edge of the boundary while the last is on
// the trailing edge. For an interior vertex, which face is specified
// first does not matter (since the set is periodic).
//
// In both cases, the location of the base face in this sequence -- the
// face whose corner vertex is being described here -- must be specified
// in the return value to populateFaceVertexDescriptor() (e.g. when a
// boundary vertex has 3 incident faces, a return value of 0, 1 or 2
// will indicate which is the base face).
//
// The corresponding methods to specify mesh control vertex indices (or
// face-varying indices) complete the specification of the neighborhood:
//
// int getFaceCornerVertexIndices(Index baseFace, int cornerVertex,
// Index vertexIndices[]) const;
//
// int getFaceCornerFVarValueIndices(Index baseFace, int cornerVertex,
// Index fvarValueIndices[],
// int fvarChannel) const;
//
// and are invoked by the Factory when needed.
//
// For each incident face, the indices for all vertices of that face are
// to be specified (not the one-ring or some other subset). These indices
// must also be specified in an orientation relative to the vertex, i.e.
// for a vertex A and an incident face with face-vertices that may be
// stored internally as {D, C, A, B}, they must be specified with A first
// as {A, B, C, D}. This may seem a bit cumbersome, but it has clear
// advantages when dealing with face-varying indices and unordered faces.
//
// More compact ways of specifying vertex indices for ordered, manifold
// cases may be worth exploring in future, but face-varying indices and
// non-manifold (unordered) vertices will always require such a full set,
// so both methods will need to co-exist.
//
class VertexDescriptor {
public:
// The full declaration must be enclosed by calls to these methods:
//
// Note that vertex valences or face sizes in excess of those defined
// in Bfr::Limits (typically 16-bits) are not valid. When specifying
// values in excess of these limits, initialization will fail and/or
// the descriptor will be marked invalid and finalization will fail.
//
//@{
/// @name Methods to begin and end specification
///
/// Partially constructed instances are populated using a set of
/// methods between calls to Initialize() and Finalize(). Both return
/// false to indicate failure due to invalid input, or the instance
/// can be inspected after each to determine if valid.
///
/// @brief Initialize specification with the number of incident faces
bool Initialize(int numIncidentFaces);
/// @brief Terminate the sequence of specifications
bool Finalize();
/// @brief Return if instance is valid
bool IsValid() const;
//@}
//
// WIP - need to migrate these comments into Doxygen
//
// Three groups of methods describe the topology around a vertex:
// - simple properties (vertex is a boundary, manifold, etc.)
// - sizes of incident faces (constant or size for each face)
// - sharpness of the vertex and its incident edges (optional)
//
// Manifold and boundary conditions:
//
// The manifold property is a strict condition but preferred for
// efficiency and is usually available from common connected mesh
// representations. When declaring the topology as "manifold",
// the Factory assumes the following:
//
// - all incident faces are "ordered" (counter-clockwise)
// - all incident faces are consistently oriented
// - all incident edges are non-degenerate
//
// If not certain that all of these conditions are met, it is best
// to not declare manifold -- leaving the Factory to make sense of
// the set of incident faces from the face-vertex indices that are
// provided elsewhere.
//
//@{
/// @name Methods to specify topology
///
/// Methods to specify the overall topology, the sizes of incident
/// faces and any assigned sharpness values.
/// @brief Declare the vertex neighborhood as manifold (ordered)
void SetManifold(bool isManifold);
/// @brief Declare the vertex neighborhood as being on a boundary
void SetBoundary(bool isOnBoundary);
/// @brief Assign the size of an incident face
void SetIncidentFaceSize(int faceIndex, int faceSize);
/// @brief Remove any assigned sizes of incident faces
void ClearIncidentFaceSizes();
/// @brief Assign sharpness to the vertex
void SetVertexSharpness(float sharpness);
/// @brief Remove any sharpness assigned to the vertex
void ClearVertexSharpness();
/// @brief Assign sharpness to the edge of a manifold neighborhood
///
/// For use with a vertex declared manifold only, assigns a given
/// sharpness to the indicated edge in the ordered sequence of edges
/// around the vertex. In the case of a boundary vertex, the number
/// of incident edges in this ordered sequence will exceed the number
/// of incident faces by one.
///
/// @param edgeIndex Index of the edge in the ordered sequence
/// @param edgeSharpness Sharpness to be assigned to the edge
///
void SetManifoldEdgeSharpness(int edgeIndex, float edgeSharpness);
/// @brief Assign sharpness to the edges of an incident face
///
/// In all cases, sharpness can be assigned to edges by associating
/// those edges with their incident faces. This method assigns sharpness
/// to the two edges incident edges of an incident face. An alternative
/// is available for the case of a manifold vertex.
///
/// @param faceIndex Index of the incident face
/// @param leadingEdgeSharp Sharpness to assign to the leading edge
/// of the incident face, i.e. the edge of the
/// face following the vertex.
/// @param trailingEdgeSharp Sharpness to assign to the trailing edge
/// of the incident face, i.e. the edge of the
/// face preceding the vertex.
///
void SetIncidentFaceEdgeSharpness(int faceIndex, float leadingEdgeSharp,
float trailingEdgeSharp);
/// @brief Remove any sharpness assigned to the incident edges
void ClearEdgeSharpness();
//@}
//@{
/// @name Methods to inspect topology to confirm assignment
///
/// While the public interface is primarily intended for assignment,
/// methods are available to inspect intermediate results.
///
/// @brief Return if vertex neighborhood is manifold
bool IsManifold() const;
/// @brief Return if vertex neighborhood is on a boundary
bool IsBoundary() const;
/// @brief Return if the sizes of incident faces are assigned
bool HasIncidentFaceSizes() const;
/// @brief Return the size of an incident face
int GetIncidentFaceSize(int faceIndex) const;
/// @brief Return if sharpness was assigned to the vertex
bool HasVertexSharpness() const;
/// @brief Return the sharpness of the vertex
float GetVertexSharpness() const;
/// @brief Return if sharpness was assigned to the incident edges
bool HasEdgeSharpness() const;
/// @brief Return the sharpness assigned to a manifold edge
float GetManifoldEdgeSharpness(int edgeIndex) const;
/// @brief Return the sharpness assigned to edges of an incident face
void GetIncidentFaceEdgeSharpness(int faceIndex,
float * leadingEdgeSharp, float * trailingEdgeSharp) const;
//@}
protected:
/// @cond PROTECTED
friend class FaceVertex;
VertexDescriptor() { }
~VertexDescriptor() { }
typedef Vtr::internal::StackBuffer<int,8,true> IntBuffer;
typedef Vtr::internal::StackBuffer<float,16,true> FloatBuffer;
void initFaceSizes();
void initEdgeSharpness();
/// @endcond
protected:
/// @cond PROTECTED
// Member variables assigned through the above interface:
unsigned short _isValid : 1;
unsigned short _isInitialized : 1;
unsigned short _isFinalized : 1;
unsigned short _isManifold : 1;
unsigned short _isBoundary : 1;
unsigned short _hasFaceSizes : 1;
unsigned short _hasEdgeSharpness : 1;
short _numFaces;
float _vertSharpness;
FloatBuffer _faceEdgeSharpness;
IntBuffer _faceSizeOffsets;
/// @endcond
};
//
// Public inline methods for simple assignment:
//
inline bool
VertexDescriptor::IsValid() const {
return _isValid;
}
inline void
VertexDescriptor::SetManifold(bool isManifold) {
_isManifold = isManifold;
}
inline bool
VertexDescriptor::IsManifold() const {
return _isManifold;
}
inline void
VertexDescriptor::SetBoundary(bool isBoundary) {
_isBoundary = isBoundary;
}
inline bool
VertexDescriptor::IsBoundary() const {
return _isBoundary;
}
//
// Public inline methods involving sizes of incident faces:
//
inline bool
VertexDescriptor::HasIncidentFaceSizes() const {
return _hasFaceSizes;
}
inline void
VertexDescriptor::ClearIncidentFaceSizes() {
_hasFaceSizes = false;
}
inline void
VertexDescriptor::SetIncidentFaceSize(int incFaceIndex, int faceSize) {
if (!_hasFaceSizes) initFaceSizes();
_faceSizeOffsets[incFaceIndex] = faceSize;
}
inline int
VertexDescriptor::GetIncidentFaceSize(int incFaceIndex) const {
return _isFinalized ?
(_faceSizeOffsets[incFaceIndex+1] - _faceSizeOffsets[incFaceIndex]) :
_faceSizeOffsets[incFaceIndex];
}
//
// Public inline methods involving vertex sharpness:
//
inline bool
VertexDescriptor::HasVertexSharpness() const {
return _vertSharpness > 0.0f;
}
inline void
VertexDescriptor::ClearVertexSharpness() {
_vertSharpness = 0.0f;
}
inline void
VertexDescriptor::SetVertexSharpness(float vertSharpness) {
_vertSharpness = vertSharpness;
}
inline float
VertexDescriptor::GetVertexSharpness() const {
return _vertSharpness;
}
//
// Public inline methods involving vertex sharpness:
//
inline bool
VertexDescriptor::HasEdgeSharpness() const {
return _hasEdgeSharpness;
}
inline void
VertexDescriptor::ClearEdgeSharpness() {
_hasEdgeSharpness = false;
}
inline void
VertexDescriptor::SetManifoldEdgeSharpness(int edgeIndex, float sharpness) {
if (!_hasEdgeSharpness) initEdgeSharpness();
// Assign the leading edge of the face after the edge (even index):
if (edgeIndex < _numFaces) {
_faceEdgeSharpness[2*edgeIndex] = sharpness;
}
// Assign the trailing edge of the face before the edge (odd index):
if (edgeIndex > 0) {
_faceEdgeSharpness[2*edgeIndex-1] = sharpness;
} else if (!IsBoundary()) {
_faceEdgeSharpness[2*_numFaces-1] = sharpness;
}
}
inline float
VertexDescriptor::GetManifoldEdgeSharpness(int edgeIndex) const {
// All edges are first of the pair (even index) except last of boundary
return _faceEdgeSharpness[2*edgeIndex - (edgeIndex == _numFaces)];
}
inline void
VertexDescriptor::SetIncidentFaceEdgeSharpness(int faceIndex,
float leadingEdgeSharpness, float trailingEdgeSharpness) {
if (!_hasEdgeSharpness) initEdgeSharpness();
_faceEdgeSharpness[2*faceIndex ] = leadingEdgeSharpness;
_faceEdgeSharpness[2*faceIndex+1] = trailingEdgeSharpness;
}
inline void
VertexDescriptor::GetIncidentFaceEdgeSharpness(int faceIndex,
float * leadingEdgeSharpness, float * trailingEdgeSharpness) const {
*leadingEdgeSharpness = _faceEdgeSharpness[2*faceIndex];
*trailingEdgeSharpness = _faceEdgeSharpness[2*faceIndex+1];
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_VERTEX_DESCRIPTOR_H */

163
opensubdiv/bfr/vertexTag.h Normal file
View File

@ -0,0 +1,163 @@
//
// Copyright 2021 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.
//
#ifndef OPENSUBDIV3_BFR_VERTEX_TAG_H
#define OPENSUBDIV3_BFR_VERTEX_TAG_H
#include "../version.h"
#include <cstring>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// VertexTag is a simple set of bits that identify exceptional properties
// at the corner vertices of a face that warrant closer inspection (and
// potential additional processing). As with some bitfields in Far, this
// supports bitwise-OR so that tags for the corners of a face can quickly
// be combined to determine properties of the associated limit surface.
//
// In order to accommodate the two separate uses more clearly -- that of
// a set of bits applying to a single corner/vertex versus a set of bits
// resulting from the combination (bitwise-OR) of several -- the bitfield
// is defined as a base class and two separate classes are derived from
// it to suit those purposes.
//
class FeatureBits {
public:
FeatureBits() { }
~FeatureBits() { }
public:
// Integer/bit conversions and operations:
typedef unsigned short IntType;
IntType GetBits() const {
IntType bits;
std::memcpy(&bits, this, sizeof(*this));
return bits;
}
void SetBits(IntType bits) {
std::memcpy(this, &bits, sizeof(*this));
}
void Clear() {
SetBits(0);
}
protected:
friend class FaceVertex;
friend struct FaceVertexSubset;
IntType _boundaryVerts : 1;
IntType _infSharpVerts : 1;
IntType _infSharpEdges : 1;
IntType _infSharpDarts : 1;
IntType _semiSharpVerts : 1;
IntType _semiSharpEdges : 1;
IntType _unCommonFaceSizes : 1;
IntType _irregularFaceSizes : 1;
IntType _unOrderedFaces : 1;
IntType _nonManifoldVerts : 1;
IntType _boundaryNonSharp : 1;
};
//
// VertexTag wraps the FeatureBits for use with a single corner/vertex:
//
// Note that a bit is not defined to detect extra-ordinary or regular
// valence. Since subsets of the topology are ultimately used in the
// limit surface definition, and face-varying surfaces are potentially
// subsets of subsets, we intentionally avoid having to re-compute
// that bit for each subset. Such a bit has little value for a single
// corner in Bfr, so the collective presence is determined when the
// surface definition is finalized in the regular/irregular test.
//
class VertexTag : public FeatureBits {
public:
VertexTag() { }
~VertexTag() { }
// Queries for single corner/vertex (some reversing sense of the bit):
bool IsBoundary() const { return _boundaryVerts; }
bool IsInterior() const { return !_boundaryVerts; }
bool IsInfSharp() const { return _infSharpVerts; }
bool HasInfSharpEdges() const { return _infSharpEdges; }
bool IsInfSharpDart() const { return _infSharpDarts; }
bool IsSemiSharp() const { return _semiSharpVerts; }
bool HasSemiSharpEdges() const { return _semiSharpEdges; }
bool HasUnCommonFaceSizes() const { return _unCommonFaceSizes; }
bool HasIrregularFaceSizes() const { return _irregularFaceSizes; }
bool IsOrdered() const { return !_unOrderedFaces; }
bool IsUnOrdered() const { return _unOrderedFaces; }
bool IsManifold() const { return !_nonManifoldVerts; }
bool IsNonManifold() const { return _nonManifoldVerts; }
bool HasNonSharpBoundary() const { return _boundaryNonSharp; }
bool HasSharpEdges() const { return HasInfSharpEdges() ||
HasSemiSharpEdges(); }
};
//
// MultiVertexTag wraps the FeatureBits for use with bits combined from
// several corners/vertices. It includes the Combine() method to apply
// the bitwise-OR with a given VertexTag, in addition to using different
// names for the access methods to reflect their collective nature (e.g.
// the use of "has" versus "is").
//
class MultiVertexTag : public FeatureBits {
public:
MultiVertexTag() { }
~MultiVertexTag() { }
// Queries for multiple VertexTags combined into one:
bool HasBoundaryVertices() const { return _boundaryVerts; }
bool HasInfSharpVertices() const { return _infSharpVerts; }
bool HasInfSharpEdges() const { return _infSharpEdges; }
bool HasInfSharpDarts() const { return _infSharpDarts; }
bool HasSemiSharpVertices() const { return _semiSharpVerts; }
bool HasSemiSharpEdges() const { return _semiSharpEdges; }
bool HasUnCommonFaceSizes() const { return _unCommonFaceSizes; }
bool HasIrregularFaceSizes() const { return _irregularFaceSizes; }
bool HasUnOrderedVertices() const { return _unOrderedFaces; }
bool HasNonManifoldVertices() const { return _nonManifoldVerts; }
bool HasNonSharpBoundary() const { return _boundaryNonSharp; }
bool HasSharpVertices() const { return HasInfSharpVertices() ||
HasSemiSharpVertices(); }
bool HasSharpEdges() const { return HasInfSharpEdges() ||
HasSemiSharpEdges(); }
void Combine(VertexTag const & tag) {
SetBits(GetBits() | tag.GetBits());
}
};
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_BFR_VERTEX_TAG_H */

View File

@ -222,6 +222,8 @@ protected:
// Copy constructor exposed via the factory class:
TopologyRefiner(TopologyRefiner const & source);
public:
// Levels and Refinements available internally (avoids need for more friends)
Vtr::internal::Level & getLevel(int l) { return *_levels[l]; }
Vtr::internal::Level const & getLevel(int l) const { return *_levels[l]; }

View File

@ -236,11 +236,11 @@ D3D11ComputeEvaluator::Compile(BufferDescriptor const &srcDesc,
ss << _workGroupSize; std::string workgroupSizeValue(ss.str()); ss.str("");
D3D_SHADER_MACRO defines[] =
{ "LENGTH", lengthValue.c_str(),
"SRC_STRIDE", srcStrideValue.c_str(),
"DST_STRIDE", dstStrideValue.c_str(),
"WORK_GROUP_SIZE", workgroupSizeValue.c_str(),
0, 0 };
{ { "LENGTH", lengthValue.c_str() },
{ "SRC_STRIDE", srcStrideValue.c_str() },
{ "DST_STRIDE", dstStrideValue.c_str() },
{ "WORK_GROUP_SIZE", workgroupSizeValue.c_str() },
{ 0, 0 } };
ID3DBlob * computeShaderBuffer = NULL;
ID3DBlob * errorBuffer = NULL;

View File

@ -26,6 +26,8 @@ add_subdirectory(common)
if (NOT NO_REGRESSION)
add_subdirectory(bfr_evaluate)
add_subdirectory(hbr_regression)
add_subdirectory(far_regression)

View File

@ -0,0 +1,54 @@
#
# Copyright 2021 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.
#
include_directories("${OPENSUBDIV_INCLUDE_DIR}")
set(SOURCE_FILES
main.cpp
bfrSurfaceEvaluator.cpp
farPatchEvaluator.cpp
)
set(PLATFORM_LIBRARIES
"${OSD_LINK_TARGET}"
)
osd_add_executable(bfr_evaluate "regression"
${SOURCE_FILES}
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:bfr_obj>
$<TARGET_OBJECTS:regression_common_obj>
)
install(TARGETS bfr_evaluate DESTINATION "${CMAKE_BINDIR_BASE}")
add_test(bfr_evaluate_pos ${EXECUTABLE_OUTPUT_PATH}/bfr_evaluate
-all -silent -l 3 -pass 2 -d1)
add_test(bfr_evaluate_uv1 ${EXECUTABLE_OUTPUT_PATH}/bfr_evaluate
-all -silent -l 3 -pass 0 -skippos -uv -uvint 1)
add_test(bfr_evaluate_uv5 ${EXECUTABLE_OUTPUT_PATH}/bfr_evaluate
-all -silent -l 3 -pass 0 -skippos -uv -uvint 5)

View File

@ -0,0 +1,208 @@
//
// Copyright 2021 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.
//
#include "bfrSurfaceEvaluator.h"
template <typename REAL>
BfrSurfaceEvaluator<REAL>::BfrSurfaceEvaluator(
Far::TopologyRefiner const & baseMesh,
Vec3Vector const & basePos,
Vec3Vector const & baseUVs,
FactoryOptions const & factoryOptions) :
_baseMesh(baseMesh),
_baseMeshPos(basePos),
_baseMeshUVs(baseUVs),
_factory(baseMesh, factoryOptions) {
}
template <typename REAL>
bool
BfrSurfaceEvaluator<REAL>::FaceHasLimit(IndexType baseFace) const {
return _factory.FaceHasLimitSurface(baseFace);
}
template <typename REAL>
void
BfrSurfaceEvaluator<REAL>::Evaluate(IndexType baseFace,
TessCoordVector const & tessCoords,
EvalResults<REAL> & results) const {
// Allocate vectors for the properties to be evaluated:
int numCoords = (int) tessCoords.size() / 2;
results.Resize(numCoords);
// Create the Surfaces for position and UV (optional) and assert if
// not valid, since a limit surface is expected here. (Note we may
// create the position surface but not actually evaluate it.)
SurfaceType pSurface;
SurfaceType uvSurface;
// Figure out how to get a command line arg here to run both
bool initSeparate = false;
if (initSeparate || !results.evalUV) {
_factory.InitVertexSurface(baseFace, &pSurface);
if (results.evalUV) {
_factory.InitFaceVaryingSurface(baseFace, &uvSurface);
}
} else {
_factory.InitSurfaces(baseFace, &pSurface, &uvSurface);
}
assert(pSurface.IsValid());
assert(uvSurface.IsValid() == results.evalUV);
// Evaluate directly or using stencils:
if (results.useStencils) {
evaluateByStencils(pSurface, uvSurface, tessCoords, results);
} else {
evaluateDirectly(pSurface, uvSurface, tessCoords, results);
}
}
template <typename REAL>
void
BfrSurfaceEvaluator<REAL>::evaluateDirectly(
SurfaceType const & pSurface, SurfaceType const & uvSurface,
TessCoordVector const & tessCoords, EvalResults<REAL> & results) const {
int numCoords = (int) tessCoords.size() / 2;
if (results.evalPosition) {
Vec3Vector baseFacePos(pSurface.GetNumPatchPoints());
REAL const * meshPoints = &_baseMeshPos[0][0];
REAL * patchPoints = &baseFacePos[0][0];
pSurface.PreparePatchPoints(meshPoints, 3, patchPoints, 3);
REAL const * st = &tessCoords[0];
for (int i = 0; i < numCoords; ++i, st += 2) {
if (!results.eval1stDeriv) {
pSurface.Evaluate(st, patchPoints, 3, &results.p[i][0]);
} else if (!results.eval2ndDeriv) {
pSurface.Evaluate(st, patchPoints, 3,
&results.p[i][0], &results.du[i][0], &results.dv[i][0]);
} else {
pSurface.Evaluate(st, patchPoints, 3,
&results.p[i][0], &results.du[i][0], &results.dv[i][0],
&results.duu[i][0], &results.duv[i][0], &results.dvv[i][0]);
}
}
}
if (results.evalUV) {
Vec3Vector baseFaceUVs(uvSurface.GetNumPatchPoints());
REAL const * meshPoints = &_baseMeshUVs[0][0];
REAL * patchPoints = &baseFaceUVs[0][0];
uvSurface.PreparePatchPoints(meshPoints, 3, patchPoints, 3);
REAL const * st = &tessCoords[0];
for (int i = 0; i < numCoords; ++i, st += 2) {
uvSurface.Evaluate(st, patchPoints, 3, &results.uv[i][0]);
}
}
}
template <typename REAL>
void
BfrSurfaceEvaluator<REAL>::evaluateByStencils(
SurfaceType const & pSurface, SurfaceType const & uvSurface,
TessCoordVector const & tessCoords, EvalResults<REAL> & results) const {
std::vector<REAL> stencilWeights;
int numCoords = (int) tessCoords.size() / 2;
if (results.evalPosition) {
stencilWeights.resize(6 * pSurface.GetNumControlPoints());
REAL * sP = &stencilWeights[0];
REAL * sDu = sP + pSurface.GetNumControlPoints();
REAL * sDv = sDu + pSurface.GetNumControlPoints();
REAL * sDuu = sDv + pSurface.GetNumControlPoints();
REAL * sDuv = sDuu + pSurface.GetNumControlPoints();
REAL * sDvv = sDuv + pSurface.GetNumControlPoints();
REAL const * st = &tessCoords[0];
for (int i = 0; i < numCoords; ++i, st += 2) {
REAL const * meshPos = &_baseMeshPos[0][0];
if (!results.eval1stDeriv) {
pSurface.EvaluateStencil(st, &sP[0]);
} else if (!results.eval2ndDeriv) {
pSurface.EvaluateStencil(st, &sP[0], &sDu[0], &sDv[0]);
} else {
pSurface.EvaluateStencil(st, &sP[0], &sDu[0], &sDv[0],
&sDuu[0], &sDuv[0], &sDvv[0]);
}
if (results.evalPosition) {
pSurface.ApplyStencilFromMesh(&sP[0], meshPos, 3,
&results.p[i][0]);
}
if (results.eval1stDeriv) {
pSurface.ApplyStencilFromMesh(&sDu[0], meshPos, 3,
&results.du[i][0]);
pSurface.ApplyStencilFromMesh(&sDv[0], meshPos, 3,
&results.dv[i][0]);
}
if (results.eval2ndDeriv) {
pSurface.ApplyStencilFromMesh(&sDuu[0], meshPos, 3,
&results.duu[i][0]);
pSurface.ApplyStencilFromMesh(&sDuv[0], meshPos, 3,
&results.duv[i][0]);
pSurface.ApplyStencilFromMesh(&sDvv[0], meshPos, 3,
&results.dvv[i][0]);
}
}
}
if (results.evalUV) {
REAL const * meshUVs = &_baseMeshUVs[0][0];
stencilWeights.resize(uvSurface.GetNumControlPoints());
REAL * sUV = &stencilWeights[0];
REAL const * st = &tessCoords[0];
for (int i = 0; i < numCoords; ++i, st += 2) {
uvSurface.EvaluateStencil(st, &sUV[0]);
uvSurface.ApplyStencilFromMesh(&sUV[0], meshUVs, 3,
&results.uv[i][0]);
}
}
}
//
// Explicit instantiation for float and double:
//
template class BfrSurfaceEvaluator<float>;
template class BfrSurfaceEvaluator<double>;

View File

@ -0,0 +1,82 @@
//
// Copyright 2021 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.
//
#include "./types.h"
#include <opensubdiv/far/topologyRefiner.h>
#include <opensubdiv/bfr/refinerSurfaceFactory.h>
#include <opensubdiv/bfr/tessellation.h>
using namespace OpenSubdiv;
using namespace OpenSubdiv::OPENSUBDIV_VERSION;
template <typename REAL>
class BfrSurfaceEvaluator {
public:
typedef Bfr::Surface<REAL> SurfaceType;
typedef Bfr::RefinerSurfaceFactory<> SurfaceFactory;
typedef Bfr::SurfaceFactory::Options FactoryOptions;
typedef Bfr::SurfaceFactory::Index IndexType;
typedef std::vector<REAL> TessCoordVector;
typedef std::vector< Vec3<REAL> > Vec3Vector;
public:
BfrSurfaceEvaluator(Far::TopologyRefiner const & baseMesh,
Vec3Vector const & basePos,
Vec3Vector const & baseUVs,
FactoryOptions const & factoryOptions);
~BfrSurfaceEvaluator() { }
public:
bool FaceHasLimit(IndexType baseFace) const;
void Evaluate(IndexType baseface,
TessCoordVector const & tessCoords,
EvalResults<REAL> & results) const;
private:
void evaluateDirectly(SurfaceType const & posSurface,
SurfaceType const & uvSurface,
TessCoordVector const & tessCoords,
EvalResults<REAL> & results) const;
void evaluateByStencils(SurfaceType const & posSurface,
SurfaceType const & uvSurface,
TessCoordVector const & tessCoords,
EvalResults<REAL> & results) const;
private:
Far::TopologyRefiner const & _baseMesh;
Vec3Vector const & _baseMeshPos;
Vec3Vector const & _baseMeshUVs;
SurfaceFactory _factory;
};

View File

@ -0,0 +1,291 @@
//
// Copyright 2021 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.
//
#include "farPatchEvaluator.h"
#include <opensubdiv/far/topologyRefinerFactory.h>
#include <opensubdiv/far/topologyDescriptor.h>
#include <opensubdiv/far/primvarRefiner.h>
#include <opensubdiv/far/stencilTable.h>
template <typename REAL>
FarPatchEvaluator<REAL>::FarPatchEvaluator(
Far::TopologyRefiner const & baseMesh,
Vec3RealVector const & basePos,
Vec3RealVector const & baseUVs,
BfrSurfaceOptions const & bfrSurfaceOptions) :
_baseMesh(baseMesh),
_baseMeshPos(basePos),
_baseMeshUVs(baseUVs) {
//
// Initialize simple members first:
//
_regFaceSize = Sdc::SchemeTypeTraits::GetRegularFaceSize(
baseMesh.GetSchemeType());
//
// Declare options to use in construction of PatchTable et al:
//
int primaryLevel = bfrSurfaceOptions.GetApproxLevelSharp();
int secondaryLevel = bfrSurfaceOptions.GetApproxLevelSmooth();
Far::PatchTableFactory::Options patchOptions(primaryLevel);
patchOptions.SetPatchPrecision<REAL>();
patchOptions.SetFVarPatchPrecision<REAL>();
patchOptions.useInfSharpPatch = true;
patchOptions.generateLegacySharpCornerPatches = false;
patchOptions.shareEndCapPatchPoints = false;
patchOptions.endCapType =
Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
bool hasUVs = !baseUVs.empty();
int fvarChannel = 0;
patchOptions.generateFVarTables = hasUVs;
patchOptions.numFVarChannels = hasUVs ? 1 : 0;
patchOptions.fvarChannelIndices = &fvarChannel;
patchOptions.generateFVarLegacyLinearPatches = false;
patchOptions.generateVaryingTables = false;
Far::TopologyRefiner::AdaptiveOptions refineOptions =
patchOptions.GetRefineAdaptiveOptions();
refineOptions.isolationLevel = primaryLevel;
refineOptions.secondaryLevel = secondaryLevel;
//
// Create a TopologyRefiner (sharing the base) to adaptively refine
// and create the associated PatchTable:
//
Far::TopologyRefiner *patchRefiner =
Far::TopologyRefinerFactory<Far::TopologyDescriptor>::Create(
baseMesh);
patchRefiner->RefineAdaptive(refineOptions);
_patchTable = Far::PatchTableFactory::Create(*patchRefiner, patchOptions);
_patchFaces = new Far::PtexIndices(baseMesh);
_patchMap = new Far::PatchMap(*_patchTable);
//
// Declare buffers/vectors for refined/patch points:
//
Far::TopologyLevel const & baseLevel = baseMesh.GetLevel(0);
int numBasePoints = baseLevel.GetNumVertices();
int numRefinedPoints = patchRefiner->GetNumVerticesTotal() - numBasePoints;
int numLocalPoints = _patchTable->GetNumLocalPoints();
_patchPos.resize(numBasePoints + numRefinedPoints + numLocalPoints);
std::memcpy(&_patchPos[0], &basePos[0], numBasePoints * sizeof(Vec3Real));
//
// Similarly declare buffers/vectors for refined/patch UVs:
//
int numBaseUVs = 0;
int numRefinedUVs = 0;
int numLocalUVs = 0;
if (hasUVs) {
numBaseUVs = baseLevel.GetNumFVarValues();
numRefinedUVs = patchRefiner->GetNumFVarValuesTotal() - numBaseUVs;
numLocalUVs = _patchTable->GetNumLocalPointsFaceVarying();
_patchUVs.resize(numBaseUVs + numRefinedUVs + numLocalUVs);
std::memcpy(&_patchUVs[0], &baseUVs[0], numBaseUVs * sizeof(Vec3Real));
}
//
// Compute refined and local patch points and UVs:
//
if (numRefinedPoints) {
Far::PrimvarRefinerReal<REAL> primvarRefiner(*patchRefiner);
Vec3Real const * srcP = &_patchPos[0];
Vec3Real * dstP = &_patchPos[numBasePoints];
Vec3Real const * srcUV = hasUVs ? &_patchUVs[0] : 0;
Vec3Real * dstUV = hasUVs ? &_patchUVs[numBaseUVs] : 0;
for (int level = 1; level < patchRefiner->GetNumLevels(); ++level) {
primvarRefiner.Interpolate(level, srcP, dstP);
srcP = dstP;
dstP += patchRefiner->GetLevel(level).GetNumVertices();
if (hasUVs) {
primvarRefiner.InterpolateFaceVarying(level, srcUV, dstUV);
srcUV = dstUV;
dstUV += patchRefiner->GetLevel(level).GetNumFVarValues();
}
}
}
if (numLocalPoints) {
_patchTable->GetLocalPointStencilTable<REAL>()->UpdateValues(
&_patchPos[0], &_patchPos[numBasePoints + numRefinedPoints]);
}
if (hasUVs && numLocalUVs) {
_patchTable->GetLocalPointFaceVaryingStencilTable<REAL>()->UpdateValues(
&_patchUVs[0], &_patchUVs[numBaseUVs + numRefinedUVs]);
}
delete patchRefiner;
}
template <typename REAL>
bool
FarPatchEvaluator<REAL>::FaceHasLimit(Far::Index baseFace) const {
return ! _baseMesh.GetLevel(0).IsFaceHole(baseFace);
}
template <typename REAL>
void
FarPatchEvaluator<REAL>::Evaluate(Far::Index baseFace,
TessCoordVector const & tessCoords,
EvalResults<REAL> & results) const {
assert(FaceHasLimit(baseFace));
int numCoords = (int) tessCoords.size() / 2;
// Allocate vectors for the properties to be evaluated:
results.Resize(numCoords);
//
// Identify the patch face and see if it needs to be re-parameterized:
//
int patchFace = _patchFaces->GetFaceId(baseFace);
int faceSize = _baseMesh.GetLevel(0).GetFaceVertices(baseFace).size();
Bfr::Parameterization faceParam(_baseMesh.GetSchemeType(), faceSize);
bool reparameterize = faceParam.HasSubFaces();
//
// Evaluate at each of the given coordinates:
//
REAL const * stPair = &tessCoords[0];
for (int i = 0; i < numCoords; ++i, stPair += 2) {
REAL st[2] = { stPair[0], stPair[1] };
int patchIndex = patchFace;
if (reparameterize) {
patchIndex += faceParam.ConvertCoordToNormalizedSubFace(st, st);
}
REAL s = st[0];
REAL t = st[1];
Far::PatchTable::PatchHandle const * patchHandle =
_patchMap->FindPatch(patchIndex, s, t);
assert(patchHandle);
// Evaluate position and derivatives:
if (results.evalPosition) {
REAL wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
if (!results.eval1stDeriv) {
_patchTable->EvaluateBasis(*patchHandle, s, t, wP);
} else if (!results.eval2ndDeriv) {
_patchTable->EvaluateBasis(*patchHandle, s, t, wP,
wDu, wDv);
} else {
_patchTable->EvaluateBasis(*patchHandle, s, t, wP,
wDu, wDv, wDuu, wDuv, wDvv);
}
Vec3Real * P = results.evalPosition ? &results.p[i] : 0;
Vec3Real * Du = results.eval1stDeriv ? &results.du[i] : 0;
Vec3Real * Dv = results.eval1stDeriv ? &results.dv[i] : 0;
Vec3Real * Duu = results.eval2ndDeriv ? &results.duu[i] : 0;
Vec3Real * Duv = results.eval2ndDeriv ? &results.duv[i] : 0;
Vec3Real * Dvv = results.eval2ndDeriv ? &results.dvv[i] : 0;
Far::ConstIndexArray cvIndices =
_patchTable->GetPatchVertices(*patchHandle);
P->Clear();
if (results.eval1stDeriv) {
Du->Clear();
Dv->Clear();
if (results.eval2ndDeriv) {
Duu->Clear();
Duv->Clear();
Dvv->Clear();
}
}
for (int cv = 0; cv < cvIndices.size(); ++cv) {
P->AddWithWeight(_patchPos[cvIndices[cv]], wP[cv]);
if (results.eval1stDeriv) {
Du->AddWithWeight(_patchPos[cvIndices[cv]], wDu[cv]);
Dv->AddWithWeight(_patchPos[cvIndices[cv]], wDv[cv]);
if (results.eval2ndDeriv) {
Duu->AddWithWeight(_patchPos[cvIndices[cv]], wDuu[cv]);
Duv->AddWithWeight(_patchPos[cvIndices[cv]], wDuv[cv]);
Dvv->AddWithWeight(_patchPos[cvIndices[cv]], wDvv[cv]);
}
}
}
}
if (results.evalUV) {
REAL wUV[20];
_patchTable->EvaluateBasisFaceVarying(*patchHandle, s, t, wUV);
Vec3Real & UV = results.uv[i];
UV.Clear();
Far::ConstIndexArray cvIndices =
_patchTable->GetPatchFVarValues(*patchHandle);
for (int cv = 0; cv < cvIndices.size(); ++cv) {
UV.AddWithWeight(_patchUVs[cvIndices[cv]], wUV[cv]);
}
}
}
}
template <typename REAL>
FarPatchEvaluator<REAL>::~FarPatchEvaluator() {
delete _patchTable;
delete _patchFaces;
delete _patchMap;
}
//
// Explicit instantiation for float and double:
//
template class FarPatchEvaluator<float>;
template class FarPatchEvaluator<double>;

View File

@ -0,0 +1,83 @@
//
// Copyright 2021 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.
//
#include "./types.h"
#include <opensubdiv/far/topologyRefiner.h>
#include <opensubdiv/far/patchTable.h>
#include <opensubdiv/far/patchTableFactory.h>
#include <opensubdiv/far/patchMap.h>
#include <opensubdiv/far/ptexIndices.h>
#include <opensubdiv/bfr/refinerSurfaceFactory.h>
#include <opensubdiv/bfr/tessellation.h>
using namespace OpenSubdiv;
using namespace OpenSubdiv::OPENSUBDIV_VERSION;
//
// FarPatchEvaluator bundles the Far::PatchTable and its ecosystem of
// related class to provide an evaluation interface targeted towards
// evaluation of the base faces of a mesh.
//
template <typename REAL>
class FarPatchEvaluator {
public:
typedef Far::PatchTableFactory PatchFactory;
typedef Bfr::SurfaceFactory::Options BfrSurfaceOptions;
typedef std::vector<REAL> TessCoordVector;
typedef Vec3<REAL> Vec3Real;
typedef std::vector<Vec3Real> Vec3RealVector;
public:
FarPatchEvaluator(Far::TopologyRefiner const & baseMesh,
Vec3RealVector const & basePos,
Vec3RealVector const & baseUVs,
BfrSurfaceOptions const & bfrSurfaceOptions);
~FarPatchEvaluator();
public:
bool FaceHasLimit(Far::Index baseFace) const;
void Evaluate(Far::Index baseface,
TessCoordVector const & tessCoords,
EvalResults<REAL> & results) const;
private:
Far::TopologyRefiner const & _baseMesh;
Vec3RealVector const & _baseMeshPos;
Vec3RealVector const & _baseMeshUVs;
Far::PatchTable * _patchTable;
Far::PatchMap * _patchMap;
Far::PtexIndices * _patchFaces;
Vec3RealVector _patchPos;
Vec3RealVector _patchUVs;
int _regFaceSize;
};

View File

@ -0,0 +1,99 @@
//
// Copyright 2021 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.
//
#include <regression/common/shape_utils.h>
#include <regression/shapes/all.h>
//------------------------------------------------------------------------------
static void
initShapes(std::vector<ShapeDesc> & shapes) {
shapes.push_back(ShapeDesc("catmark_toroidal_tet", catmark_toroidal_tet, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube", catmark_cube, kCatmark));
shapes.push_back(ShapeDesc("catmark_cubes_semisharp", catmark_cubes_semisharp, kCatmark));
shapes.push_back(ShapeDesc("catmark_cubes_infsharp", catmark_cubes_infsharp, kCatmark));
shapes.push_back(ShapeDesc("catmark_pyramid", catmark_pyramid, kCatmark));
shapes.push_back(ShapeDesc("catmark_pyramid_creases0", catmark_pyramid_creases0, kCatmark));
shapes.push_back(ShapeDesc("catmark_pyramid_creases1", catmark_pyramid_creases1, kCatmark));
shapes.push_back(ShapeDesc("catmark_torus", catmark_torus, kCatmark));
shapes.push_back(ShapeDesc("catmark_torus_creases0", catmark_torus_creases0, kCatmark));
shapes.push_back(ShapeDesc("catmark_torus_creases1", catmark_torus_creases1, kCatmark));
shapes.push_back(ShapeDesc("catmark_edgecorner", catmark_edgecorner, kCatmark));
shapes.push_back(ShapeDesc("catmark_edgeonly", catmark_edgeonly, kCatmark));
shapes.push_back(ShapeDesc("catmark_edgenone", catmark_edgenone, kCatmark));
shapes.push_back(ShapeDesc("catmark_quadstrips", catmark_quadstrips, kCatmark));
shapes.push_back(ShapeDesc("catmark_xord_interior", catmark_xord_interior, kCatmark));
shapes.push_back(ShapeDesc("catmark_xord_boundary", catmark_xord_boundary, kCatmark));
shapes.push_back(ShapeDesc("catmark_val2_interior", catmark_val2_interior, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonquads", catmark_nonquads, kCatmark));
shapes.push_back(ShapeDesc("catmark_single_crease", catmark_single_crease, kCatmark));
shapes.push_back(ShapeDesc("catmark_inf_crease0", catmark_inf_crease0, kCatmark));
shapes.push_back(ShapeDesc("catmark_inf_crease1", catmark_inf_crease1, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_verts", catmark_nonman_verts, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_edges", catmark_nonman_edges, kCatmark));
shapes.push_back(ShapeDesc("catmark_hole_test1", catmark_hole_test1, kCatmark));
shapes.push_back(ShapeDesc("catmark_hole_test2", catmark_hole_test2, kCatmark));
shapes.push_back(ShapeDesc("catmark_hole_test3", catmark_hole_test3, kCatmark));
shapes.push_back(ShapeDesc("catmark_hole_test4", catmark_hole_test4, kCatmark));
shapes.push_back(ShapeDesc("catmark_chaikin0", catmark_chaikin0, kCatmark));
shapes.push_back(ShapeDesc("catmark_chaikin1", catmark_chaikin1, kCatmark));
shapes.push_back(ShapeDesc("catmark_chaikin2", catmark_chaikin2, kCatmark));
shapes.push_back(ShapeDesc("catmark_smoothtris0", catmark_smoothtris0, kCatmark));
shapes.push_back(ShapeDesc("catmark_smoothtris1", catmark_smoothtris1, kCatmark));
shapes.push_back(ShapeDesc("catmark_pole8", catmark_pole8, kCatmark));
// shapes.push_back(ShapeDesc("catmark_pole64", catmark_pole64, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_quadpole64",catmark_nonman_quadpole64,kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_edge100", catmark_nonman_edge100, kCatmark));
shapes.push_back(ShapeDesc("catmark_helmet", catmark_helmet, kCatmark));
shapes.push_back(ShapeDesc("catmark_bishop", catmark_bishop, kCatmark));
shapes.push_back(ShapeDesc("catmark_pawn", catmark_pawn, kCatmark));
// shapes.push_back(ShapeDesc("catmark_rook", catmark_rook, kCatmark));
shapes.push_back(ShapeDesc("catmark_car", catmark_car, kCatmark));
shapes.push_back(ShapeDesc("loop_toroidal_tet", loop_toroidal_tet, kLoop));
shapes.push_back(ShapeDesc("loop_tetrahedron", loop_tetrahedron, kLoop));
shapes.push_back(ShapeDesc("loop_cube", loop_cube, kLoop));
shapes.push_back(ShapeDesc("loop_cubes_semisharp", loop_cubes_semisharp, kLoop));
shapes.push_back(ShapeDesc("loop_cubes_infsharp", loop_cubes_infsharp, kLoop));
shapes.push_back(ShapeDesc("loop_cube_asymmetric", loop_cube_asymmetric, kLoop));
shapes.push_back(ShapeDesc("loop_icosahedron", loop_icosahedron, kLoop));
shapes.push_back(ShapeDesc("loop_icos_semisharp", loop_icos_semisharp, kLoop));
shapes.push_back(ShapeDesc("loop_icos_infsharp", loop_icos_infsharp, kLoop));
shapes.push_back(ShapeDesc("loop_triangle_edgecorner", loop_triangle_edgecorner, kLoop));
shapes.push_back(ShapeDesc("loop_triangle_edgeonly", loop_triangle_edgeonly, kLoop));
shapes.push_back(ShapeDesc("loop_triangle_edgenone", loop_triangle_edgenone, kLoop));
shapes.push_back(ShapeDesc("loop_xord_interior", loop_xord_interior, kLoop));
shapes.push_back(ShapeDesc("loop_xord_boundary", loop_xord_boundary, kLoop));
shapes.push_back(ShapeDesc("loop_nonman_verts", loop_nonman_verts, kLoop));
shapes.push_back(ShapeDesc("loop_nonman_edges", loop_nonman_edges, kLoop));
// shapes.push_back(ShapeDesc("loop_pole64", loop_pole64, kLoop));
shapes.push_back(ShapeDesc("loop_nonman_edge100", loop_nonman_edge100, kLoop));
shapes.push_back(ShapeDesc("bilinear_cube", bilinear_cube, kBilinear));
shapes.push_back(ShapeDesc("bilinear_nonplanar", bilinear_nonplanar, kBilinear));
shapes.push_back(ShapeDesc("bilinear_nonquads0", bilinear_nonquads0, kBilinear));
shapes.push_back(ShapeDesc("bilinear_nonquads1", bilinear_nonquads1, kBilinear));
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,149 @@
//
// Copyright 2021 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.
//
#include <regression/common/shape_utils.h>
#include <regression/shapes/all.h>
static void
initShapesAll(std::vector<ShapeDesc> & shapes) {
shapes.push_back(ShapeDesc("catmark_cube", catmark_cube, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube_corner0", catmark_cube_corner0, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube_corner1", catmark_cube_corner1, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube_corner2", catmark_cube_corner2, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube_corner3", catmark_cube_corner3, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube_corner4", catmark_cube_corner4, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube_creases0", catmark_cube_creases0, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube_creases1", catmark_cube_creases1, kCatmark));
shapes.push_back(ShapeDesc("catmark_cube_creases2", catmark_cube_creases2, kCatmark));
shapes.push_back(ShapeDesc("catmark_cubes_infsharp", catmark_cubes_infsharp, kCatmark));
shapes.push_back(ShapeDesc("catmark_cubes_semisharp", catmark_cubes_semisharp, kCatmark));
shapes.push_back(ShapeDesc("catmark_chaikin0", catmark_chaikin0, kCatmark));
shapes.push_back(ShapeDesc("catmark_chaikin1", catmark_chaikin1, kCatmark));
shapes.push_back(ShapeDesc("catmark_chaikin2", catmark_chaikin2, kCatmark));
shapes.push_back(ShapeDesc("catmark_dart_edgecorner", catmark_dart_edgecorner, kCatmark));
shapes.push_back(ShapeDesc("catmark_dart_edgeonly", catmark_dart_edgeonly, kCatmark));
shapes.push_back(ShapeDesc("catmark_edgecorner", catmark_edgecorner, kCatmark));
shapes.push_back(ShapeDesc("catmark_edgeonly", catmark_edgeonly, kCatmark));
shapes.push_back(ShapeDesc("catmark_edgenone", catmark_edgenone, kCatmark));
shapes.push_back(ShapeDesc("catmark_fan", catmark_fan, kCatmark));
shapes.push_back(ShapeDesc("catmark_flap", catmark_flap, kCatmark));
shapes.push_back(ShapeDesc("catmark_flap2", catmark_flap2, kCatmark));
shapes.push_back(ShapeDesc("catmark_fvar_bound0", catmark_fvar_bound0, kCatmark));
shapes.push_back(ShapeDesc("catmark_fvar_bound1", catmark_fvar_bound1, kCatmark));
shapes.push_back(ShapeDesc("catmark_fvar_bound2", catmark_fvar_bound2, kCatmark));
shapes.push_back(ShapeDesc("catmark_fvar_bound3", catmark_fvar_bound3, kCatmark));
shapes.push_back(ShapeDesc("catmark_fvar_bound4", catmark_fvar_bound4, kCatmark));
shapes.push_back(ShapeDesc("catmark_fvar_project0", catmark_fvar_project0, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test0", catmark_gregory_test0, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test1", catmark_gregory_test1, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test2", catmark_gregory_test2, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test3", catmark_gregory_test3, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test4", catmark_gregory_test4, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test5", catmark_gregory_test5, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test6", catmark_gregory_test6, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test7", catmark_gregory_test7, kCatmark));
shapes.push_back(ShapeDesc("catmark_gregory_test8", catmark_gregory_test8, kCatmark));
shapes.push_back(ShapeDesc("catmark_helmet", catmark_helmet, kCatmark));
shapes.push_back(ShapeDesc("catmark_hole_test1", catmark_hole_test1, kCatmark));
shapes.push_back(ShapeDesc("catmark_hole_test2", catmark_hole_test2, kCatmark));
shapes.push_back(ShapeDesc("catmark_hole_test3", catmark_hole_test3, kCatmark));
shapes.push_back(ShapeDesc("catmark_hole_test4", catmark_hole_test4, kCatmark));
shapes.push_back(ShapeDesc("catmark_lefthanded", catmark_lefthanded, kCatmark));
shapes.push_back(ShapeDesc("catmark_righthanded", catmark_righthanded, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_edges", catmark_nonman_edges, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_edge100", catmark_nonman_edge100, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_verts", catmark_nonman_verts, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_quadpole8", catmark_nonman_quadpole8, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_quadpole64", catmark_nonman_quadpole64, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_quadpole360", catmark_nonman_quadpole360, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonman_bareverts", catmark_nonman_bareverts, kCatmark));
shapes.push_back(ShapeDesc("catmark_nonquads", catmark_nonquads, kCatmark));
shapes.push_back(ShapeDesc("catmark_pawn", catmark_pawn, kCatmark));
shapes.push_back(ShapeDesc("catmark_pole8", catmark_pole8, kCatmark));
shapes.push_back(ShapeDesc("catmark_pole64", catmark_pole64, kCatmark));
shapes.push_back(ShapeDesc("catmark_pyramid_creases0", catmark_pyramid_creases0, kCatmark));
shapes.push_back(ShapeDesc("catmark_pyramid_creases1", catmark_pyramid_creases1, kCatmark));
shapes.push_back(ShapeDesc("catmark_pyramid_creases2", catmark_pyramid_creases2, kCatmark));
shapes.push_back(ShapeDesc("catmark_pyramid", catmark_pyramid, kCatmark));
shapes.push_back(ShapeDesc("catmark_quadstrips", catmark_quadstrips, kCatmark));
shapes.push_back(ShapeDesc("catmark_single_crease", catmark_single_crease, kCatmark));
shapes.push_back(ShapeDesc("catmark_inf_crease0", catmark_inf_crease0, kCatmark));
shapes.push_back(ShapeDesc("catmark_inf_crease1", catmark_inf_crease1, kCatmark));
shapes.push_back(ShapeDesc("catmark_smoothtris0", catmark_smoothtris0, kCatmark));
shapes.push_back(ShapeDesc("catmark_smoothtris1", catmark_smoothtris1, kCatmark));
shapes.push_back(ShapeDesc("catmark_tent_creases0", catmark_tent_creases0, kCatmark));
shapes.push_back(ShapeDesc("catmark_tent_creases1", catmark_tent_creases1, kCatmark));
shapes.push_back(ShapeDesc("catmark_tent", catmark_tent, kCatmark));
shapes.push_back(ShapeDesc("catmark_toroidal_tet", catmark_toroidal_tet, kCatmark));
shapes.push_back(ShapeDesc("catmark_torus", catmark_torus, kCatmark));
shapes.push_back(ShapeDesc("catmark_torus_creases0", catmark_torus_creases0, kCatmark));
shapes.push_back(ShapeDesc("catmark_torus_creases1", catmark_torus_creases1, kCatmark));
shapes.push_back(ShapeDesc("catmark_val2_interior", catmark_val2_interior, kCatmark));
shapes.push_back(ShapeDesc("catmark_val2_back2back", catmark_val2_back2back, kCatmark));
shapes.push_back(ShapeDesc("catmark_val2_foldover", catmark_val2_foldover, kCatmark));
shapes.push_back(ShapeDesc("catmark_val2_nonman", catmark_val2_nonman, kCatmark));
shapes.push_back(ShapeDesc("catmark_xord_interior", catmark_xord_interior, kCatmark));
shapes.push_back(ShapeDesc("catmark_xord_boundary", catmark_xord_boundary, kCatmark));
shapes.push_back(ShapeDesc("bilinear_cube", bilinear_cube, kBilinear));
shapes.push_back(ShapeDesc("bilinear_nonplanar", bilinear_nonplanar, kBilinear));
shapes.push_back(ShapeDesc("bilinear_nonquads0", bilinear_nonquads0, kBilinear));
shapes.push_back(ShapeDesc("bilinear_nonquads1", bilinear_nonquads1, kBilinear));
shapes.push_back(ShapeDesc("loop_chaikin0", loop_chaikin0, kLoop));
shapes.push_back(ShapeDesc("loop_chaikin1", loop_chaikin1, kLoop));
shapes.push_back(ShapeDesc("loop_cube", loop_cube, kLoop));
shapes.push_back(ShapeDesc("loop_cube_asymmetric", loop_cube_asymmetric, kLoop));
shapes.push_back(ShapeDesc("loop_cube_creases0", loop_cube_creases0, kLoop));
shapes.push_back(ShapeDesc("loop_cube_creases1", loop_cube_creases1, kLoop));
shapes.push_back(ShapeDesc("loop_cubes_infsharp", loop_cubes_infsharp, kLoop));
shapes.push_back(ShapeDesc("loop_cubes_semisharp", loop_cubes_semisharp, kLoop));
shapes.push_back(ShapeDesc("loop_fvar_bound0", loop_fvar_bound0, kLoop));
shapes.push_back(ShapeDesc("loop_fvar_bound1", loop_fvar_bound1, kLoop));
shapes.push_back(ShapeDesc("loop_fvar_bound2", loop_fvar_bound2, kLoop));
shapes.push_back(ShapeDesc("loop_fvar_bound3", loop_fvar_bound3, kLoop));
shapes.push_back(ShapeDesc("loop_icosahedron", loop_icosahedron, kLoop));
shapes.push_back(ShapeDesc("loop_icos_infsharp", loop_icos_infsharp, kLoop));
shapes.push_back(ShapeDesc("loop_icos_semisharp", loop_icos_semisharp, kLoop));
shapes.push_back(ShapeDesc("loop_nonman_edges", loop_nonman_edges, kLoop));
shapes.push_back(ShapeDesc("loop_nonman_edge100", loop_nonman_edge100, kLoop));
shapes.push_back(ShapeDesc("loop_nonman_verts", loop_nonman_verts, kLoop));
shapes.push_back(ShapeDesc("loop_pole8", loop_pole8, kLoop));
shapes.push_back(ShapeDesc("loop_pole64", loop_pole64, kLoop));
shapes.push_back(ShapeDesc("loop_saddle_edgecorner", loop_saddle_edgecorner, kLoop));
shapes.push_back(ShapeDesc("loop_saddle_edgeonly", loop_saddle_edgeonly, kLoop));
shapes.push_back(ShapeDesc("loop_tetrahedron", loop_tetrahedron, kLoop));
shapes.push_back(ShapeDesc("loop_toroidal_tet", loop_toroidal_tet, kLoop));
shapes.push_back(ShapeDesc("loop_triangle_edgecorner", loop_triangle_edgecorner, kLoop));
shapes.push_back(ShapeDesc("loop_triangle_edgeonly", loop_triangle_edgeonly, kLoop));
shapes.push_back(ShapeDesc("loop_xord_boundary", loop_xord_boundary, kLoop));
shapes.push_back(ShapeDesc("loop_xord_interior", loop_xord_interior, kLoop));
shapes.push_back(ShapeDesc("loop_val2_interior", loop_val2_interior, kLoop));
// More complicated shapes with longer execution times:
shapes.push_back(ShapeDesc("catmark_car", catmark_car, kCatmark));
shapes.push_back(ShapeDesc("catmark_rook", catmark_rook, kCatmark));
shapes.push_back(ShapeDesc("catmark_bishop", catmark_bishop, kCatmark));
// shapes.push_back(ShapeDesc("catmark_pole360", catmark_pole360, kCatmark));
// shapes.push_back(ShapeDesc("loop_pole360", loop_pole360, kLoop));
}

View File

@ -0,0 +1,954 @@
//
// Copyright 2021 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.
//
#include "./types.h"
#include "./bfrSurfaceEvaluator.h"
#include "./farPatchEvaluator.h"
#include "../../regression/common/far_utils.h"
#include "init_shapes.h"
#include "init_shapes_all.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <cstdio>
using namespace OpenSubdiv;
using namespace OpenSubdiv::OPENSUBDIV_VERSION;
//
// Global set of shapes -- populated by variants of initShapes() that
// include explicit lists:
//
std::vector<ShapeDesc> g_shapes;
//
// Command line arguments and their parsing:
//
class Args {
public:
// options related to testing and reporting:
unsigned int posEvaluate : 1;
unsigned int d1Evaluate : 1;
unsigned int d2Evaluate : 1;
unsigned int uvEvaluate : 1;
unsigned int posIgnore : 1;
unsigned int d1Ignore : 1;
unsigned int d2Ignore : 1;
unsigned int uvIgnore : 1;
unsigned int printArgs : 1;
unsigned int printProgress : 1;
unsigned int printFaceDiffs : 1;
unsigned int printSummary : 1;
unsigned int printWarnings : 1;
unsigned int ptexConvert : 1;
// options affecting configuration and execution:
unsigned int evalByStencils : 1;
unsigned int doublePrecision : 1;
unsigned int noCacheFlag : 1;
// options affecting the shape of the limit surface:
int depthSharp;
int depthSmooth;
int bndInterp;
int uvInterp;
// options related to tessellation and comparison:
int uniformRes;
float relTolerance;
float absTolerance;
float uvTolerance;
// options affecting the list of shapes to be tested:
int shapeCount;
Scheme shapeScheme;
bool shapesCat2Loop;
bool shapesAll;
std::vector<ShapeDesc> shapes;
// options determining overall success/failure:
int passCount;
public:
Args(int argc, char **argv) :
posEvaluate(true),
d1Evaluate(false),
d2Evaluate(false),
uvEvaluate(false),
posIgnore(false),
d1Ignore(false),
d2Ignore(false),
uvIgnore(false),
printArgs(true),
printProgress(true),
printFaceDiffs(false),
printSummary(true),
printWarnings(true),
ptexConvert(false),
evalByStencils(false),
doublePrecision(false),
noCacheFlag(false),
depthSharp(-1),
depthSmooth(-1),
bndInterp(-1),
uvInterp(-1),
uniformRes(3),
relTolerance(0.00005f),
absTolerance(0.0f),
uvTolerance(0.0001f),
shapeCount(0),
shapeScheme(kCatmark),
shapesCat2Loop(false),
shapesAll(false),
shapes(),
passCount(0) {
std::string fileString;
std::vector<std::string> shapeNames;
for (int i = 1; i < argc; ++i) {
char * arg = argv[i];
// Options related to input .obj files:
if (strstr(arg, ".obj")) {
if (readString(arg, fileString)) {
// Use the scheme declared at the time so that multiple
// shape/scheme pairs can be specified
shapes.push_back(
ShapeDesc(arg, fileString.c_str(), shapeScheme));
} else {
fprintf(stderr,
"Error: Unable to open/read .obj file '%s'\n", arg);
exit(0);
}
// Options affecting the limit surface shapes:
} else if (!strcmp(arg, "-l")) {
if (++i < argc) {
int maxLevel = atoi(argv[i]);
depthSharp = maxLevel;
depthSmooth = maxLevel;
}
} else if (!strcmp(arg, "-lsharp")) {
if (++i < argc) depthSharp = atoi(argv[i]);
} else if (!strcmp(arg, "-lsmooth")) {
if (++i < argc) depthSmooth = atoi(argv[i]);
} else if (!strcmp(argv[i], "-bint")) {
if (++i < argc) bndInterp = atoi(argv[i]);
} else if (!strcmp(argv[i], "-uvint")) {
if (++i < argc) uvInterp = atoi(argv[i]);
// Options affecting what gets evaluated:
} else if (!strcmp(arg, "-res")) {
if (++i < argc) uniformRes = atoi(argv[i]);
} else if (!strcmp(arg, "-pos")) {
posEvaluate = true;
} else if (!strcmp(arg, "-nopos")) {
posEvaluate = false;
} else if (!strcmp(arg, "-d1")) {
d1Evaluate = true;
} else if (!strcmp(arg, "-nod1")) {
d1Evaluate = false;
} else if (!strcmp(arg, "-d2")) {
d2Evaluate = true;
} else if (!strcmp(arg, "-nod2")) {
d2Evaluate = false;
} else if (!strcmp(arg, "-uv")) {
uvEvaluate = true;
} else if (!strcmp(arg, "-nouv")) {
uvEvaluate = false;
} else if (!strcmp(arg, "-ptex")) {
ptexConvert = true;
} else if (!strcmp(arg, "-noptex")) {
ptexConvert = false;
// Options affecting what gets compared and reported:
} else if (!strcmp(arg, "-skippos")) {
posIgnore = true;
} else if (!strcmp(arg, "-skipd1")) {
d1Ignore = true;
} else if (!strcmp(arg, "-skipd2")) {
d2Ignore = true;
} else if (!strcmp(arg, "-skipuv")) {
uvIgnore = true;
} else if (!strcmp(arg, "-faces")) {
printFaceDiffs = true;
// Options affecting comparison tolerances:
} else if (!strcmp(argv[i], "-reltol")) {
if (++i < argc) relTolerance = (float)atof(argv[i]);
} else if (!strcmp(argv[i], "-abstol")) {
if (++i < argc) absTolerance = (float)atof(argv[i]);
} else if (!strcmp(argv[i], "-uvtol")) {
if (++i < argc) uvTolerance = (float)atof(argv[i]);
// Options controlling other internal processing:
} else if (!strcmp(arg, "-stencils")) {
evalByStencils = true;
} else if (!strcmp(arg, "-double")) {
doublePrecision = true;
} else if (!strcmp(arg, "-nocache")) {
noCacheFlag = true;
// Options affecting the shapes to be included:
} else if (!strcmp(arg, "-bilinear")) {
shapeScheme = kBilinear;
} else if (!strcmp(arg, "-catmark")) {
shapeScheme = kCatmark;
} else if (!strcmp(arg, "-loop")) {
shapeScheme = kLoop;
} else if (!strcmp(arg, "-cat2loop")) {
shapesCat2Loop = true;
} else if (!strcmp(arg, "-count")) {
if (++i < argc) shapeCount = atoi(argv[i]);
} else if (!strcmp(arg, "-shape")) {
if (++i < argc) {
shapeNames.push_back(std::string(argv[i]));
}
} else if (!strcmp(arg, "-all")) {
shapesAll = true;
// Printing and reporting:
} else if (!strcmp(arg, "-args")) {
printArgs = true;
} else if (!strcmp(arg, "-noargs")) {
printArgs = false;
} else if (!strcmp(arg, "-prog")) {
printProgress = true;
} else if (!strcmp(arg, "-noprog")) {
printProgress = false;
} else if (!strcmp(arg, "-sum")) {
printSummary = true;
} else if (!strcmp(arg, "-nosum")) {
printSummary = false;
} else if (!strcmp(arg, "-quiet")) {
printWarnings = false;
} else if (!strcmp(arg, "-silent")) {
printArgs = false;
printProgress = false;
printSummary = false;
printWarnings = false;
// Success/failure of the entire test:
} else if (!strcmp(argv[i], "-pass")) {
if (++i < argc) passCount = atoi(argv[i]);
// Unrecognized...
} else {
fprintf(stderr, "Error: Unrecognized argument '%s'\n", arg);
exit(0);
}
}
// Validation -- possible conflicting options, values, etc.
if (bndInterp > 2) {
fprintf(stderr, "Warning: Ignoring bad value to -bint (%d)\n",
bndInterp);
bndInterp = -1;
}
if (uvInterp > 5) {
fprintf(stderr, "Warning: Ignoring bad value to -uvint (%d)\n",
uvInterp);
uvInterp = -1;
}
if (d2Evaluate) {
if (!d1Evaluate) {
fprintf(stderr, "Warning: 2nd deriv evaluation forces 1st.\n");
d1Evaluate = true;
}
if (!posEvaluate) {
fprintf(stderr, "Warning: 2nd deriv evaluation forces pos.\n");
posEvaluate = true;
}
} else if (d1Evaluate) {
if (!posEvaluate) {
fprintf(stderr, "Warning: 1st deriv evaluation forces pos.\n");
posEvaluate = true;
}
}
if (!posEvaluate && !uvEvaluate) {
fprintf(stderr, "Error: All pos and UV evaluation disabled.\n");
exit(0);
}
if (posIgnore && d1Ignore && d2Ignore && uvIgnore) {
fprintf(stderr, "Error: All pos and UV comparisons disabled.\n");
exit(0);
}
if ((depthSmooth == 0) || (depthSharp == 0)) {
fprintf(stderr,
"Warning: Far evaluation unstable with refinement level 0.\n");
}
// Managing the list of shapes:
assert(g_shapes.empty());
if (!shapeNames.empty()) {
if (shapesAll) {
initShapesAll(g_shapes);
} else {
initShapes(g_shapes);
}
// Maybe worth building a map -- for this and more...
for (size_t i = 0; i < shapeNames.size(); ++i) {
std::string & shapeName = shapeNames[i];
bool found = false;
for (size_t j = 0; !found && (j < g_shapes.size()); ++j) {
if (g_shapes[j].name == shapeName) {
shapes.push_back(g_shapes[j]);
found = true;
break;
}
}
if (!found) {
fprintf(stderr,
"Error: Specified shape '%s' not found.\n",
shapeName.c_str());
exit(0);
}
}
}
}
~Args() { }
void
Print() const {
char const * boolStrings[2] = { "false", "true" };
char const * bIntStrings[3] = { "BOUNDARY_NONE",
"BOUNDARY_EDGE_ONLY",
"BOUNDARY_EDGE_AND_CORNER" };
char const * fvIntStrings[6] = { "LINEAR_NONE",
"LINEAR_CORNERS_ONLY",
"LINEAR_CORNERS_PLUS1",
"LINEAR_CORNERS_PLUS2",
"LINEAR_BOUNDARIES",
"LINEAR_ALL" };
printf("\n");
printf("Shape options:\n");
if (depthSharp >= 0) {
printf(" - max level sharp = %d\n", depthSharp);
} else {
printf(" - max level sharp = %d (dflt)\n",
(Bfr::SurfaceFactory::Options()).GetApproxLevelSharp());
}
if (depthSmooth >= 0) {
printf(" - max level smooth = %d\n", depthSmooth);
} else {
printf(" - max level smooth = %d (dflt)\n",
(Bfr::SurfaceFactory::Options()).GetApproxLevelSmooth());
}
if (bndInterp < 0) {
printf(" - boundary interp = (as assigned)\n");
} else {
printf(" - boundary interp = %s\n", bIntStrings[bndInterp]);
}
if (uvEvaluate) {
if (uvInterp < 0) {
printf(" - UV linear interp = (as assigned)\n");
} else {
printf(" - UV linear interp = %s\n", fvIntStrings[uvInterp]);
}
}
printf("Evaluation options:\n");
printf(" - tessellation res = %d\n", uniformRes);
printf(" - position = %s\n", boolStrings[posEvaluate]);
printf(" - 1st derivative = %s\n", boolStrings[d1Evaluate]);
printf(" - 2nd derivative = %s\n", boolStrings[d2Evaluate]);
printf(" - UV = %s\n", boolStrings[uvEvaluate]);
printf("Comparison options:\n");
if (absTolerance > 0.0f) {
printf(" - tolerance (abs) = %g\n", absTolerance);
} else {
printf(" - tolerance (rel) = %g\n", relTolerance);
}
if (uvEvaluate) {
printf(" - tolerance UV = %g\n", uvTolerance);
}
if (posEvaluate && posIgnore) {
printf(" - ignore pos = %s\n", boolStrings[posIgnore]);
}
if (d1Evaluate && d1Ignore) {
printf(" - ignore 1st deriv = %s\n", boolStrings[d1Ignore]);
}
if (d2Evaluate && d2Ignore) {
printf(" - ignore 2nd deriv = %s\n", boolStrings[d2Ignore]);
}
if (uvEvaluate && uvIgnore) {
printf(" - ignore UV = %s\n", boolStrings[uvIgnore]);
}
printf("\n");
}
private:
Args() { }
bool
readString(const char *fileName, std::string& fileString) {
std::ifstream ifs(fileName);
if (ifs) {
std::stringstream ss;
ss << ifs.rdbuf();
ifs.close();
fileString = ss.str();
return true;
}
return false;
}
};
//
// Create a TopologyRefiner from a Shape:
//
template <typename REAL>
Far::TopologyRefiner *
createTopologyRefiner(ShapeDesc const & shapeDesc,
std::vector< Vec3<REAL> > & shapePos,
std::vector< Vec3<REAL> > & shapeUVs,
Args const & args) {
typedef Vec3<REAL> Vec3Real;
//
// Load the Shape -- skip with a warning on failure:
//
Shape * shape = Shape::parseObj(shapeDesc.data.c_str(),
shapeDesc.scheme,
shapeDesc.isLeftHanded);
if (shape == 0) {
if (args.printWarnings) {
fprintf(stderr, "Warning: Failed to parse shape '%s'\n",
shapeDesc.name.c_str());
}
return 0;
}
// Verify UVs before continuing:
if (args.uvEvaluate) {
if (shape->uvs.empty() != shape->faceuvs.empty()) {
if (args.printWarnings) {
fprintf(stderr,
"Warning: Incomplete UVs assigned to Shape '%s'\n",
shapeDesc.name.c_str());
}
delete shape;
return 0;
}
}
//
// Create a TopologyRefiner and load position and UVs:
//
Sdc::SchemeType sdcType = GetSdcType(*shape);
if (args.shapesCat2Loop && (sdcType == Sdc::SCHEME_LOOP)) {
if (args.printWarnings) {
fprintf(stderr,
"\t\tWarning: Applying Catmark to Loop shape '%s'\n",
shapeDesc.name.c_str());
}
sdcType = Sdc::SCHEME_CATMARK;
}
Sdc::Options sdcOptions = GetSdcOptions(*shape);
if (args.bndInterp >= 0) {
sdcOptions.SetVtxBoundaryInterpolation(
(Sdc::Options::VtxBoundaryInterpolation) args.bndInterp);
}
if (args.uvInterp >= 0) {
sdcOptions.SetFVarLinearInterpolation(
(Sdc::Options::FVarLinearInterpolation) args.uvInterp);
}
Far::TopologyRefiner * refiner =
Far::TopologyRefinerFactory<Shape>::Create(*shape,
Far::TopologyRefinerFactory<Shape>::Options(sdcType, sdcOptions));
if (refiner == 0) {
if (args.printWarnings) {
fprintf(stderr, "Warning: Unable to interpret Shape '%s'\n",
shapeDesc.name.c_str());
}
delete shape;
return 0;
}
int numVertices = refiner->GetNumVerticesTotal();
shapePos.resize(numVertices);
for (int i = 0; i < numVertices; ++i) {
shapePos[i] = Vec3Real(shape->verts[i*3],
shape->verts[i*3+1],
shape->verts[i*3+2]);
}
shapeUVs.resize(0);
if (args.uvEvaluate) {
if (refiner->GetNumFVarChannels()) {
int numUVs = refiner->GetNumFVarValuesTotal(0);
shapeUVs.resize(numUVs);
for (int i = 0; i < numUVs; ++i) {
shapeUVs[i] = Vec3Real(shape->uvs[i*2],
shape->uvs[i*2+1],
0.0f);
}
}
}
delete shape;
return refiner;
}
//
// Compute the bounding box of a Vec3 vector and a relative tolerance:
//
template <typename REAL>
REAL
GetRelativeTolerance(std::vector< Vec3<REAL> > const & p, REAL fraction) {
Vec3<REAL> pMin = p[0];
Vec3<REAL> pMax = p[0];
for (size_t i = 1; i < p.size(); ++i) {
Vec3<REAL> const & pi = p[i];
pMin[0] = std::min(pMin[0], pi[0]);
pMin[1] = std::min(pMin[1], pi[1]);
pMin[2] = std::min(pMin[2], pi[2]);
pMax[0] = std::max(pMax[0], pi[0]);
pMax[1] = std::max(pMax[1], pi[1]);
pMax[2] = std::max(pMax[2], pi[2]);
}
Vec3<REAL> pDelta = pMax - pMin;
REAL maxSize = std::max(std::abs(pDelta[0]), std::abs(pDelta[1]));
maxSize = std::max(maxSize, std::abs(pDelta[2]));
return fraction * maxSize;
}
//
// An independent test from limit surface evaluation: comparing the
// conversion of (u,v) coordinates for Bfr::Parameterization to Ptex
// and back (subject to a given tolerance):
//
template <typename REAL>
void
ValidatePtexConversion(Bfr::Parameterization const & param,
REAL const givenCoord[2], REAL tol = 0.0001f) {
if (!param.HasSubFaces()) return;
//
// Convert the given (u,v) coordinate to Ptex and back and
// compare the final result to the original:
//
REAL ptexCoord[2];
REAL finalCoord[2];
int ptexFace = param.ConvertCoordToNormalizedSubFace(givenCoord, ptexCoord);
param.ConvertNormalizedSubFaceToCoord(ptexFace, ptexCoord, finalCoord);
bool subFaceDiff = (ptexFace != param.GetSubFace(givenCoord));
bool uCoordDiff = (std::abs(finalCoord[0] - givenCoord[0]) > tol);
bool vCoordDiff = (std::abs(finalCoord[1] - givenCoord[1]) > tol);
if (subFaceDiff || uCoordDiff || vCoordDiff) {
fprintf(stderr,
"Warning: Mismatch in sub-face Parameterization conversion:\n");
if (subFaceDiff ) {
fprintf(stderr,
" converted sub-face (%d) != original (%d)\n",
ptexFace, param.GetSubFace(givenCoord));
}
if (uCoordDiff || vCoordDiff) {
fprintf(stderr,
" converted coord (%f,%f) != original (%f,%f)\n",
finalCoord[0], finalCoord[1], givenCoord[0], givenCoord[1]);
}
}
}
//
// Compare two meshes using Bfr::Surfaces and a Far::PatchTable:
//
template <typename REAL>
int
testMesh(Far::TopologyRefiner const & mesh,
std::string const & meshName,
std::vector< Vec3<REAL> > const & meshPos,
std::vector< Vec3<REAL> > const & meshUVs,
Args const & args) {
//
// Determine what to evaluate/compare based on args and mesh content
// (remember that these are not completely independent -- position
// evaluation will have been set if evaluating any derivatives):
//
bool evalPos = args.posEvaluate;
bool evalD1 = args.d1Evaluate;
bool evalD2 = args.d2Evaluate;
bool evalUV = args.uvEvaluate && (meshUVs.size() > 0);
bool comparePos = evalPos && !args.posIgnore;
bool compareD1 = evalD1 && !args.d1Ignore;
bool compareD2 = evalD2 && !args.d2Ignore;
bool compareUV = evalUV && !args.uvIgnore;
// If nothing to compare, return 0 failures:
if ((comparePos + compareD1 + compareD2 + compareUV) == 0) {
return 0;
}
// Declare/allocate output evaluation buffers for both Bfr and Far:
std::vector<REAL> evalCoords;
EvalResults<REAL> bfrResults;
bfrResults.evalPosition = evalPos;
bfrResults.eval1stDeriv = evalD1;
bfrResults.eval2ndDeriv = evalD2;
bfrResults.evalUV = evalUV;
bfrResults.useStencils = args.evalByStencils;
EvalResults<REAL> farResults;
farResults.evalPosition = evalPos;
farResults.eval1stDeriv = evalD1;
farResults.eval2ndDeriv = evalD2;
farResults.evalUV = evalUV;
//
// Create evaluators for Bfr and Far -- using the same set of Bfr
// options to ensure consistency (the Far evaluator needs to interpret
// them appropriate to Far::PatchTable and associated refinement)
//
Bfr::SurfaceFactory::Options surfaceOptions;
// Leave approximation defaults in place unless explicitly overridden:
if (args.depthSharp >= 0) {
surfaceOptions.SetApproxLevelSharp(args.depthSharp);
}
if (args.depthSmooth >= 0) {
surfaceOptions.SetApproxLevelSmooth(args.depthSmooth);
}
surfaceOptions.SetDefaultFVarID(0);
surfaceOptions.EnableCaching(!args.noCacheFlag);
BfrSurfaceEvaluator<REAL> bfrEval(mesh, meshPos, meshUVs, surfaceOptions);
FarPatchEvaluator<REAL> farEval(mesh, meshPos, meshUVs, surfaceOptions);
//
// Initialize tolerances and variables to track differences:
//
REAL pTol = (args.absTolerance > 0.0f) ? args.absTolerance :
GetRelativeTolerance<REAL>(meshPos, args.relTolerance);
REAL d1Tol = pTol * 5.0f;
REAL d2Tol = d1Tol * 5.0f;
REAL uvTol = args.uvTolerance;
VectorDelta<REAL> pDelta(pTol);
VectorDelta<REAL> duDelta(d1Tol);
VectorDelta<REAL> dvDelta(d1Tol);
VectorDelta<REAL> duuDelta(d2Tol);
VectorDelta<REAL> duvDelta(d2Tol);
VectorDelta<REAL> dvvDelta(d2Tol);
VectorDelta<REAL> uvDelta(uvTol);
FaceDelta<REAL> faceDelta;
MeshDelta<REAL> meshDelta;
bool meshHasBeenLabeled = false;
int numFaces = mesh.GetNumFacesTotal();
for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
//
// Make sure both match in terms of identifying a limit surface:
//
assert(bfrEval.FaceHasLimit(faceIndex) ==
farEval.FaceHasLimit(faceIndex));
if (!farEval.FaceHasLimit(faceIndex)) continue;
//
// Declare/define a Tessellation to generate a consistent set of
// (u,v) locations to compare and evaluate:
//
int faceSize = mesh.GetLevel(0).GetFaceVertices(faceIndex).size();
Bfr::Parameterization faceParam(mesh.GetSchemeType(), faceSize);
assert(faceParam.IsValid());
Bfr::Tessellation faceTess(faceParam, args.uniformRes);
assert(faceTess.IsValid());
evalCoords.resize(2 * faceTess.GetNumCoords());
faceTess.GetCoords(&evalCoords[0]);
//
// Before evaluating and comparing, run the test to convert the
// parametric coords to Ptex and back:
//
if (args.ptexConvert) {
for (int i = 0; i < faceTess.GetNumCoords(); ++i) {
ValidatePtexConversion<REAL>(faceParam, &evalCoords[2*i]);
}
}
//
// Evaluate and capture results of comparisons between results:
//
bfrEval.Evaluate(faceIndex, evalCoords, bfrResults);
farEval.Evaluate(faceIndex, evalCoords, farResults);
if (comparePos) {
pDelta.Compare(bfrResults.p, farResults.p);
}
if (compareD1) {
duDelta.Compare(bfrResults.du, farResults.du);
dvDelta.Compare(bfrResults.dv, farResults.dv);
}
if (compareD2) {
duuDelta.Compare(bfrResults.duu, farResults.duu);
duvDelta.Compare(bfrResults.duv, farResults.duv);
dvvDelta.Compare(bfrResults.dvv, farResults.dvv);
}
if (compareUV) {
uvDelta.Compare(bfrResults.uv, farResults.uv);
}
//
// Note collective differences for this face and report:
//
faceDelta.Clear();
faceDelta.AddPDelta(pDelta);
faceDelta.AddDuDelta(duDelta);
faceDelta.AddDvDelta(dvDelta);
faceDelta.AddDuuDelta(duuDelta);
faceDelta.AddDuvDelta(duvDelta);
faceDelta.AddDvvDelta(dvvDelta);
faceDelta.AddUVDelta(uvDelta);
if (args.printFaceDiffs && faceDelta.hasDeltas) {
if (!meshHasBeenLabeled) {
meshHasBeenLabeled = true;
printf("'%s':\n", meshName.c_str());
}
printf("\t Face %d:\n", faceIndex);
if (comparePos && faceDelta.numPDeltas) {
printf("\t\t POS:%6d diffs, max delta P = %g\n",
faceDelta.numPDeltas, (float) faceDelta.maxPDelta);
}
if (compareD1 && faceDelta.numD1Deltas) {
printf("\t\t D1:%6d diffs, max delta D1 = %g\n",
faceDelta.numD1Deltas, (float) faceDelta.maxD1Delta);
}
if (compareD2 && faceDelta.numD2Deltas) {
printf("\t\t D2:%6d diffs, max delta D2 = %g\n",
faceDelta.numD2Deltas, (float) faceDelta.maxD2Delta);
}
if (compareUV && faceDelta.hasUVDeltas) {
printf("\t\t UV:%6d diffs, max delta UV = %g\n",
uvDelta.numDeltas, (float) uvDelta.maxDelta);
}
}
// Add the results for this face to the collective mesh delta:
meshDelta.AddFace(faceDelta);
}
//
// Report the differences for this mesh:
//
if (meshDelta.numFacesWithDeltas) {
if (args.printFaceDiffs) {
printf("\t Total:\n");
} else {
printf("'%s':\n", meshName.c_str());
}
}
if (comparePos && meshDelta.numFacesWithPDeltas) {
printf("\t\tPOS diffs:%6d faces, max delta P = %g\n",
meshDelta.numFacesWithPDeltas, (float) meshDelta.maxPDelta);
}
if (compareD1 && meshDelta.numFacesWithD1Deltas) {
printf("\t\t D1 diffs:%6d faces, max delta D1 = %g\n",
meshDelta.numFacesWithD1Deltas, (float) meshDelta.maxD1Delta);
}
if (compareD2 && meshDelta.numFacesWithD2Deltas) {
printf("\t\t D2 diffs:%6d faces, max delta D2 = %g\n",
meshDelta.numFacesWithD2Deltas, (float) meshDelta.maxD2Delta);
}
if (compareUV && meshDelta.numFacesWithUVDeltas) {
printf("\t\t UV diffs:%6d faces, max delta UV = %g\n",
meshDelta.numFacesWithUVDeltas, (float) meshDelta.maxUVDelta);
}
return meshDelta.numFacesWithDeltas;
}
//
// Run the comparison for a given Shape in single or double precision:
//
template <typename REAL>
int
testShape(ShapeDesc const & shapeDesc, Args const & args) {
//
// Get the TopologyRefiner, positions and UVs for the Shape, report
// failure to generate the shape, and run the test:
//
std::string const & meshName = shapeDesc.name;
std::vector< Vec3<REAL> > basePos;
std::vector< Vec3<REAL> > baseUV;
Far::TopologyRefiner * refiner =
createTopologyRefiner<REAL>(shapeDesc, basePos, baseUV, args);
if (refiner == 0) {
if (args.printWarnings) {
fprintf(stderr,
"Warning: Shape '%s' ignored (unable to construct refiner)\n",
meshName.c_str());
}
return -1;
}
int nFailures = testMesh<REAL>(*refiner, meshName, basePos, baseUV, args);
delete refiner;
return nFailures;
}
//
// Run comparison tests on a list of shapes using command line options:
//
int
main(int argc, char **argv) {
Args args(argc, argv);
// Capture relevant command line options used here:
if (args.printArgs) {
args.Print();
}
//
// Initialize the list of shapes and test each (or only the first):
//
// - currently the internal list can be overridden on the command
// line (so use of wildcards is possible)
//
// - still exploring additional command line options, e.g. hoping
// to specify a list of shape names from the internal list...
//
// So a bit more to be done here...
//
std::vector<ShapeDesc>& shapeList = g_shapes;
if (!args.shapes.empty()) {
shapeList.swap(args.shapes);
}
if (shapeList.empty()) {
if (args.shapesAll) {
initShapesAll(shapeList);
} else {
initShapes(shapeList);
}
}
int shapesToTest = (int) shapeList.size();
int shapesIgnored = 0;
if ((args.shapeCount > 0) && (args.shapeCount < shapesToTest)) {
shapesIgnored = shapesToTest - args.shapeCount;
shapesToTest = args.shapeCount;
}
if (args.printProgress) {
printf("Testing %d shapes", shapesToTest);
if (shapesIgnored) {
printf(" (%d ignored)", shapesIgnored);
}
printf(":\n");
}
//
// Run the comparison test for each shape (ShapeDesc) in the
// specified precision and report results:
//
int shapesFailed = 0;
for (int shapeIndex = 0; shapeIndex < shapesToTest; ++shapeIndex) {
ShapeDesc & shapeDesc = shapeList[shapeIndex];
if (args.printProgress) {
printf("%4d of %d: '%s'\n", 1 + shapeIndex, shapesToTest,
shapeDesc.name.c_str());
}
int nFailures = args.doublePrecision ?
testShape<double>(shapeDesc, args) :
testShape<float>(shapeDesc, args);
if (nFailures < 0) {
// Possible error/warning...?
++ shapesFailed;
}
if (nFailures > 0) {
++ shapesFailed;
}
}
if (args.printSummary) {
printf("\n");
if (shapesFailed == 0) {
printf("All tests passed for %d shapes\n", shapesToTest);
} else {
printf("Total failures: %d of %d shapes\n", shapesFailed,
shapesToTest);
}
}
return (shapesFailed == args.passCount) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Some files were not shown because too many files have changed in this diff Show More