Merge branch 'dev' into dev-mac-subproject
@ -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)
|
||||
|
||||
|
@ -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: |
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 |
|
||||
|
710
documentation/bfr_overview.rst
Normal 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
|
||||
|
BIN
documentation/images/bfr_eval_surface.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
documentation/images/bfr_eval_surfacefactory.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
documentation/images/bfr_param_patch_quad.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
documentation/images/bfr_param_patch_tri.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
documentation/images/bfr_param_subfaces.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
documentation/images/bfr_param_subfaces_3.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
documentation/images/bfr_param_subfaces_3_uv.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
documentation/images/bfr_param_subfaces_5.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
documentation/images/bfr_param_subfaces_5_uv.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
documentation/images/bfr_param_subfaces_abc.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
documentation/images/bfr_param_subfaces_abc_uv.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
documentation/images/bfr_tess_diff_edges_gpu.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
documentation/images/bfr_tess_diff_edges_osd.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
documentation/images/bfr_tess_diff_tri_gpu.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
documentation/images/bfr_tess_diff_tri_osd.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
documentation/images/bfr_tess_mXn_quad_A.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
documentation/images/bfr_tess_mXn_quad_B.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
documentation/images/bfr_tess_nonuni_pent_A.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
documentation/images/bfr_tess_nonuni_pent_B.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
documentation/images/bfr_tess_nonuni_quad_A.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
documentation/images/bfr_tess_nonuni_quad_B.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
documentation/images/bfr_tess_nonuni_tri_A.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
documentation/images/bfr_tess_nonuni_tri_B.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
documentation/images/bfr_tess_pent_quads.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
documentation/images/bfr_tess_pent_tris.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
documentation/images/bfr_tess_quad_order.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
documentation/images/bfr_tess_quad_quads.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
documentation/images/bfr_tess_quad_tris.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
documentation/images/bfr_tess_tri_order.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
documentation/images/bfr_tess_uni_quad_5.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
documentation/images/bfr_tess_uni_quad_8.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
documentation/images/bfr_tess_uni_tri_5.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
documentation/images/bfr_tess_uni_tri_8.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
documentation/images/bfr_tutorial_1_2.png
Normal file
After Width: | Height: | Size: 794 KiB |
BIN
documentation/images/bfr_tutorial_2_1.png
Normal file
After Width: | Height: | Size: 837 KiB |
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
@ -69,7 +69,8 @@ def Process(srcfile, title):
|
||||
rest += ("\n"
|
||||
"----\n"
|
||||
"\n"
|
||||
".. code:: c\n")
|
||||
".. code:: c++\n"
|
||||
"\n")
|
||||
|
||||
code = ReadFile(srcfile)
|
||||
|
||||
|
@ -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*.
|
||||
|
||||
+---------------------------------------+---------------------------------------+
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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}
|
||||
|
110
opensubdiv/bfr/CMakeLists.txt
Normal 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 )
|
||||
|
||||
#-------------------------------------------------------------------------------
|
557
opensubdiv/bfr/faceSurface.cpp
Normal 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
|
215
opensubdiv/bfr/faceSurface.h
Normal 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 */
|
172
opensubdiv/bfr/faceTopology.cpp
Normal 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
|
119
opensubdiv/bfr/faceTopology.h
Normal 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 */
|
903
opensubdiv/bfr/faceVertex.cpp
Normal 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
@ -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 */
|
98
opensubdiv/bfr/faceVertexSubset.h
Normal 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
@ -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
@ -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 */
|
916
opensubdiv/bfr/irregularPatchBuilder.cpp
Normal 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
|
179
opensubdiv/bfr/irregularPatchBuilder.h
Normal 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 */
|
63
opensubdiv/bfr/irregularPatchType.h
Normal 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
@ -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 */
|
238
opensubdiv/bfr/parameterization.cpp
Normal 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
|
247
opensubdiv/bfr/parameterization.h
Normal 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 */
|
503
opensubdiv/bfr/patchTree.cpp
Normal 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
@ -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 */
|
598
opensubdiv/bfr/patchTreeBuilder.cpp
Normal 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
|
145
opensubdiv/bfr/patchTreeBuilder.h
Normal 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 */
|
543
opensubdiv/bfr/pointOperations.h
Normal 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 */
|
554
opensubdiv/bfr/refinerSurfaceFactory.cpp
Normal 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
|
179
opensubdiv/bfr/refinerSurfaceFactory.h
Normal 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 */
|
441
opensubdiv/bfr/regularPatchBuilder.cpp
Normal 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
|
106
opensubdiv/bfr/regularPatchBuilder.h
Normal 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
@ -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
@ -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 */
|
85
opensubdiv/bfr/surfaceData.cpp
Normal 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
|
129
opensubdiv/bfr/surfaceData.h
Normal 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 */
|
1293
opensubdiv/bfr/surfaceFactory.cpp
Normal file
563
opensubdiv/bfr/surfaceFactory.h
Normal 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 */
|
81
opensubdiv/bfr/surfaceFactoryCache.cpp
Normal 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
|
150
opensubdiv/bfr/surfaceFactoryCache.h
Normal 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 */
|
236
opensubdiv/bfr/surfaceFactoryMeshAdapter.h
Normal 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 */
|
2504
opensubdiv/bfr/tessellation.cpp
Normal file
385
opensubdiv/bfr/tessellation.h
Normal 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 */
|
122
opensubdiv/bfr/vertexDescriptor.cpp
Normal 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
|
449
opensubdiv/bfr/vertexDescriptor.h
Normal 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
@ -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 */
|
@ -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]; }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -26,6 +26,8 @@ add_subdirectory(common)
|
||||
|
||||
if (NOT NO_REGRESSION)
|
||||
|
||||
add_subdirectory(bfr_evaluate)
|
||||
|
||||
add_subdirectory(hbr_regression)
|
||||
|
||||
add_subdirectory(far_regression)
|
||||
|
54
regression/bfr_evaluate/CMakeLists.txt
Normal 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)
|
||||
|
208
regression/bfr_evaluate/bfrSurfaceEvaluator.cpp
Normal 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>;
|
||||
|
82
regression/bfr_evaluate/bfrSurfaceEvaluator.h
Normal 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;
|
||||
};
|
291
regression/bfr_evaluate/farPatchEvaluator.cpp
Normal 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>;
|
||||
|
83
regression/bfr_evaluate/farPatchEvaluator.h
Normal 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;
|
||||
};
|
99
regression/bfr_evaluate/init_shapes.h
Normal 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));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
149
regression/bfr_evaluate/init_shapes_all.h
Normal 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));
|
||||
}
|
954
regression/bfr_evaluate/main.cpp
Normal 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;
|
||||
}
|