OpenSubdiv/opensubdiv/bfr/surfaceFactory.h
Barry Fowler a1c7be7c8e Addition of Bfr interface (1 of 4): opensubdiv/bfr
This set of commits includes the addition of a new evaluation interface
that treats a subdivision mesh more like a piecewise parametric surface
primitive.  The new interface was placed in namespace "Bfr" for "Base
Face Representation" as all concepts and classes relate to a single face
of the base mesh.
2022-08-02 20:38:17 -07:00

564 lines
23 KiB
C++

//
// 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 */