Add Chromium-only Blender WebEngine parity work
This commit is contained in:
93
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/CMakeLists.txt
vendored
Normal file
93
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
#
|
||||
# Copyright 2021 Pixar
|
||||
#
|
||||
# Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
# https://opensubdiv.org/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 )
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
540
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceSurface.cpp
vendored
Normal file
540
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceSurface.cpp
vendored
Normal file
@@ -0,0 +1,540 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
198
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceSurface.h
vendored
Normal file
198
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceSurface.h
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
155
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceTopology.cpp
vendored
Normal file
155
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceTopology.cpp
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
102
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceTopology.h
vendored
Normal file
102
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceTopology.h
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
988
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceVertex.cpp
vendored
Normal file
988
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceVertex.cpp
vendored
Normal file
@@ -0,0 +1,988 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/license.
|
||||
//
|
||||
|
||||
#include "../bfr/faceVertex.h"
|
||||
#include "../sdc/crease.h"
|
||||
#include "../vtr/array.h"
|
||||
#include "../vtr/stackBuffer.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, fvIndices);
|
||||
}
|
||||
|
||||
//
|
||||
// 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,
|
||||
Index const fvIndices[]) {
|
||||
|
||||
//
|
||||
// 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.
|
||||
// This requires closer inspection to confirm, so make use of
|
||||
// as many pre-conditions available here to avoid doing so:
|
||||
isNonManifoldCrease = !_isExpInfSharp &&
|
||||
(numNonManifoldEdges == 2) && (GetNumFaces() > numEdges) &&
|
||||
testNonManifoldCrease(edges, numEdges, fvIndices);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FaceVertex::testNonManifoldCrease(Edge const edges[], int numEdges,
|
||||
Index const fvIndices[]) const {
|
||||
//
|
||||
// Local struct that keeps track of face-corners remaining around
|
||||
// a vertex as those visited as part of connected manifold subsets
|
||||
// are removed during inspection:
|
||||
//
|
||||
struct FaceCornerArray {
|
||||
typedef Vtr::internal::StackBuffer<int,32,true> MemberArray;
|
||||
typedef Vtr::ConstArray<int> SearchArray;
|
||||
|
||||
MemberArray leading;
|
||||
MemberArray trailing;
|
||||
int size;
|
||||
|
||||
FaceCornerArray(int n) : leading(n), trailing(n), size(n) { }
|
||||
|
||||
int FindLeading(int vertex) const {
|
||||
return SearchArray(leading, size).FindIndex(vertex);
|
||||
}
|
||||
int FindTrailing(int vertex) const {
|
||||
return SearchArray(trailing, size).FindIndex(vertex);
|
||||
}
|
||||
void RemoveFace(int index) {
|
||||
std::swap(leading[index], leading[size - 1]);
|
||||
std::swap(trailing[index], trailing[size - 1]);
|
||||
-- size;
|
||||
}
|
||||
int RemoveManifoldSubset(int startVertex, int endVertex) {
|
||||
assert(startVertex != endVertex);
|
||||
|
||||
for (int nextVertex = startVertex; nextVertex != endVertex; ) {
|
||||
int faceIndex = FindLeading(nextVertex);
|
||||
if (faceIndex < 0) return (nextVertex == startVertex) ? 0 : -1;
|
||||
|
||||
nextVertex = trailing[faceIndex];
|
||||
if (nextVertex == startVertex) return -1;
|
||||
|
||||
RemoveFace(faceIndex);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Identify the vertices at the ends of the two non-manifold edges
|
||||
// of the potential crease:
|
||||
//
|
||||
int creaseEnd[2] = { -1, -1 };
|
||||
|
||||
for (int i = 0; i < numEdges; ++i) {
|
||||
if (edges[i].nonManifold) {
|
||||
creaseEnd[creaseEnd[0] >= 0] = edges[i].endVertex;
|
||||
}
|
||||
}
|
||||
assert((creaseEnd[0] >= 0) && (creaseEnd[1] >= 0));
|
||||
|
||||
//
|
||||
// Use the locally defined set of face-edge pairs to determine if
|
||||
// the collection of faces around the vertex forms a non-manifold
|
||||
// crease.
|
||||
//
|
||||
// First initialize the FaceCornerArray from the face vertices:
|
||||
//
|
||||
int numFaces = GetNumFaces();
|
||||
|
||||
FaceCornerArray faceCorners(numFaces);
|
||||
|
||||
for (int i = 0; i < numFaces; ++i) {
|
||||
faceCorners.leading[i] = GetFaceIndexLeading( i, fvIndices);
|
||||
faceCorners.trailing[i] = GetFaceIndexTrailing(i, fvIndices);
|
||||
}
|
||||
|
||||
// Remove faces for manifold subsets in one direction:
|
||||
int removed = 0;
|
||||
do {
|
||||
removed = faceCorners.RemoveManifoldSubset(creaseEnd[0], creaseEnd[1]);
|
||||
if (removed < 0) {
|
||||
return false;
|
||||
}
|
||||
} while (removed);
|
||||
|
||||
if (faceCorners.size == 0) return true;
|
||||
|
||||
// Remove faces for manifold subsets in the other direction:
|
||||
do {
|
||||
removed = faceCorners.RemoveManifoldSubset(creaseEnd[1], creaseEnd[0]);
|
||||
if (removed < 0) {
|
||||
return false;
|
||||
}
|
||||
} while (removed);
|
||||
|
||||
return (faceCorners.size == 0);
|
||||
}
|
||||
|
||||
} // end namespace Bfr
|
||||
|
||||
} // end namespace OPENSUBDIV_VERSION
|
||||
} // end namespace OpenSubdiv
|
||||
382
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceVertex.h
vendored
Normal file
382
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceVertex.h
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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[]);
|
||||
|
||||
bool testNonManifoldCrease(Edge const edges[], int numEdges,
|
||||
Index const faceVertIndices[]) const;
|
||||
|
||||
void finalizeUnOrderedTags(Edge const edges[], int numEdges,
|
||||
Index const faceVertIndices[]);
|
||||
|
||||
// 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 */
|
||||
81
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceVertexSubset.h
vendored
Normal file
81
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceVertexSubset.h
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
691
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/hash.cpp
vendored
Normal file
691
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/hash.cpp
vendored
Normal file
@@ -0,0 +1,691 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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;
|
||||
|
||||
#ifdef OPENSUBDIV3_BFR_HASH_INCLUDE_UNUSED_FUNCTIONS
|
||||
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
|
||||
#endif
|
||||
};
|
||||
|
||||
#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
|
||||
46
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/hash.h
vendored
Normal file
46
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/hash.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
987
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/irregularPatchBuilder.cpp
vendored
Normal file
987
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/irregularPatchBuilder.cpp
vendored
Normal file
@@ -0,0 +1,987 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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;
|
||||
}
|
||||
cHull.isVal2Interior = true;
|
||||
}
|
||||
}
|
||||
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:
|
||||
//
|
||||
// Currently a single val-2 interior vertex is handled without
|
||||
// the map in most cases. The presence of more than one val-2
|
||||
// interior vertex leads to convoluted topology traversals that
|
||||
// are avoided by the use of the map.
|
||||
//
|
||||
_controlFacesOverlap = (numVal2IntCorners > 1);
|
||||
|
||||
if (numVal2IntCorners == 1) {
|
||||
//
|
||||
// Identify and inspect the topology at the val-2 corner to
|
||||
// see if it warrants handling with the vertex map. Tag the
|
||||
// neighboring corners accordingly if not:
|
||||
//
|
||||
for (int corner = 0; corner < faceSize; ++corner) {
|
||||
CornerHull & cHull = _cornerHullInfo[corner];
|
||||
|
||||
if (cHull.isVal2Interior) {
|
||||
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
|
||||
|
||||
int oppFaceSize = cTop.GetFaceSize(cTop.GetFaceAfter(1));
|
||||
if (oppFaceSize == 3) {
|
||||
// All three corners of the opposite face are corners
|
||||
// of the base face
|
||||
_controlFacesOverlap = true;
|
||||
break;
|
||||
}
|
||||
if ((oppFaceSize == 4) && (numVal3IntAdjTris == (faceSize-2))) {
|
||||
// Possible "pyramid" where a single exterior vertex
|
||||
// must added as a special case
|
||||
_controlFacesOverlap = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Tag the preceding corner of the val-2 corner:
|
||||
int prevCorner = corner ? (corner - 1) : (faceSize - 1);
|
||||
_cornerHullInfo[prevCorner].preVal2Interior = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If no significant overlap is present, adjust the control vertex
|
||||
// counts for those preceding the val-2 corner (decrementing by 1)
|
||||
// and adjust control vertex offsets for all that follow:
|
||||
//
|
||||
if (!_controlFacesOverlap) {
|
||||
_numControlVerts = faceSize;
|
||||
for (int corner = 0; corner < faceSize; ++corner) {
|
||||
CornerHull & cHull = _cornerHullInfo[corner];
|
||||
|
||||
cHull.nextControlVert = _numControlVerts;
|
||||
cHull.numControlVerts -= cHull.preVal2Interior;
|
||||
|
||||
_numControlVerts += cHull.numControlVerts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_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)) ? (1 + cHull.preVal2Interior) : 0;
|
||||
int M = (S - 2) - (cSub.IsBoundary() ? 0 : 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)) ? (1 + cHull.preVal2Interior) : 0;
|
||||
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)), cHull.preVal2Interior);
|
||||
}
|
||||
*faceSizes++ = S;
|
||||
faceVerts += S;
|
||||
|
||||
nextVert += S - 2 - (cHull.preVal2Interior && (j == (N-1)));
|
||||
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)), cHull.preVal2Interior);
|
||||
}
|
||||
*faceSizes++ = S;
|
||||
faceVerts += S;
|
||||
|
||||
nextVert += S - 2 - (cHull.preVal2Interior && (j == (N-1)));
|
||||
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 a special case that uses the vertex
|
||||
// map to identify local control vertices from their indices. The second
|
||||
// is the trivial case -- where all vertices other than the initial
|
||||
// corner vertex lie on the perimeter and do not wrap around. The third
|
||||
// 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, int numVal2InLast) const {
|
||||
|
||||
//
|
||||
// When identifying local face-vertices for a control face adjacent
|
||||
// to a corner, care is required to deal with the possibility of
|
||||
// exterior vertices being shared with control faces associated with
|
||||
// the next corner vertex -- which becomes an issue when sharing
|
||||
// between the last and first corner.
|
||||
//
|
||||
// After the initial corner face-vertex, the remaining face-vertices
|
||||
// are identified in three groups:
|
||||
// - a simple sequence of exterior vertices following the corner,
|
||||
// which excludes the last two face-vertices where any sharing
|
||||
// may occur
|
||||
// - the next-to-last face-vertex -- an exterior vertex which may
|
||||
// be shared and wrap around the base face
|
||||
// - the last face vertex -- an exterior vertex which may also be
|
||||
// shared/wrapped, or an adj corner vertex for the last face
|
||||
//
|
||||
int S = numFVerts;
|
||||
int N = _surface.GetFaceSize();
|
||||
|
||||
// Start with the corner vertex:
|
||||
*fVerts++ = corner;
|
||||
|
||||
// The simple sequence of exterior vertices follows the corner:
|
||||
int numSequentialVerts = S - 2 - 1 - (lastFace ? numVal2InLast : 0);
|
||||
for (int i = 0; i < numSequentialVerts; ++i) {
|
||||
*fVerts++ = nextPerimeterVert++;
|
||||
}
|
||||
|
||||
// The next-to-last face-vertex may be shared with the next face
|
||||
// and so may wrap around the entire face:
|
||||
int nextToLastPerimOfFace = nextPerimeterVert++;
|
||||
if (nextToLastPerimOfFace == _numControlVerts) {
|
||||
nextToLastPerimOfFace = N;
|
||||
}
|
||||
*fVerts++ = nextToLastPerimOfFace;
|
||||
|
||||
// The last face-vertex may be exterior or the adjacent corner:
|
||||
if (!lastFace) {
|
||||
// If exterior, it may also be shared with the next face and
|
||||
// wrap around the entire face:
|
||||
int lastPerimOfFace = nextPerimeterVert++;
|
||||
if (lastPerimOfFace == _numControlVerts) {
|
||||
lastPerimOfFace = N;
|
||||
}
|
||||
*fVerts = lastPerimOfFace;
|
||||
} else {
|
||||
// If the next/adjacent corner, successive corner vertices may
|
||||
// need to be added if that corner is a val-2 interior vertex:
|
||||
for (int i = numVal2InLast; i > 0; --i) {
|
||||
*fVerts++ = (corner + 1 + i) % N;
|
||||
}
|
||||
*fVerts = (corner + 1) % N;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Append boundary edge sharpness to existing sharp edges:
|
||||
edgeVertPairs += *numSharpEdges * 2;
|
||||
edgeSharpness += *numSharpEdges;
|
||||
|
||||
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
|
||||
167
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/irregularPatchBuilder.h
vendored
Normal file
167
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/irregularPatchBuilder.h
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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:
|
||||
// This will not be necessary if use of the index map is ever removed:
|
||||
bool ControlHullDependsOnMeshIndices() const { return _useControlVertMap; }
|
||||
|
||||
// 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 isVal2Interior : 1;
|
||||
unsigned int preVal2Interior : 1;
|
||||
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, int numVal2InLast) 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 */
|
||||
46
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/irregularPatchType.h
vendored
Normal file
46
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/irregularPatchType.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
38
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/limits.h
vendored
Normal file
38
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/limits.h
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
220
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/parameterization.cpp
vendored
Normal file
220
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/parameterization.cpp
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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());
|
||||
|
||||
// Include an interval around the domain when identifying the subface:
|
||||
int uTile = (int) (uvCoord[0] + 0.25f);
|
||||
int vTile = (int) (uvCoord[1] + 0.25f);
|
||||
|
||||
// Clamp tiles in the U and V direction to valid sub-face tiles on
|
||||
// the boundaries of the rectangular subset. This is trivial in the
|
||||
// U direction but less so in V as the top row is usually not full
|
||||
// (so max in V is greater for the first colums in U):
|
||||
uTile = std::max(0, std::min(uTile, _uDim - 1));
|
||||
|
||||
vTile = std::max(0, vTile);
|
||||
if ((vTile * _uDim + uTile) >= _faceSize) {
|
||||
// Only clamp upper bound in V when tile exceeds face size:
|
||||
vTile = (_faceSize / _uDim) - 1 + (uTile < (_faceSize % _uDim));
|
||||
}
|
||||
|
||||
// Be sure to support in-place conversion (i.e. uvCoord == subCoord):
|
||||
subCoord[0] = uvCoord[0] - (REAL) uTile;
|
||||
subCoord[1] = uvCoord[1] - (REAL) vTile;
|
||||
if (normalized) {
|
||||
subCoord[0] *= 2.0f;
|
||||
subCoord[1] *= 2.0f;
|
||||
}
|
||||
return vTile * _uDim + 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
|
||||
230
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/parameterization.h
vendored
Normal file
230
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/parameterization.h
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
486
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/patchTree.cpp
vendored
Normal file
486
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/patchTree.cpp
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
234
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/patchTree.h
vendored
Normal file
234
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/patchTree.h
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
582
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/patchTreeBuilder.cpp
vendored
Normal file
582
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/patchTreeBuilder.cpp
vendored
Normal file
@@ -0,0 +1,582 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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.SetSecondaryLevel(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
|
||||
128
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/patchTreeBuilder.h
vendored
Normal file
128
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/patchTreeBuilder.h
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
526
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/pointOperations.h
vendored
Normal file
526
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/pointOperations.h
vendored
Normal file
@@ -0,0 +1,526 @@
|
||||
//
|
||||
// Copyright 2022 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
537
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/refinerSurfaceFactory.cpp
vendored
Normal file
537
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/refinerSurfaceFactory.cpp
vendored
Normal file
@@ -0,0 +1,537 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
162
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/refinerSurfaceFactory.h
vendored
Normal file
162
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/refinerSurfaceFactory.h
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
424
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/regularPatchBuilder.cpp
vendored
Normal file
424
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/regularPatchBuilder.cpp
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
89
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/regularPatchBuilder.h
vendored
Normal file
89
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/regularPatchBuilder.h
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
680
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surface.cpp
vendored
Normal file
680
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surface.cpp
vendored
Normal file
@@ -0,0 +1,680 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
429
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surface.h
vendored
Normal file
429
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surface.h
vendored
Normal file
@@ -0,0 +1,429 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
68
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceData.cpp
vendored
Normal file
68
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceData.cpp
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
112
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceData.h
vendored
Normal file
112
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceData.h
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
1281
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactory.cpp
vendored
Normal file
1281
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactory.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
546
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactory.h
vendored
Normal file
546
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactory.h
vendored
Normal file
@@ -0,0 +1,546 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
64
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactoryCache.cpp
vendored
Normal file
64
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactoryCache.cpp
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
133
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactoryCache.h
vendored
Normal file
133
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactoryCache.h
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
219
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactoryMeshAdapter.h
vendored
Normal file
219
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/surfaceFactoryMeshAdapter.h
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
2555
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/tessellation.cpp
vendored
Normal file
2555
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/tessellation.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
379
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/tessellation.h
vendored
Normal file
379
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/tessellation.h
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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.
|
||||
///
|
||||
/// In the case of facets, the size is constant for all facets of the
|
||||
/// output array but triangles may sometimes be generated in 4-sided
|
||||
/// facets with non-uniform tessellation rates (quad-preservation
|
||||
/// reduces but does not eliminate all triangles). Such triangles are
|
||||
/// indicated by the use of an invalid/negative index in the fourth
|
||||
/// position.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// When using facets of size 4, triangles may sometimes be generated
|
||||
/// and are indicated by the use of an invalid/negative index in the
|
||||
/// fourth position.
|
||||
///
|
||||
/// 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 */
|
||||
105
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/vertexDescriptor.cpp
vendored
Normal file
105
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/vertexDescriptor.cpp
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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
|
||||
432
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/vertexDescriptor.h
vendored
Normal file
432
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/vertexDescriptor.h
vendored
Normal file
@@ -0,0 +1,432 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
146
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/vertexTag.h
vendored
Normal file
146
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/vertexTag.h
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/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 */
|
||||
Reference in New Issue
Block a user