OpenSubdiv/opensubdiv/bfr/tessellation.cpp

2505 lines
83 KiB
C++
Raw Normal View History

//
// Copyright 2021 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../bfr/tessellation.h"
#include <cstring>
#include <cstdio>
#include <cassert>
#include <limits>
#include <algorithm>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Tessellation patterns are composed of concentric rings of points (or
// "coords" for coordinates) and facets -- beginning with the boundary
// and moving inward. Each ring of can be further divided into subsets
// corresponding elements associated with each edge.
//
// While the nature of these rings is similar for the different types of
// parameterizations, each is different enough to have warranted its own
// implementation at a high level. Lower level utilities for assembling
// the rings from strips of coords and facets are common to all.
//
// WIP - consider moving some of these implementation details to separate
// internal header and source files
//
namespace {
//
// Simple classes to provide array-like interfaces to the primitive
// data buffers passed by clients. Both parametric coordinate pairs
// and the integer tuples (size 3 or 4) representing a single facet
// of a tessellation are represented here.
//
// These array interfaces are "minimal" in the sense that they provide
// only what is needed here -- rather than trying to support a wider
// range of use where full generality is needed (e.g. the arithmetic
// operators are limited to those used here for advancing through and
// assigning elements of the array, so the full range required for
// arbitrary address arithmetic are not included).
//
// Both arrays support a user-specified stride within which the tuple
// for the coord or facet is assigned.
//
// Floating point pair and its array for points of a tessellation:
template <typename REAL>
class Coord2 {
public:
Coord2(REAL * data) : _uv(data) { }
Coord2() : _uv(0) { }
~Coord2() { }
void Set(REAL u, REAL v) { _uv[0] = u, _uv[1] = v; }
REAL const & operator[](int index) const { return _uv[index]; }
REAL & operator[](int index) { return _uv[index]; }
private:
REAL * _uv;
};
template <typename REAL>
class Coord2Array {
public:
Coord2Array(REAL * data, int stride) : _data(data), _stride(stride) { }
Coord2Array() : _data(0), _stride(2) { }
~Coord2Array() { }
Coord2Array operator+(int offset) {
return Coord2Array(_data + offset*_stride, _stride);
}
Coord2<REAL> operator[](int index) {
return Coord2<REAL>(_data + index*_stride);
}
private:
REAL * _data;
int _stride;
};
// Integer 3- or 4-tuple and its array for facets of a tessellation:
class Facet {
public:
Facet(int * indices, int n) : _T(indices), _size(n) { }
Facet() : _T(0), _size(4) { }
~Facet() { }
void Set(int a, int b, int c) {
// Assign size-1 to ensure last index of 4-tuple is set
_T[_size - 1] = -1;
_T[0] = a, _T[1] = b, _T[2] = c;
}
void Set(int a, int b, int c, int d) {
_T[0] = a, _T[1] = b, _T[2] = c, _T[3] = d;
}
int const & operator[](int index) const { return _T[index]; }
int & operator[](int index) { return _T[index]; }
private:
int * _T;
int _size;
};
class FacetArray {
public:
FacetArray(int * data, int size, int stride) :
_data(data), _size(size), _stride(stride) { }
~FacetArray() { }
FacetArray operator+(int offset) {
return FacetArray(_data + offset*_stride, _size, _stride);
}
Facet operator[](int index) {
return Facet(_data + index*_stride, _size);
}
private:
FacetArray() : _data(0), _size(4), _stride(4) { }
int * _data;
int _size;
int _stride;
};
//
// Functions for assembling simple, common sets of coordinate pairs:
//
template <typename REAL>
inline int
appendUIsoLine(Coord2Array<REAL> P, int n, REAL u, REAL v, REAL dv) {
for (int i = 0; i < n; ++i, v += dv) {
P[i].Set(u,v);
}
return n;
}
template <typename REAL>
inline int
appendVIsoLine(Coord2Array<REAL> P, int n, REAL u, REAL v, REAL du) {
for (int i = 0; i < n; ++i, u += du) {
P[i].Set(u,v);
}
return n;
}
template <typename REAL>
inline int
appendUVLine(Coord2Array<REAL> P, int n, REAL u, REAL v, REAL du, REAL dv) {
for (int i = 0; i < n; ++i, u += du, v += dv) {
P[i].Set(u,v);
}
return n;
}
//
// Functions for assembling simple, common sets of facets:
//
inline int
appendTri(FacetArray facets, int t0, int t1, int t2) {
facets[0].Set(t0, t1, t2);
return 1;
}
inline int
appendQuad(FacetArray facets, int q0, int q1, int q2, int q3,
int triangulationSign) {
if (triangulationSign == 0) {
// no triangulation
facets[0].Set(q0, q1, q2, q3);
return 1;
} else if (triangulationSign > 0) {
// triangulate along diagonal in direction of leading edge
facets[0].Set(q0, q1, q2);
facets[1].Set(q2, q3, q0);
return 2;
} else {
// triangulate along diagonal opposing the leading edge
facets[0].Set(q2, q3, q1);
facets[1].Set(q0, q1, q3);
return 2;
}
}
inline int
appendTriFan(FacetArray facets, int size, int startIndex) {
for (int i = 1; i <= size; ++i) {
facets[i-1].Set(startIndex + (i - 1),
startIndex + ((i < size) ? i : 0),
startIndex + size);
}
return size;
}
//
// Useful struct for storing bounding indices and other topology of
// a strip of facets so points can be connected in various ways:
//
// A strip of facets is defined between an outer and inner ring of
// points -- denoted as follows, where the "i" and "o" prefixes are
// used to designate points on the inner and outer rings:
//
// oPrev --- iFirst ... iFirst+/-i ... iLast --- oLast+1
// | |
// oFirst --- oFirst+1 ... oFirst+j ... oFirst+N-1 --- oLast
//
// Since these points form part of a ring, they will wrap around to
// the beginning of the ring for the last edge and so the sequence
// is not always sequential. Transitions to the "first" and "last"
// of both the outer and inner rings are potentially discontinuous,
// which is why they are provided as separate members.
//
// This topological structure is similar but slightly different for
// quad-based versus triangular parameterizations. For quad-based
// parameterizations the parametric range of the inner and outer
// sequences are the same, but for triangular, the extent of the
// inner ring is one edge less (and the triangular domain may be
// offset a half edge length so that uniformly spaced points on
// both will alternate).
//
struct FacetStrip {
public:
FacetStrip() { std::memset((void*) this, 0, sizeof(*this)); }
int connectUniformQuads( FacetArray facets) const;
int connectUniformTris( FacetArray facets) const;
int connectNonUniformFacets(FacetArray facets) const;
public:
// Members defining how the strip should be used:
unsigned int quadTopology : 1;
unsigned int quadTriangulate : 1;
unsigned int innerReversed : 1;
unsigned int excludeFirstFace : 1;
unsigned int splitFirstFace : 1;
unsigned int splitLastFace : 1;
unsigned int includeLastFace : 1;
// Members defining the dimensions of the strip -- the number
// of "inner edges" potentially excludes the two edges that
// connect the inner ring to the outer:
int outerEdges;
int innerEdges;
// Members containing indices for points noted above. Since
// a strip may wrap around the concentric rings of points,
// pairs of points that may appear to have successive indices
// will not -- which is why these are assigned externally:
int outerFirst, outerLast, outerPrev;
int innerFirst, innerLast;
};
int
FacetStrip::connectUniformQuads(FacetArray facets) const {
assert(quadTopology);
assert(innerEdges == (outerEdges - 2));
//
// For connecting quads, the pattern is simplified as follows:
//
// oPrev ---- iFirst ... iLast ---- oLast+1
// | 3 2 | 3 2 | 3 2 |
// | 0 1 | 0 1 | 0 1 |
// oFirst -- oFirst+1 ... oFirst+N-1 -- oLast
//
// with the first and last quads not sharing any inner edges
// (between inner-first and inner-last) and potentially being
// split to include the triangle on the outer edge.
//
// It is typical for the first quad to always be included and
// for the last to be excluded -- the last quad usually being
// included by the next strip in the ring (unless split).
//
int nFacets = 0;
// Split or assign the first quad (precedes inner edges):
int out0 = outerFirst;
int in0 = innerFirst;
if (splitFirstFace) {
nFacets += appendTri(facets + nFacets, out0, out0 + 1, in0);
} else if (!excludeFirstFace) {
nFacets += appendQuad(facets + nFacets,
out0, out0 + 1, in0, outerPrev, quadTriangulate);
}
// Assign quads sharing the inner edges (last is a special case):
int outI = outerFirst + 1;
int inI = innerFirst;
if (innerEdges) {
int triSign = quadTriangulate;
int inDelta = innerReversed ? -1 : 1;
for (int i = 1; i <= innerEdges; ++i, ++outI, inI += inDelta) {
if (i > (innerEdges / 2)) triSign = - (int)quadTriangulate;
int outJ = outI + 1;
int inJ = (i < innerEdges) ? (inI + inDelta) : innerLast;
nFacets += appendQuad(facets + nFacets,
outI, outJ, inJ, inI, triSign);
}
}
// Split or assign the last quad (follows inner edges):
int outN = outerLast;
int inN = innerLast;
if (splitLastFace) {
nFacets += appendTri(facets + nFacets, outI, outN, inN);
} else if (includeLastFace) {
nFacets += appendQuad(facets + nFacets,
outI, outN, outN+1, inN, - (int)quadTriangulate);
}
return nFacets;
}
int
FacetStrip::connectUniformTris(FacetArray facets) const {
assert(!quadTopology);
assert(!excludeFirstFace);
assert(!includeLastFace);
assert(!innerReversed);
//
// Assign the set of tris for the "sawtooth" strip with N outer
// edges and N-3 inner edges of the inner ring:
//
// 1 3 2M-1
// oPrev --- iFirst -- i1 ... ii --- iLast -- oLast+1
// / 2\1 0/ \ / \ \1 0/ 2\ / \.
// /0 1\2 / \ / \ \2 /0 1\ / \.
// oFirst --- o1 ---- o2 .. oi ... oM --- oN-1 --- oLast
// 0 2 4 2M
//
// The first and last pair of tris may optionally be split by
// connecting the "first" or "last" points between the two rows
// (i.e. [oFirst, oFirst+1, iFirst]) which bisects the two
// triangles normally included.
//
// Following the first pair (or single tri if split), a single
// leading triangle ([o1, o2, iFirst] above) is then assigned,
// followed by pairs of adjacent tris below each inner edge:
// the first of the pair based on the inner edge, the second on
// the outer edge.
//
int nFacets = 0;
// Split or assign the first pair of tris (precedes inner edges):
int out0 = outerFirst;
int in0 = innerFirst;
if (splitFirstFace) {
nFacets += appendTri(facets + nFacets, out0, out0+1, in0);
} else {
nFacets += appendTri(facets + nFacets, out0, out0+1,outerPrev);
nFacets += appendTri(facets + nFacets, in0, outerPrev, out0+1);
}
// Assign the next tri -- preceding the pairs for the inner edges:
nFacets += appendTri(facets + nFacets, out0 + 1, out0 + 2, in0);
// Assign pair of tris below each inner edge (last is special):
int outI = outerFirst + 2;
int inI = innerFirst;
if (innerEdges) {
for (int i = 1; i <= innerEdges; ++i, ++inI, ++outI) {
int outJ = outI + 1;
int inJ = (i < innerEdges) ? (inI + 1) : innerLast;
nFacets += appendTri(facets + nFacets, inJ, inI, outI);
nFacets += appendTri(facets + nFacets, outI, outJ, inJ);
}
}
// Split the last pair of tris (follows inner edges):
int outN = outerLast;
int inN = innerLast;
if (splitLastFace) {
nFacets += appendTri(facets + nFacets, outI, outN, inN);
}
return nFacets;
}
int
FacetStrip::connectNonUniformFacets(FacetArray facets) const {
//
// General case:
//
// oPrev -- iFirst . ... i0+/-i ... . iLast --*
// | / . . \ |
// | / | | \ |
// oFirst -------- o0 ... o0+i ... oN-1 ------ oLast
//
// The sequence of edges -- both inner and outer -- is parameterized
// over the integer range [0 .. M*N] where M and N are the resolution
// (number of edges) of the inner and outer rings respectively.
//
// Note that the current implementation expects the faces at the
// ends to be "split", i.e. a diagonal edge created between the
// first/last points of the inner and outer rings at both ends.
// It is possible that this will later be relaxed (allowing an
// unsplit quad at the corner to be generated), as is currently
// the case with uniform strips. In the meantime, the caller is
// expected to explicitly request split corners to make it clear
// where they need to adapt later.
//
assert(splitFirstFace && splitLastFace);
int M = innerEdges + (quadTopology ? 2 : 3);
int N = outerEdges;
int dtOuter = M;
int dtInner = N;
int dtMin = std::min(dtInner, dtOuter);
int dtMax = std::max(dtInner, dtOuter);
// Use larger slope when M ~= N to accomodate tri insertion:
int dtSlopeMax = ((dtMax / 2) < dtMin) ? (dtMin - 1) : (dtMax / 2);
int tOuterLast = dtOuter * N;
int tOuterMiddle = tOuterLast / 2;
int tInnerOffset = 0;
int tInnerLast = dtInner * (M - 1);
// If tris, adjust parametric range for the inner edges:
if (!quadTopology) {
tInnerOffset = dtInner / 2;
tInnerLast += tInnerOffset - dtInner;
}
int dInner = innerReversed ? -1 : 1;
//
// Two points are successively identified on each of the inner and
// outer sequence of edges, from which facets will be generated:
//
// inner0 inner1
// * ----- * . . .
// /
// /
// * ----------- * . . .
// outer0 outer1
//
// Identify the parameterization and coordinate indices for the
// points starting the sequence:
//
int tOuter0 = 0;
int cOuter0 = outerFirst;
int tOuter1 = dtOuter;
int cOuter1 = (N == 1) ? outerLast : (outerFirst + 1);
int tInner0 = tInnerOffset + dtInner;
int cInner0 = innerFirst;
int tInner1 = tInner0 + (innerEdges ? dtInner : 0);
int cInner1 = (innerEdges == 1) ? innerLast : (innerFirst + dInner);
//
// Walk forward through the strip, identifying each successive quad
// and choosing the most "vertical" edge to use to triangulate it:
//
bool keepQuads = quadTopology && !quadTriangulate;
int nFacetsExpected = 0;
if (keepQuads) {
nFacetsExpected = std::max(innerEdges, outerEdges);
// Include a symmetric center triangle if any side is odd:
if ((nFacetsExpected & 1) == 0) {
nFacetsExpected += (innerEdges & 1) || (outerEdges & 1);
}
} else {
nFacetsExpected = innerEdges + outerEdges;
}
// These help maintain symmetry where possible:
int nFacetsLeading = nFacetsExpected / 2;
int nFacetsMiddle = nFacetsExpected & 1;
int middleFacet = nFacetsMiddle ? nFacetsLeading : -1;
bool middleQuad = keepQuads && (outerEdges & 1) && (innerEdges & 1);
//
// Assign all expected facets sequentially -- advancing references
// to the inner and outer edges according to what is used for each:
//
for (int facetIndex = 0; facetIndex < nFacetsExpected; ++facetIndex) {
bool generateTriOuter = false;
bool generateTriInner = false;
bool generateQuad = false;
//
// Detect simple cases first: the symmetric center face or
// triangles in the absence of an inner or outer edge:
//
if (facetIndex == middleFacet) {
if (middleQuad) {
generateQuad = true;
} else if (outerEdges & 1) {
generateTriOuter = true;
} else {
generateTriInner = true;
}
} else if (tInner1 == tInner0) {
generateTriOuter = true;
} else if (tOuter1 == tOuter0) {
generateTriInner = true;
} else {
//
// For the general case, assign a quad if specified and
// possible. Otherwise continue with a triangle. Both
// situations avoid poor aspect and preserve symmetry:
//
if (keepQuads) {
// If face is after the midpoint, use the same kind of
// face as its mirrored counterpart. Otherwise, reject a
// quad trying to cross the midpoint. Finally, test the
// slope of the "vertical" edge of the potential quad:
if (facetIndex >= nFacetsLeading) {
int mirroredFacetIndex = nFacetsLeading - 1 -
(facetIndex - nFacetsLeading - nFacetsMiddle);
generateQuad = (facets[mirroredFacetIndex][3] >= 0);
} else if ((tInner1 > tOuterMiddle) ||
(tOuter1 > tOuterMiddle)) {
generateQuad = false;
} else {
int dtSlope1 = std::abs(tOuter1 - tInner1);
generateQuad = (dtSlope1 <= dtSlopeMax);
}
}
if (!generateQuad) {
// Can't detect symmetric triangles as inner or outer as
// easily as quads, but the test is relatively simple --
// choose the diagonal spanning the shortest interval
// (when equal, choose relative to midpoint for symmetry):
int dtDiagToOuter1 = tOuter1 - tInner0;
int dtDiagToInner1 = tInner1 - tOuter0;
bool useOuterEdge = (dtDiagToOuter1 == dtDiagToInner1)
? (tOuter1 > tOuterMiddle)
: (dtDiagToOuter1 < dtDiagToInner1);
if (useOuterEdge) {
generateTriOuter = true;
} else {
generateTriInner = true;
}
}
}
// Assign the face as determined above:
if (generateTriOuter) {
facets[facetIndex].Set(cOuter0, cOuter1, cInner0);
} else if (generateTriInner) {
facets[facetIndex].Set(cInner1, cInner0, cOuter0);
} else {
facets[facetIndex].Set(cOuter0, cOuter1, cInner1, cInner0);
}
// Advance to the next point of the next outer edge:
bool advanceOuter = generateTriOuter || generateQuad;
if (advanceOuter) {
tOuter0 = tOuter1;
cOuter0 = cOuter1;
tOuter1 += dtOuter;
cOuter1 = cOuter1 + 1;
if (tOuter1 >= tOuterLast) {
tOuter1 = tOuterLast;
cOuter1 = outerLast;
}
}
// Advance to the next point of the next inner edge:
bool advanceInner = generateTriInner || generateQuad;
if (advanceInner) {
tInner0 = tInner1;
cInner0 = cInner1;
tInner1 += dtInner;
cInner1 = cInner1 + dInner;
if (tInner1 >= tInnerLast) {
tInner1 = tInnerLast;
cInner1 = innerLast;
}
}
}
return nFacetsExpected;
}
}
//
// Utility functions to help assembly of tessellation patterns -- grouped
// into local structs/namespaces for each of the supported parameterization
// types: quad, triangle (tri) or quadrangulated sub-faces (qsub):
//
// Given the similar structure to these -- the construction of patterns
// using concentric rings of Coords, rings of Facets between successive
// concentric rings, etc. -- there are some opportunities for refactoring
// some of these. (But there are typically subtle differences between
// each that complicate doing so.)
//
class quad {
public:
// Public methods for counting coords and facets:
static int CountUniformFacets(int edgeRes, bool triangulate);
static int CountSegmentedFacets(int const uvRes[], bool triangulate);
static int CountNonUniformFacets(int const outerRes[], int const uvRes[],
bool triangulate);
static int CountInteriorCoords(int edgeRes);
static int CountInteriorCoords(int const uvRes[]);
// Public methods for identifying and assigning coordinate pairs:
template <typename REAL>
static int GetEdgeCoords(int edge, int edgeRes,
Coord2Array<REAL> coords);
template <typename REAL>
static int GetBoundaryCoords(int const edgeRates[],
Coord2Array<REAL> coords);
template <typename REAL>
static int GetInteriorCoords(int const uvRes[2],
Coord2Array<REAL> coords);
// Public methods for identifying and assigning facets:
static int GetUniformFacets(int edgeRes, bool triangulate,
FacetArray facets);
static int GetSegmentedFacets(int const uvRes[], bool triangulate,
FacetArray facets);
static int GetNonUniformFacets(int const outerRes[], int const innerRes[],
int nBoundaryEdges, bool triangulate,
FacetArray facets);
private:
// Private methods used by those above:
static int countUniformCoords(int edgeRes);
static int countNonUniformEdgeFacets(int outerRes, int innerRes);
template <typename REAL>
static int getCenterCoord(Coord2Array<REAL> coords);
template <typename REAL>
static int getInteriorRingCoords(int uRes, int vRes,
REAL uStart, REAL vStart,
REAL uDelta, REAL vDelta,
Coord2Array<REAL> coords);
static int getInteriorRingFacets(int uRes, int vRes,
int indexOfFirstCoord, bool triangulate,
FacetArray facets);
static int getBoundaryRingFacets(int const outerRes[], int uRes, int vRes,
int nBoundaryEdges, bool triangulate,
FacetArray facets);
static int getSingleStripFacets(int uRes, int vRes,
int indexOfFirstCoord, bool triangulate,
FacetArray facets);
};
class tri {
public:
// Public methods for counting coords and facets:
static int CountUniformFacets(int edgeRes);
static int CountNonUniformFacets(int const outerRes[], int innerRes);
static int CountInteriorCoords(int edgeRes);
// Public methods for identifying and assigning coordinate pairs:
template <typename REAL>
static int GetEdgeCoords(int edge, int edgeRes,
Coord2Array<REAL> coords);
template <typename REAL>
static int GetBoundaryCoords(int const edgeRates[],
Coord2Array<REAL> coords);
template <typename REAL>
static int GetInteriorCoords(int edgeRes,
Coord2Array<REAL> coords);
// Public methods for identifying and assigning facets:
static int GetUniformFacets(int edgeRes,
FacetArray facets);
static int GetNonUniformFacets(int const outerRes[], int innerRes,
int nBoundaryEdges,
FacetArray facets);
private:
// Private methods used by those above:
static int countUniformCoords(int edgeRes);
template <typename REAL>
static int getCenterCoord(Coord2Array<REAL> coords);
template <typename REAL>
static int getInteriorRingCoords(int edgeRes,
REAL uStart, REAL vStart, REAL tDelta,
Coord2Array<REAL> coords);
static int getInteriorRingFacets(int edgeRes, int indexOfFirstCoord,
FacetArray facets);
static int getBoundaryRingFacets(int const outerRes[], int innerRes,
int nBoundaryEdges,
FacetArray facets);
};
class qsub {
public:
// Public methods for counting coords and facets:
static int CountUniformFacets(int N, int edgeRes, bool triangulate);
static int CountNonUniformFacets(int N, int const outerRes[], int innerRes,
bool triangulate);
static int CountInteriorCoords(int N, int edgeRes);
// Public methods for identifying and assigning coordinate pairs (note
// these require a Parameterization while others only need face size N)
template <typename REAL>
static int GetEdgeCoords(Parameterization P, int edge, int edgeRes,
Coord2Array<REAL> coords);
template <typename REAL>
static int GetBoundaryCoords(Parameterization P, int const edgeRates[],
Coord2Array<REAL> coords);
template <typename REAL>
static int GetInteriorCoords(Parameterization P, int edgeRes,
Coord2Array<REAL> coords);
// Public methods for identifying and assigning facets:
static int GetUniformFacets(int N, int edgeRes, bool triangulate,
FacetArray facets);
static int GetNonUniformFacets(int N, int const outerRes[], int innerRes,
int nBoundaryEdges, bool triangulate,
FacetArray facets);
private:
// Private methods used by those above:
static int countUniformCoords(int N, int edgeRes);
template <typename REAL>
static int getCenterCoord(Coord2Array<REAL> coords);
template <typename REAL>
static int getCenterRingCoords(Parameterization P, REAL tStart,
Coord2Array<REAL> coords);
template <typename REAL>
static int getRingEdgeCoords(Parameterization P, int edge, int edgeRes,
bool incFirst, bool incLast,
REAL tStart, REAL tDelta,
Coord2Array<REAL> coords);
template <typename REAL>
static int getInteriorRingCoords(Parameterization P, int edgeRes,
REAL tStart, REAL tDelta,
Coord2Array<REAL> coords);
static int getCenterFacets(int N, int indexOfFirstCoord,
FacetArray facets);
static int getInteriorRingFacets(int N, int edgeRes,
int indexOfFirstCoord, bool triangulate,
FacetArray facets);
static int getBoundaryRingFacets(int N, int const outerRes[], int innerRes,
int nBoundaryEdges, bool triangulate,
FacetArray facets);
};
//
// Implementations for quad functions:
//
inline int
quad::CountUniformFacets(int edgeRes, bool triangulate) {
return (edgeRes * edgeRes) << (int) triangulate;
}
inline int
quad::CountSegmentedFacets(int const uvRes[], bool triangulate) {
// WIP - may extend later to handle different opposing outer rates
assert((uvRes[0] == 1) || (uvRes[1] == 1));
return (uvRes[0] * uvRes[1]) << (int) triangulate;
}
int
quad::countNonUniformEdgeFacets(int outerRes, int innerRes) {
int nFacets = std::max(outerRes, (innerRes - 2));
// If the lesser is odd, a triangle will be added in the middle:
if ((nFacets & 1) == 0) {
nFacets += (outerRes & 1) || (innerRes & 1);
}
return nFacets;
}
int
quad::CountNonUniformFacets(int const outerRes[], int const innerRes[],
bool triangulate) {
int uRes = innerRes[0];
int vRes = innerRes[1];
assert((uRes > 1) && (vRes > 1));
// Count interior facets based on edges of inner ring:
int innerUEdges = uRes - 2;
int innerVEdges = vRes - 2;
int nInterior = innerUEdges * innerVEdges;
// If triangulating, things are much simpler:
if (triangulate) {
int nFacets = nInterior * 2;
nFacets += innerUEdges + outerRes[0];
nFacets += innerVEdges + outerRes[1];
nFacets += innerUEdges + outerRes[2];
nFacets += innerVEdges + outerRes[3];
return nFacets;
}
//
// Accumulate boundary facets for each edge based on uniformity...
//
// A uniform edge contributes a quad for each inner edge, plus one
// facet for the leading corner (quad if uniform, tri if not) and a
// tri for the trailing corner if it is not uniform. A non-uniform
// edge contributes quads and tris based on the larger of the inner
// and outer resolutions.
//
bool uniformEdges[4];
uniformEdges[0] = (outerRes[0] == uRes);
uniformEdges[1] = (outerRes[1] == vRes);
uniformEdges[2] = (outerRes[2] == uRes);
uniformEdges[3] = (outerRes[3] == vRes);
bool uniformCorners[4];
uniformCorners[0] = (uniformEdges[0] && uniformEdges[3]);
uniformCorners[1] = (uniformEdges[1] && uniformEdges[0]);
uniformCorners[2] = (uniformEdges[2] && uniformEdges[1]);
uniformCorners[3] = (uniformEdges[3] && uniformEdges[2]);
int nBoundary = 0;
nBoundary += uniformEdges[0] ? (innerUEdges + 1 + !uniformCorners[1]) :
countNonUniformEdgeFacets(outerRes[0], uRes);
nBoundary += uniformEdges[1] ? (innerVEdges + 1 + !uniformCorners[2]) :
countNonUniformEdgeFacets(outerRes[1], vRes);
nBoundary += uniformEdges[2] ? (innerUEdges + 1 + !uniformCorners[3]) :
countNonUniformEdgeFacets(outerRes[2], uRes);
nBoundary += uniformEdges[3] ? (innerVEdges + 1 + !uniformCorners[0]) :
countNonUniformEdgeFacets(outerRes[3], vRes);
return nInterior + nBoundary;
}
inline int
quad::countUniformCoords(int edgeRes) {
return (edgeRes + 1) * (edgeRes + 1);
}
inline int
quad::CountInteriorCoords(int edgeRes) {
return (edgeRes - 1) * (edgeRes - 1);
}
inline int
quad::CountInteriorCoords(int const uvRes[]) {
return (uvRes[0] - 1) * (uvRes[1] - 1);
}
template <typename REAL>
inline int
quad::getCenterCoord(Coord2Array<REAL> coords) {
coords[0].Set(0.5f, 0.5f);
return 1;
}
template <typename REAL>
int
quad::GetEdgeCoords(int edge, int edgeRes, Coord2Array<REAL> coords) {
REAL dt = 1.0f / (REAL)edgeRes;
REAL t0 = dt;
REAL t1 = 1.0f - dt;
int n = edgeRes - 1;
switch (edge) {
case 0: return appendVIsoLine<REAL>(coords, n, t0, 0.0f, dt);
case 1: return appendUIsoLine<REAL>(coords, n, 1.0f, t0, dt);
case 2: return appendVIsoLine<REAL>(coords, n, t1, 1.0f, -dt);
case 3: return appendUIsoLine<REAL>(coords, n, 0.0f, t1, -dt);
}
return 0;
}
template <typename REAL>
int
quad::GetBoundaryCoords(int const edgeRates[], Coord2Array<REAL> coords) {
int nCoords = 0;
nCoords += appendVIsoLine<REAL>(coords + nCoords, edgeRates[0], 0.0f, 0.0f,
1.0f / (REAL) edgeRates[0]);
nCoords += appendUIsoLine<REAL>(coords + nCoords, edgeRates[1], 1.0f, 0.0f,
1.0f / (REAL) edgeRates[1]);
nCoords += appendVIsoLine<REAL>(coords + nCoords, edgeRates[2], 1.0f, 1.0f,
-1.0f / (REAL) edgeRates[2]);
nCoords += appendUIsoLine<REAL>(coords + nCoords, edgeRates[3], 0.0f, 1.0f,
-1.0f / (REAL) edgeRates[3]);
return nCoords;
}
template <typename REAL>
int
quad::getInteriorRingCoords(int uRes, int vRes,
REAL u0, REAL v0, REAL du, REAL dv,
Coord2Array<REAL> coords) {
int nCoords = 0;
if ((uRes > 0) && (vRes > 0)) {
REAL u1 = 1.0f - u0;
REAL v1 = 1.0f - v0;
nCoords += appendVIsoLine(coords + nCoords, uRes, u0, v0, du);
nCoords += appendUIsoLine(coords + nCoords, vRes, u1, v0, dv);
nCoords += appendVIsoLine(coords + nCoords, uRes, u1, v1,-du);
nCoords += appendUIsoLine(coords + nCoords, vRes, u0, v1,-dv);
} else if (uRes > 0) {
nCoords += appendVIsoLine(coords, uRes+1, u0, v0, du);
} else if (vRes > 0) {
nCoords += appendUIsoLine(coords, vRes+1, u0, v0, dv);
} else {
return getCenterCoord(coords);
}
return nCoords;
}
template <typename REAL>
int
quad::GetInteriorCoords(int const uvRes[2], Coord2Array<REAL> coords) {
int nIntRings = std::min((uvRes[0] / 2), (uvRes[1] / 2));
if (nIntRings == 0) return 0;
REAL du = 1.0f / (REAL) uvRes[0];
REAL dv = 1.0f / (REAL) uvRes[1];
REAL u = du;
REAL v = dv;
int uRes = uvRes[0] - 2;
int vRes = uvRes[1] - 2;
//
// Note that with separate U and V res, one can go negative so beware
// of making any assumptions -- defer to the function for the ring:
//
int nCoords = 0;
for (int i=0; i < nIntRings; ++i, uRes -= 2, vRes -= 2, u += du, v += dv) {
nCoords += getInteriorRingCoords(uRes, vRes, u, v, du, dv,
coords + nCoords);
}
return nCoords;
}
int
quad::getSingleStripFacets(int uRes, int vRes, int coord0, bool triangulate,
FacetArray facets) {
assert((uRes == 1) || (vRes == 1));
FacetStrip qStrip;
qStrip.quadTopology = true;
qStrip.quadTriangulate = triangulate;
qStrip.splitFirstFace = false;
qStrip.splitLastFace = false;
qStrip.innerReversed = true;
qStrip.includeLastFace = true;
if (uRes > 1) {
qStrip.outerEdges = uRes;
qStrip.innerEdges = uRes - 2;
// Assign these successively around the strip:
qStrip.outerFirst = coord0;
qStrip.outerLast = qStrip.outerFirst + uRes;
qStrip.innerLast = qStrip.outerLast + 2;
qStrip.innerFirst = qStrip.outerLast + uRes;
qStrip.outerPrev = qStrip.innerFirst + 1;
return qStrip.connectUniformQuads(facets);
} else {
qStrip.outerEdges = vRes;
qStrip.innerEdges = vRes - 2;
qStrip.outerPrev = coord0;
qStrip.outerFirst = coord0 + 1;
qStrip.outerLast = qStrip.outerFirst + vRes;
qStrip.innerLast = qStrip.outerLast + 2;
qStrip.innerFirst = qStrip.outerLast + vRes;
return qStrip.connectUniformQuads(facets);
}
}
int
quad::getInteriorRingFacets(int uRes, int vRes, int coord0, bool triangulate,
FacetArray facets) {
assert((uRes >= 0) && (vRes >= 0));
//
// Deal with some simple and special cases first:
//
int totalInnerFacets = uRes * vRes;
if (totalInnerFacets == 0) return 0;
if (totalInnerFacets == 1) {
return appendQuad(facets, coord0, coord0+1, coord0+2, coord0+3,
triangulate);
}
// The single interior strip is enclosed by a single ring:
if ((uRes == 1) || (vRes == 1)) {
return getSingleStripFacets(uRes, vRes, coord0, triangulate, facets);
}
//
// The general case -- one or more quads for each edge that are
// connected to the next interior ring of vertices:
//
int nFacets = 0;
int uResInner = uRes - 2;
int vResInner = vRes - 2;
int outerRingStart = coord0;
int innerRingStart = coord0 + 2 * (uRes + vRes);
FacetStrip qStrip;
qStrip.quadTopology = true;
qStrip.quadTriangulate = triangulate;
qStrip.splitFirstFace = false;
qStrip.splitLastFace = false;
qStrip.outerEdges = uRes;
qStrip.outerFirst = outerRingStart;
qStrip.outerPrev = innerRingStart - 1;
qStrip.outerLast = outerRingStart + uRes;
qStrip.innerEdges = uResInner;
qStrip.innerReversed = false;
qStrip.innerFirst = innerRingStart;
qStrip.innerLast = innerRingStart + uResInner;
nFacets += qStrip.connectUniformQuads(facets + nFacets);
qStrip.outerEdges = vRes;
qStrip.outerFirst += uRes;
qStrip.outerPrev = qStrip.outerFirst - 1;
qStrip.outerLast = qStrip.outerFirst + vRes;
qStrip.innerEdges = vResInner;
qStrip.innerReversed = false;
qStrip.innerFirst = qStrip.innerLast;
qStrip.innerLast += vResInner;
nFacets += qStrip.connectUniformQuads(facets + nFacets);
qStrip.outerEdges = uRes;
qStrip.outerFirst += vRes;
qStrip.outerPrev = qStrip.outerFirst - 1;
qStrip.outerLast = qStrip.outerFirst + uRes;
qStrip.innerEdges = uResInner;
qStrip.innerReversed = (vResInner == 0);
qStrip.innerFirst = qStrip.innerLast;
qStrip.innerLast += uResInner * (qStrip.innerReversed ? -1 : 1);
nFacets += qStrip.connectUniformQuads(facets + nFacets);
qStrip.outerEdges = vRes;
qStrip.outerFirst += uRes;
qStrip.outerPrev = qStrip.outerFirst - 1;
qStrip.outerLast = outerRingStart;
qStrip.innerEdges = vResInner;
qStrip.innerReversed = (uResInner == 0);
qStrip.innerFirst = qStrip.innerLast;
qStrip.innerLast = innerRingStart;
nFacets += qStrip.connectUniformQuads(facets + nFacets);
return nFacets;
}
int
quad::getBoundaryRingFacets(int const outerRes[], int uRes, int vRes,
int nBoundaryEdges, bool triangulate,
FacetArray facets) {
// Identify edges and corners that should preserve uniform behavior:
bool uniformEdges[4];
uniformEdges[0] = (outerRes[0] == uRes);
uniformEdges[1] = (outerRes[1] == vRes);
uniformEdges[2] = (outerRes[2] == uRes);
uniformEdges[3] = (outerRes[3] == vRes);
bool uniformCorners[4];
uniformCorners[0] = (uniformEdges[0] && uniformEdges[3]);
uniformCorners[1] = (uniformEdges[1] && uniformEdges[0]);
uniformCorners[2] = (uniformEdges[2] && uniformEdges[1]);
uniformCorners[3] = (uniformEdges[3] && uniformEdges[2]);
// Initialize inner edge counts and the FacetStrip for local use:
assert((uRes > 1) && (vRes > 1));
int innerResU = uRes - 2;
int innerResV = vRes - 2;
int nFacets = 0;
int outerRingStart = 0;
int innerRingStart = nBoundaryEdges;
FacetStrip qStrip;
qStrip.quadTopology = true;
qStrip.quadTriangulate = triangulate;
// Assign strip indices for the inner and outer rings:
qStrip.outerEdges = outerRes[0];
qStrip.outerFirst = outerRingStart;
qStrip.outerPrev = innerRingStart - 1;
qStrip.outerLast = outerRingStart + outerRes[0];
qStrip.innerEdges = innerResU;
qStrip.innerReversed = false;
qStrip.innerFirst = innerRingStart;
qStrip.innerLast = innerRingStart + innerResU;
if (uniformEdges[0]) {
qStrip.splitFirstFace = !uniformCorners[0];
qStrip.splitLastFace = !uniformCorners[1];
nFacets += qStrip.connectUniformQuads(facets + nFacets);
} else {
qStrip.splitFirstFace = true;
qStrip.splitLastFace = true;
nFacets += qStrip.connectNonUniformFacets(facets + nFacets);
}
qStrip.outerEdges = outerRes[1];
qStrip.outerFirst = qStrip.outerLast;
qStrip.outerPrev = qStrip.outerFirst - 1;
qStrip.outerLast += outerRes[1];
qStrip.innerEdges = innerResV;
qStrip.innerReversed = false;
qStrip.innerFirst = qStrip.innerLast;
qStrip.innerLast += innerResV;
if (uniformEdges[1]) {
qStrip.splitFirstFace = !uniformCorners[1];
qStrip.splitLastFace = !uniformCorners[2];
nFacets += qStrip.connectUniformQuads(facets + nFacets);
} else {
qStrip.splitFirstFace = true;
qStrip.splitLastFace = true;
nFacets += qStrip.connectNonUniformFacets(facets + nFacets);
}
qStrip.outerEdges = outerRes[2];
qStrip.outerFirst = qStrip.outerLast;
qStrip.outerPrev = qStrip.outerFirst - 1;
qStrip.outerLast += outerRes[2];
qStrip.innerEdges = innerResU;
qStrip.innerReversed = (innerResV == 0);
qStrip.innerFirst = qStrip.innerLast;
qStrip.innerLast += innerResU * (qStrip.innerReversed ? -1 : 1);
if (uniformEdges[2]) {
qStrip.splitFirstFace = !uniformCorners[2];
qStrip.splitLastFace = !uniformCorners[3];
nFacets += qStrip.connectUniformQuads(facets + nFacets);
} else {
qStrip.splitFirstFace = true;
qStrip.splitLastFace = true;
nFacets += qStrip.connectNonUniformFacets(facets + nFacets);
}
qStrip.outerEdges = outerRes[3];
qStrip.outerFirst = qStrip.outerLast;
qStrip.outerPrev = qStrip.outerFirst - 1;
qStrip.outerLast = 0;
qStrip.innerEdges = innerResV;
qStrip.innerReversed = (innerResU == 0);
qStrip.innerFirst = qStrip.innerLast;
qStrip.innerLast = innerRingStart;
if (uniformEdges[3]) {
qStrip.splitFirstFace = !uniformCorners[3];
qStrip.splitLastFace = !uniformCorners[0];
nFacets += qStrip.connectUniformQuads(facets + nFacets);
} else {
qStrip.splitFirstFace = true;
qStrip.splitLastFace = true;
nFacets += qStrip.connectNonUniformFacets(facets + nFacets);
}
return nFacets;
}
int
quad::GetSegmentedFacets(int const innerRes[], bool triangulate,
FacetArray facets) {
// WIP - may extend later to handle different opposing outer rates
// resulting in a non-uniform strip between the opposing edges
int uRes = innerRes[0];
int vRes = innerRes[1];
assert((uRes == 1) || (vRes == 1));
return getSingleStripFacets(uRes, vRes, 0, triangulate, facets);
}
int
quad::GetNonUniformFacets(int const outerRes[], int const innerRes[],
int nBoundaryEdges, bool triangulate,
FacetArray facets) {
int uRes = innerRes[0];
int vRes = innerRes[1];
assert((uRes > 1) && (vRes > 1));
// First, generate the ring of boundary facets separately:
int nFacets = getBoundaryRingFacets(outerRes, uRes, vRes, nBoundaryEdges,
triangulate, facets);
// Second, generate the remaining rings of interior facets:
int nRings = (std::min(uRes,vRes) + 1) / 2;
int coord0 = nBoundaryEdges;
for (int ring = 1; ring < nRings; ++ring) {
uRes = std::max(uRes - 2, 0);
vRes = std::max(vRes - 2, 0);
nFacets += getInteriorRingFacets(uRes, vRes, coord0, triangulate,
facets + nFacets);
coord0 += 2 * (uRes + vRes);
}
return nFacets;
}
int
quad::GetUniformFacets(int res, bool triangulate, FacetArray facets) {
// The trivial case should have been handled by the caller:
assert(res > 1);
int nRings = (res + 1) / 2;
int nFacets = 0;
int coord0 = 0;
for (int ring = 0; ring < nRings; ++ring, res -= 2) {
nFacets += getInteriorRingFacets(res, res, coord0, triangulate,
facets + nFacets);
coord0 += 4 * res;
}
return nFacets;
}
//
// REMINDER TO SELF -- according to the OpenGL docs, the "inner" tess
// rates are expected to reflect a tessellation of the entire face, i.e.
// they are not the outer rates with 2 subtracted, but are the same as
// the outer rates. Their minimum is therefore 1 -- no inner vertices,
// BUT any non-unit outer rate will trigger an interior point.
//
// Note that triangles will need considerably different treatment in
// some cases given the way we diverge from the OpenGL patterns, e.g.
// the corner faces are not bisected in the uniform case but may need
// to be when non-uniform.
//
//
// Implementations for tri functions:
//
inline int
tri::CountUniformFacets(int edgeRes) {
return edgeRes * edgeRes;
}
int
tri::CountNonUniformFacets(int const outerRes[], int innerRes) {
assert(innerRes > 2);
// Count interior facets based on edges of inner ring:
int nInnerEdges = innerRes - 3;
int nInterior = nInnerEdges ? CountUniformFacets(nInnerEdges) : 0;
//
// Note the number of boundary facets is not affected by the uniform
// behavior at corners when rates match -- in contrast to quads. In
// both cases, two tris are generated from four points at the corner,
// just with a different edge bisecting that "quad".
//
int nBoundary = (nInnerEdges + outerRes[0]) +
(nInnerEdges + outerRes[1]) +
(nInnerEdges + outerRes[2]);
return nInterior + nBoundary;
}
inline int
tri::countUniformCoords(int edgeRes) {
return edgeRes * (edgeRes + 1) / 2;
}
inline int
tri::CountInteriorCoords(int edgeRes) {
return countUniformCoords(edgeRes - 2);
}
template <typename REAL>
inline int
tri::getCenterCoord(Coord2Array<REAL> coords) {
coords[0].Set(1.0f/3.0f, 1.0f/3.0f);
return 1;
}
template <typename REAL>
int
tri::GetEdgeCoords(int edge, int edgeRes, Coord2Array<REAL> coords) {
REAL dt = 1.0f / (REAL)edgeRes;
REAL t0 = dt;
REAL t1 = 1.0f - dt;
int n = edgeRes - 1;
switch (edge) {
case 0: return appendVIsoLine<REAL>(coords, n, t0, 0.0f, dt);
case 1: return appendUVLine<REAL>( coords, n, t1, t0, -dt, dt);
case 2: return appendUIsoLine<REAL>(coords, n, 0.0f, t1, -dt);
}
return 0;
}
template <typename REAL>
int
tri::GetBoundaryCoords(int const edgeRates[], Coord2Array<REAL> coords) {
int nCoords = 0;
nCoords += appendVIsoLine<REAL>(coords + nCoords, edgeRates[0], 0.0f, 0.0f,
1.0f / (REAL) edgeRates[0]);
nCoords += appendUVLine<REAL>( coords + nCoords, edgeRates[1], 1.0f, 0.0f,
-1.0f / (REAL) edgeRates[1],
1.0f / (REAL) edgeRates[1]);
nCoords += appendUIsoLine<REAL>(coords + nCoords, edgeRates[2], 0.0f, 1.0f,
-1.0f / (REAL) edgeRates[2]);
return nCoords;
}
template <typename REAL>
int
tri::getInteriorRingCoords(int edgeRes, REAL u0, REAL v0, REAL dt,
Coord2Array<REAL> coords) {
assert(edgeRes);
REAL u1 = 1.0f - u0*2.0f;
REAL v1 = 1.0f - v0*2.0f;
int nCoords = 0;
nCoords += appendVIsoLine(coords + nCoords, edgeRes, u0, v0, dt);
nCoords += appendUVLine( coords + nCoords, edgeRes, u1, v0,-dt, dt);
nCoords += appendUIsoLine(coords + nCoords, edgeRes, u0, v1,-dt);
return nCoords;
}
template <typename REAL>
int
tri::GetInteriorCoords(int edgeRes, Coord2Array<REAL> coords) {
int nIntRings = edgeRes / 3;
if (nIntRings == 0) return 0;
REAL dt = 1.0f / (REAL) edgeRes;
REAL u = dt;
REAL v = dt;
int ringRes = edgeRes - 3;
int nCoords = 0;
for (int i = 0; i < nIntRings; ++i, ringRes -= 3, u += dt, v += dt) {
if (ringRes == 0) {
nCoords += getCenterCoord(coords + nCoords);
} else {
nCoords += getInteriorRingCoords(ringRes, u, v, dt,
coords + nCoords);
}
}
return nCoords;
}
int
tri::getInteriorRingFacets(int edgeRes, int coord0, FacetArray facets) {
//
// Deal with trivial cases with no inner vertices:
//
if (edgeRes < 1) {
return 0;
} else if (edgeRes == 1) {
return appendTri(facets, coord0, coord0+1, coord0+2);
} else if (edgeRes == 2) {
appendTri(facets + 0, coord0+0, coord0+1, coord0+5);
appendTri(facets + 1, coord0+2, coord0+3, coord0+1);
appendTri(facets + 2, coord0+4, coord0+5, coord0+3);
appendTri(facets + 3, coord0+1, coord0+3, coord0+5);
return 4;
}
//
// Generate facets for the 3 tri-strips for each edge:
//
int nFacets = 0;
int outerEdges = edgeRes;
int innerEdges = edgeRes - 3;
int outerRingStart = coord0;
int innerRingStart = coord0 + 3 * outerEdges;
FacetStrip tStrip;
tStrip.quadTopology = false;
tStrip.innerReversed = false;
tStrip.innerEdges = innerEdges;
tStrip.outerEdges = outerEdges;
tStrip.outerFirst = outerRingStart;
tStrip.outerLast = outerRingStart + outerEdges;
tStrip.outerPrev = innerRingStart - 1;
tStrip.innerFirst = innerRingStart;
tStrip.innerLast = innerRingStart + innerEdges;
nFacets += tStrip.connectUniformTris(facets + nFacets);
tStrip.outerFirst += outerEdges;
tStrip.outerLast += outerEdges;
tStrip.outerPrev = tStrip.outerFirst - 1;
tStrip.innerFirst += innerEdges;
tStrip.innerLast += innerEdges;
nFacets += tStrip.connectUniformTris(facets + nFacets);
tStrip.outerFirst += outerEdges;
tStrip.outerLast = outerRingStart;
tStrip.outerPrev = tStrip.outerFirst - 1;
tStrip.innerFirst += innerEdges;
tStrip.innerLast = innerRingStart;
nFacets += tStrip.connectUniformTris(facets + nFacets);
return nFacets;
}
int
tri::getBoundaryRingFacets(int const outerRes[], int innerRes,
int nBoundaryEdges, FacetArray facets) {
// Identify edges and corners that should preserve uniform behavior:
bool uniformEdges[3];
uniformEdges[0] = (outerRes[0] == innerRes);
uniformEdges[1] = (outerRes[1] == innerRes);
uniformEdges[2] = (outerRes[2] == innerRes);
bool uniformCorners[3];
uniformCorners[0] = (uniformEdges[0] && uniformEdges[2]);
uniformCorners[1] = (uniformEdges[1] && uniformEdges[0]);
uniformCorners[2] = (uniformEdges[2] && uniformEdges[1]);
// Initialize inner edge count and the FacetStrip for local use:
assert(innerRes > 2);
int innerEdges = innerRes - 3;
int nFacets = 0;
int outerRingStart = 0;
int innerRingStart = nBoundaryEdges;
FacetStrip tStrip;
tStrip.quadTopology = false;
tStrip.innerReversed = false;
tStrip.innerEdges = innerEdges;
// Assign the three strips of Facets:
tStrip.outerEdges = outerRes[0];
tStrip.outerFirst = outerRingStart;
tStrip.outerLast = outerRingStart + outerRes[0];
tStrip.outerPrev = innerRingStart - 1;
tStrip.innerFirst = innerRingStart;
tStrip.innerLast = innerRingStart + innerEdges;
if (uniformEdges[0]) {
tStrip.splitFirstFace = !uniformCorners[0];
tStrip.splitLastFace = !uniformCorners[1];
nFacets += tStrip.connectUniformTris(facets + nFacets);
} else {
tStrip.splitFirstFace = true;
tStrip.splitLastFace = true;
nFacets += tStrip.connectNonUniformFacets(facets + nFacets);
}
tStrip.outerEdges = outerRes[1];
tStrip.outerFirst = tStrip.outerLast;
tStrip.outerLast += outerRes[1];
tStrip.outerPrev = tStrip.outerFirst - 1;
tStrip.innerFirst = tStrip.innerLast;
tStrip.innerLast += innerEdges;
if (uniformEdges[1]) {
tStrip.splitFirstFace = !uniformCorners[1];
tStrip.splitLastFace = !uniformCorners[2];
nFacets += tStrip.connectUniformTris(facets + nFacets);
} else {
tStrip.splitFirstFace = true;
tStrip.splitLastFace = true;
nFacets += tStrip.connectNonUniformFacets(facets + nFacets);
}
tStrip.outerEdges = outerRes[2];
tStrip.outerFirst = tStrip.outerLast;
tStrip.outerLast = 0;
tStrip.outerPrev = tStrip.outerFirst - 1;
tStrip.innerFirst = tStrip.innerLast;
tStrip.innerLast = innerRingStart;
if (uniformEdges[2]) {
tStrip.splitFirstFace = !uniformCorners[2];
tStrip.splitLastFace = !uniformCorners[0];
nFacets += tStrip.connectUniformTris(facets + nFacets);
} else {
tStrip.splitFirstFace = true;
tStrip.splitLastFace = true;
nFacets += tStrip.connectNonUniformFacets(facets + nFacets);
}
return nFacets;
}
int
tri::GetUniformFacets(int edgeRes, FacetArray facets) {
// The trivial case should have been handled by the caller:
assert(edgeRes > 1);
int nRings = 1 + (edgeRes / 3);
int nFacets = 0;
int coord0 = 0;
for (int ring = 0; ring < nRings; ++ring, edgeRes -= 3) {
nFacets += getInteriorRingFacets(edgeRes, coord0, facets + nFacets);
coord0 += 3 * edgeRes;
}
return nFacets;
}
int
tri::GetNonUniformFacets(int const outerRes[], int innerRes,
int nBoundaryEdges, FacetArray facets) {
assert(innerRes > 2);
// First, generate the ring of boundary facets separately:
int nFacets = getBoundaryRingFacets(outerRes, innerRes,
nBoundaryEdges, facets);
// Second, generate the remaining rings of interior facets:
int nRings = 1 + (innerRes / 3);
int coord0 = nBoundaryEdges;
for (int ring = 1; ring < nRings; ++ring) {
innerRes -= 3;
nFacets += getInteriorRingFacets(innerRes,
coord0, facets + nFacets);
coord0 += 3 * innerRes;
}
return nFacets;
}
//
// These utilities support quadrangulated polygons used for quad-based
// subdivision schemes.
//
//
// The formulae to enumerate points and facets for a uniform tessellation
// reflect the differing topologies for the odd and even case:
//
inline int
qsub::CountUniformFacets(int N, int edgeRes, bool triangulate) {
bool resIsOdd = (edgeRes & 1);
int H = edgeRes / 2;
int nQuads = (H + resIsOdd) * H * N;
int nCenter = resIsOdd ? ((N == 3) ? 1 : N) : 0;
return (nQuads << (int)triangulate) + nCenter;
}
int
qsub::CountNonUniformFacets(int N, int const outerRes[], int innerRes,
bool triangulate) {
assert(innerRes > 1);
// Count interior facets based on edges of inner ring:
int nInnerEdges = innerRes - 2;
int nInterior = 0;
if (nInnerEdges) {
nInterior = CountUniformFacets(N, nInnerEdges, triangulate);
}
//
// Accumulate boundary facets for uniform vs non-uniform edge. Uniform
// has a quad for each inner edge, plus one facet for leading corner
// and a tri for the trailing corner if not uniform. Non-uniform has
// a tri for each inner edge and each outer edge:
//
int nBoundary = 0;
for (int i = 0; i < N; ++i) {
if (triangulate) {
nBoundary += nInnerEdges + outerRes[i];
} else if (outerRes[i] == innerRes) {
nBoundary += nInnerEdges + 1 + (innerRes != outerRes[(i+1) % N]);
} else {
int nEdge = std::max(nInnerEdges, outerRes[i]);
if ((nEdge & 1) == 0) {
nEdge += (nInnerEdges & 1) || (outerRes[i] & 1);
}
nBoundary += nEdge;
}
}
return nInterior + nBoundary;
}
inline int
qsub::countUniformCoords(int N, int edgeRes) {
int H = edgeRes / 2;
return (edgeRes & 1) ? (H+1)* (H+1) * N + ((N == 3) ? 0 : 1)
: H * (H+1) * N + 1;
}
inline int
qsub::CountInteriorCoords(int N, int edgeRes) {
assert(edgeRes > 1);
return countUniformCoords(N, edgeRes - 2);
}
template <typename REAL>
inline int
qsub::getCenterCoord(Coord2Array<REAL> coords) {
coords[0].Set(0.5f, 0.5f);
return 1;
}
template <typename REAL>
int
qsub::getRingEdgeCoords(Parameterization P, int edge, int edgeRes,
bool incFirst, bool incLast,
REAL tOrigin, REAL dt,
Coord2Array<REAL> coords) {
//
// Determine number of coords in each half, excluding the ends. The
// second half will get the extra when odd so that the sequence starts
// exactly on the boundary of the second sub-face (avoiding floating
// point error when accumulating to the boundary of the first):
//
int n0 = (edgeRes - 1) / 2;
int n1 = (edgeRes - 1) - n0;
int nCoords = 0;
if (incFirst || n0) {
REAL uv0[2];
P.GetVertexCoord(edge, uv0);
// u ranges from [tOrigin < 0.5] while v is constant
if (incFirst) {
coords[nCoords++].Set(uv0[0] + tOrigin, uv0[1] + tOrigin);
}
if (n0) {
REAL u = uv0[0] + tOrigin + dt;
REAL v = uv0[1] + tOrigin;
nCoords += appendVIsoLine(coords + nCoords, n0, u, v, dt);
}
}
if (n1 || incLast) {
REAL uv1[2];
P.GetVertexCoord((edge + 1) % P.GetFaceSize(), uv1);
// u is constant while v ranges from [0.5 > tOrigin] (even)
if (n1) {
REAL u = uv1[0] + tOrigin;
REAL v = uv1[1] + ((edgeRes & 1) ? (0.5f - 0.5f * dt) : 0.5f);
nCoords += appendUIsoLine(coords + nCoords, n1, u, v, -dt);
}
if (incLast) {
coords[nCoords++].Set(uv1[0] + tOrigin, uv1[1] + tOrigin);
}
}
return nCoords;
}
template <typename REAL>
int
qsub::GetEdgeCoords(Parameterization P, int edge, int edgeRes,
Coord2Array<REAL> coords) {
return getRingEdgeCoords<REAL>(P, edge, edgeRes, false, false,
0.0f, 1.0f / (REAL)edgeRes,
coords);
}
template <typename REAL>
int
qsub::GetBoundaryCoords(Parameterization P, int const edgeRates[],
Coord2Array<REAL> coords) {
int N = P.GetFaceSize();
int nCoords = 0;
for (int i = 0; i < N; ++i) {
nCoords += getRingEdgeCoords<REAL>(P, i, edgeRates[i], true, false,
0.0f, 1.0f / (REAL)edgeRates[i],
coords + nCoords);
}
return nCoords;
}
template <typename REAL>
int
qsub::getInteriorRingCoords(Parameterization P, int edgeRes,
REAL tOrigin, REAL dt,
Coord2Array<REAL> coords) {
assert(edgeRes > 1);
int N = P.GetFaceSize();
int nCoords = 0;
for (int i = 0; i < N; ++i) {
nCoords += getRingEdgeCoords(P, i, edgeRes, true, false,
tOrigin, dt,
coords + nCoords);
}
return nCoords;
}
template <typename REAL>
int
qsub::getCenterRingCoords(Parameterization P, REAL tOrigin,
Coord2Array<REAL> coords) {
int N = P.GetFaceSize();
// Just need the single corner point for each edge here:
for (int i = 0; i < N; ++i) {
REAL uv[2];
P.GetVertexCoord(i, uv);
coords[i].Set(uv[0] + tOrigin, uv[1] + tOrigin);
}
return (N == 3) ? N : (N + getCenterCoord(coords + N));
}
template <typename REAL>
int
qsub::GetInteriorCoords(Parameterization P, int edgeRes,
Coord2Array<REAL> coords) {
int nIntRings = edgeRes / 2;
if (nIntRings == 0) return 0;
REAL dt = 1.0f / (REAL) edgeRes;
REAL t = dt;
int ringRes = edgeRes - 2;
int nCoords = 0;
for (int i = 0; i < nIntRings; ++i, ringRes -= 2, t += dt) {
if (ringRes == 0) {
nCoords += getCenterCoord(coords + nCoords);
} else if (ringRes == 1) {
nCoords += getCenterRingCoords(P, t, coords + nCoords);
} else {
nCoords += getInteriorRingCoords(P, ringRes, t, dt,
coords + nCoords);
}
}
return nCoords;
}
int
qsub::getCenterFacets(int N, int coord0, FacetArray facets) {
return (N == 3) ? appendTri(facets, coord0, coord0+1, coord0+2)
: appendTriFan(facets, N, coord0);
}
int
qsub::getInteriorRingFacets(int N, int edgeRes, int coord0, bool triangulate,
FacetArray facets) {
//
// Deal with trivial cases with no inner vertices:
//
if (edgeRes < 1) return 0;
if (edgeRes == 1) {
return getCenterFacets(N, coord0, facets);
}
//
// Generate facets for the N quad-strips for each edge:
//
int outerRes = edgeRes;
int outerRing = coord0;
int innerRes = outerRes - 2;
int innerRing = outerRing + N * outerRes;
int nFacets = 0;
FacetStrip qStrip;
qStrip.quadTopology = true;
qStrip.quadTriangulate = triangulate;
qStrip.outerEdges = outerRes;
qStrip.innerEdges = innerRes;
qStrip.innerReversed = false;
qStrip.splitFirstFace = false;
qStrip.splitLastFace = false;
for (int edge = 0; edge < N; ++edge) {
qStrip.outerFirst = outerRing + edge * outerRes;
qStrip.innerFirst = innerRing + edge * innerRes;
qStrip.outerPrev = (edge ? qStrip.outerFirst : innerRing) - 1;
if (edge < N-1) {
qStrip.outerLast = qStrip.outerFirst + outerRes;
qStrip.innerLast = qStrip.innerFirst + innerRes;
} else {
qStrip.outerLast = outerRing;
qStrip.innerLast = innerRing;
}
nFacets += qStrip.connectUniformQuads(facets + nFacets);
}
return nFacets;
}
int
qsub::getBoundaryRingFacets(int N, int const outerRes[], int innerRes,
int nBoundaryEdges, bool triangulate,
FacetArray facets) {
int innerEdges = std::max(innerRes - 2, 0);
int nFacets = 0;
int outerRingStart = 0;
int innerRingStart = nBoundaryEdges;
// Initialize properties of the strip that are fixed:
FacetStrip qStrip;
qStrip.quadTopology = true;
qStrip.quadTriangulate = triangulate;
qStrip.innerReversed = false;
qStrip.innerEdges = innerEdges;
for (int edge = 0; edge < N; ++edge) {
qStrip.outerEdges = outerRes[edge];
// Initialize the indices starting this strip:
if (edge) {
qStrip.outerFirst = qStrip.outerLast;
qStrip.outerPrev = qStrip.outerFirst - 1;
qStrip.innerFirst = qStrip.innerLast;
} else {
qStrip.outerFirst = outerRingStart;
qStrip.outerPrev = innerRingStart - 1;
qStrip.innerFirst = innerRingStart;
}
// Initialize the indices ending this strip:
if (edge < N-1) {
qStrip.outerLast = qStrip.outerFirst + qStrip.outerEdges;
qStrip.innerLast = qStrip.innerFirst + qStrip.innerEdges;
} else {
qStrip.outerLast = outerRingStart;
qStrip.innerLast = innerRingStart;
}
// Test rates at, before and after this edge for uniform behavior:
if ((outerRes[edge] == innerRes) && (innerRes > 1)) {
qStrip.splitFirstFace = (outerRes[(edge-1+N) % N] != innerRes);
qStrip.splitLastFace = (outerRes[(edge + 1) % N] != innerRes);
nFacets += qStrip.connectUniformQuads(facets+nFacets);
} else {
qStrip.splitFirstFace = true;
qStrip.splitLastFace = true;
nFacets += qStrip.connectNonUniformFacets(facets + nFacets);
}
}
return nFacets;
}
int
qsub::GetUniformFacets(int N, int edgeRes, bool triangulate,
FacetArray facets) {
// The trivial (single facet) case should be handled externally:
if (edgeRes == 1) {
return getCenterFacets(N, 0, facets);
}
int nRings = (edgeRes + 1) / 2;
int nFacets = 0;
int coord0 = 0;
for (int ring = 0; ring < nRings; ++ring, edgeRes -= 2) {
nFacets += getInteriorRingFacets(N, edgeRes, coord0, triangulate,
facets + nFacets);
coord0 += N * edgeRes;
}
return nFacets;
}
int
qsub::GetNonUniformFacets(int N, int const outerRes[], int innerRes,
int nBoundaryEdges, bool triangulate,
FacetArray facets) {
// First, generate the ring of boundary facets separately:
int nFacets = getBoundaryRingFacets(N, outerRes, innerRes, nBoundaryEdges,
triangulate, facets);
// Second, generate the remaining rings of interior facets:
int nRings = (innerRes + 1) / 2;
int coord0 = nBoundaryEdges;
for (int ring = 1; ring < nRings; ++ring) {
innerRes = std::max(innerRes - 2, 0);
nFacets += getInteriorRingFacets(N, innerRes, coord0, triangulate,
facets + nFacets);
coord0 += N * innerRes;
}
return nFacets;
}
//
// Internal initialization methods:
//
void
Tessellation::initializeDefaults() {
std::memset((void*) this, 0, sizeof(*this));
// Assign any non-zero defaults:
_triangulate = true;
_isValid = false;
}
bool
Tessellation::validateArguments(Parameterization const & p,
int numRates, int const rates[], Options const & options) {
// Check the Parameterization:
if (!p.IsValid()) return false;
// Check given tessellation rates:
if (numRates < 1) return false;
for (int i = 0; i < numRates; ++i) {
if (rates[i] < 1) return false;
}
// Check given buffer strides in Options:
int coordStride = options.GetCoordStride();
if (coordStride && (coordStride < 2)) return false;
int facetStride = options.GetFacetStride();
if (facetStride && (facetStride < options.GetFacetSize())) return false;
return true;
}
void
Tessellation::initialize(Parameterization const & p,
int numRates, int const rates[], Options const & options) {
// Validate arguments and initialize simple members:
initializeDefaults();
if (!validateArguments(p, numRates, rates, options)) return;
_param = p;
_facetSize = (short) options.GetFacetSize();
_facetStride = options.GetFacetStride() ?
options.GetFacetStride() : options.GetFacetSize();
_coordStride = options.GetCoordStride() ? options.GetCoordStride() : 2;
// Initialize the full array of rates, returning sum of outer edge rates
int sumOfOuterRates = initializeRates(numRates, rates);
// Initialize the inventory based on the Parameterization type:
_triangulate = (_facetSize == 3) || !options.PreserveQuads();
switch (_param.GetType()) {
case Parameterization::QUAD:
initializeInventoryForParamQuad(sumOfOuterRates);
break;
case Parameterization::TRI:
initializeInventoryForParamTri(sumOfOuterRates);
break;
case Parameterization::QUAD_SUBFACES:
initializeInventoryForParamQPoly(sumOfOuterRates);
break;
}
_isValid = true;
// Debugging output:
bool printNonUniform = false; // !_isUniform;
if (printNonUniform) {
int N = _param.GetFaceSize();
printf("Tessellation::initialize(%d, numRates = %d):\n", N, numRates);
printf(" is uniform = %d\n", _isUniform);
printf(" outer rates =");
for (int i = 0; i < N; ++i) printf(" %d", _outerRates[i]);
printf("\n");
printf(" inner rate(s) = %d", _innerRates[0]);
if (N == 4) printf(" %d\n", _innerRates[1]);
printf("\n");
printf(" num boundary points = %d\n", _numBoundaryPoints);
printf(" num interior points = %d\n", _numInteriorPoints);
printf(" num facets = %d\n", _numFacets);
}
}
int
Tessellation::initializeRates(int numGivenRates, int const givenRates[]) {
_numGivenRates = numGivenRates;
// Allocate space for rates of N-sided faces if necessary:
int N = _param.GetFaceSize();
if (N > (int)(sizeof(_outerRatesLocal) / sizeof(int))) {
_outerRates = new int[N];
} else {
_outerRates = &_outerRatesLocal[0];
}
bool isQuad = (N == 4);
// Keep track of the total tessellation rate for all edges to return:
int const MaxRate = std::numeric_limits<short>::max();
int totalEdgeRate = 0;
if (numGivenRates < N) {
// Given one or two inner rates, infer outer (others < N ignored):
if ((numGivenRates == 2) && isQuad) {
// Infer outer rates from two given inner rates of quad:
_innerRates[0] = std::min(givenRates[0], MaxRate);
_innerRates[1] = std::min(givenRates[1], MaxRate);
_outerRates[0] = _outerRates[2] = _innerRates[0];
_outerRates[1] = _outerRates[3] = _innerRates[1];
_isUniform = (_innerRates[0] == _innerRates[1]);
totalEdgeRate = 2 * (_innerRates[0] + _innerRates[1]);
} else {
// Infer outer rates from single inner rate (uniform):
_innerRates[0] = std::min(givenRates[0], MaxRate);
_innerRates[1] = _innerRates[0];
std::fill(_outerRates, _outerRates + N, _innerRates[0]);
_isUniform = true;
totalEdgeRate = _innerRates[0] * N;
}
} else {
// Assign the N outer rates:
_isUniform = true;
for (int i = 0; i < N; ++i) {
_outerRates[i] = std::min(givenRates[i], MaxRate);
_isUniform = _isUniform && (_outerRates[i] == _outerRates[0]);
totalEdgeRate += _outerRates[i];
}
// Assign any given inner rates or infer:
if (numGivenRates > N) {
// Assign single inner rate, assign/infer second for quad:
_innerRates[0] = std::min(givenRates[N], MaxRate);
_innerRates[1] = ((numGivenRates == 6) && isQuad)
? std::min(givenRates[5], MaxRate) : _innerRates[0];
_isUniform = _isUniform && (_innerRates[0] == _outerRates[0]);
_isUniform = _isUniform && (_innerRates[1] == _outerRates[0]);
} else if (isQuad) {
// Infer two inner rates for quads (avg of opposite edges):
_innerRates[0] = (_outerRates[0] + _outerRates[2]) / 2;
_innerRates[1] = (_outerRates[1] + _outerRates[3]) / 2;
} else {
// Infer single inner rate for non-quads (avg of edge rates)
_innerRates[0] = totalEdgeRate / N;
_innerRates[1] = _innerRates[0];
}
}
return totalEdgeRate;
}
int
Tessellation::GetRates(int rates[]) const {
int N = _param.GetFaceSize();
int numOuterRates = std::min<int>(N, _numGivenRates);
int numInnerRates = std::max<int>(0, _numGivenRates - N);
for (int i = 0; i < numOuterRates; ++i) {
rates[i] = _outerRates[i];
}
for (int i = 0; i < numInnerRates; ++i) {
rates[N + i] = _innerRates[i > 0];
}
return _numGivenRates;
}
void
Tessellation::initializeInventoryForParamQuad(int sumOfEdgeRates) {
int const * inner = &_innerRates[0];
int const * outer = &_outerRates[0];
if (_isUniform) {
if (inner[0] > 1) {
_numInteriorPoints = quad::CountInteriorCoords(inner[0]);
_numFacets = quad::CountUniformFacets(inner[0], _triangulate);
} else if (_triangulate) {
_numInteriorPoints = 0;
_numFacets = 2;
_splitQuad = true;
} else {
_numInteriorPoints = 0;
_numFacets = 1;
_singleFace = true;
}
} else {
//
// For quads another low-res case is recognized when there are
// no interior points, but the face has extra boundary points.
// Instead of introducing a center point, the face is considered
// to be "segmented" into other faces that cover it without the
// addition of any interior vertices.
//
// This currently occurs for a pure 1 x M tessellation -- from
// which a quad strip is generated -- but could be extended to
// handle the 1 x M inner case with additional points on the
// opposing edges.
//
if ((inner[0] > 1) && (inner[1] > 1)) {
_numInteriorPoints = quad::CountInteriorCoords(_innerRates);
_numFacets = quad::CountNonUniformFacets(_outerRates, _innerRates,
_triangulate);
} else if ((outer[0] == inner[0]) && (inner[0] == outer[2]) &&
(outer[1] == inner[1]) && (inner[1] == outer[3])) {
_numInteriorPoints = 0;
_numFacets = quad::CountSegmentedFacets(_innerRates, _triangulate);
_segmentedFace = true;
} else {
_numInteriorPoints = 1;
_numFacets = sumOfEdgeRates;
_triangleFan = true;
}
}
_numBoundaryPoints = sumOfEdgeRates;
}
void
Tessellation::initializeInventoryForParamTri(int sumOfEdgeRates) {
int res = _innerRates[0];
if (_isUniform) {
if (res > 1) {
_numInteriorPoints = tri::CountInteriorCoords(res);
_numFacets = tri::CountUniformFacets(res);
} else {
_numInteriorPoints = 0;
_numFacets = 1;
_singleFace = true;
}
} else {
if (res > 2) {
_numInteriorPoints = tri::CountInteriorCoords(res);
_numFacets = tri::CountNonUniformFacets(_outerRates, res);
} else {
_numInteriorPoints = 1;
_numFacets = sumOfEdgeRates;
_triangleFan = true;
}
}
_numBoundaryPoints = sumOfEdgeRates;
}
void
Tessellation::initializeInventoryForParamQPoly(int sumOfEdgeRates) {
int N = _param.GetFaceSize();
int res = _innerRates[0];
if (_isUniform) {
if (res > 1) {
_numInteriorPoints = qsub::CountInteriorCoords(N, res);
_numFacets = qsub::CountUniformFacets(N, res, _triangulate);
} else if (N == 3) {
_numInteriorPoints = 0;
_numFacets = 1;
_singleFace = true;
} else {
_numInteriorPoints = 1;
_numFacets = N;
_triangleFan = true;
}
} else {
if (res > 1) {
_numInteriorPoints = qsub::CountInteriorCoords(N, res);
_numFacets = qsub::CountNonUniformFacets(N, _outerRates, res,
_triangulate);
} else {
_numInteriorPoints = 1;
_numFacets = sumOfEdgeRates;
_triangleFan = true;
}
}
_numBoundaryPoints = sumOfEdgeRates;
}
//
// Tessellation constructors and destructor:
//
Tessellation::Tessellation(
Parameterization const & p, int uniformRate,
Options const & options) {
initialize(p, 1, &uniformRate, options);
}
Tessellation::Tessellation(
Parameterization const & p, int numRates, int const rates[],
Options const & options) {
initialize(p, numRates, rates, options);
}
Tessellation::~Tessellation() {
if (_outerRates != &_outerRatesLocal[0]) {
delete[] _outerRates;
}
}
//
// Main methods to retrieve samples and facets:
//
template <typename REAL>
int
Tessellation::GetEdgeCoords(int edge, REAL coordBuffer[]) const {
// Remember this method excludes coords at the end vertices
int edgeRes = _outerRates[edge];
Coord2Array<REAL> coords(coordBuffer, _coordStride);
switch (_param.GetType()) {
case Parameterization::QUAD:
return quad::GetEdgeCoords(edge, edgeRes, coords);
case Parameterization::TRI:
return tri::GetEdgeCoords(edge, edgeRes, coords);
case Parameterization::QUAD_SUBFACES:
return qsub::GetEdgeCoords(_param, edge, edgeRes, coords);
default:
assert(0);
}
return -1;
}
template <typename REAL>
int
Tessellation::GetBoundaryCoords(REAL coordBuffer[]) const {
Coord2Array<REAL> coords(coordBuffer, _coordStride);
switch (_param.GetType()) {
case Parameterization::QUAD:
return quad::GetBoundaryCoords(_outerRates, coords);
case Parameterization::TRI:
return tri::GetBoundaryCoords(_outerRates, coords);
case Parameterization::QUAD_SUBFACES:
return qsub::GetBoundaryCoords(_param, _outerRates, coords);
default:
assert(0);
}
return -1;
}
template <typename REAL>
int
Tessellation::GetInteriorCoords(REAL coordBuffer[]) const {
if (_numInteriorPoints == 0) return 0;
if (_numInteriorPoints == 1) {
_param.GetCenterCoord(coordBuffer);
return 1;
}
Coord2Array<REAL> coords(coordBuffer, _coordStride);
switch (_param.GetType()) {
case Parameterization::QUAD:
return quad::GetInteriorCoords(_innerRates, coords);
case Parameterization::TRI:
return tri::GetInteriorCoords(_innerRates[0], coords);
case Parameterization::QUAD_SUBFACES:
return qsub::GetInteriorCoords(_param, _innerRates[0], coords);
default:
assert(0);
}
return 0;
}
int
Tessellation::GetFacets(int facetIndices[]) const {
FacetArray facets(facetIndices, _facetSize, _facetStride);
int N = GetFaceSize();
if (_singleFace) {
if (N == 3) {
return appendTri(facets, 0, 1, 2);
} else {
return appendQuad(facets, 0, 1, 2, 3, 0);
}
}
if (_triangleFan) {
return appendTriFan(facets, _numFacets, 0);
}
if (_splitQuad) {
return appendQuad(facets, 0, 1, 2, 3, _triangulate);
}
int nFacets = 0;
switch (_param.GetType()) {
case Parameterization::QUAD:
if (_isUniform) {
nFacets = quad::GetUniformFacets(_innerRates[0], _triangulate,
facets);
} else if (_segmentedFace) {
nFacets = quad::GetSegmentedFacets(_innerRates, _triangulate,
facets);
} else {
nFacets = quad::GetNonUniformFacets(_outerRates, _innerRates,
_numBoundaryPoints, _triangulate, facets);
}
break;
case Parameterization::TRI:
if (_isUniform) {
nFacets = tri::GetUniformFacets(_innerRates[0], facets);
} else {
nFacets = tri::GetNonUniformFacets(_outerRates, _innerRates[0],
_numBoundaryPoints, facets);
}
break;
case Parameterization::QUAD_SUBFACES:
if (_isUniform) {
nFacets = qsub::GetUniformFacets(N, _innerRates[0], _triangulate,
facets);
} else {
nFacets = qsub::GetNonUniformFacets(N, _outerRates, _innerRates[0],
_numBoundaryPoints, _triangulate, facets);
}
break;
default:
assert(0);
}
assert(nFacets == _numFacets);
return nFacets;
}
void
Tessellation::TransformFacetCoordIndices(int facetIndices[], int commonOffset) {
if (_facetSize == 4) {
for (int i = 0; i < _numFacets; ++i, facetIndices += _facetStride) {
facetIndices[0] += commonOffset;
facetIndices[1] += commonOffset;
facetIndices[2] += commonOffset;
if (facetIndices[3] >= 0) {
facetIndices[3] += commonOffset;
}
}
} else {
for (int i = 0; i < _numFacets; ++i, facetIndices += _facetStride) {
facetIndices[0] += commonOffset;
facetIndices[1] += commonOffset;
facetIndices[2] += commonOffset;
}
}
}
void
Tessellation::TransformFacetCoordIndices(int facetIndices[],
int const boundaryIndices[],
int interiorOffset) {
for (int i = 0; i < _numFacets; ++i, facetIndices += _facetStride) {
for (int j = 0; j < (int)_facetSize; ++j) {
int & index = facetIndices[j];
if (index >= 0) {
index = (index < _numBoundaryPoints)
? boundaryIndices[index]
: (index + interiorOffset);
}
}
}
}
void
Tessellation::TransformFacetCoordIndices(int facetIndices[],
int const boundaryIndices[],
int const interiorIndices[]) {
for (int i = 0; i < _numFacets; ++i, facetIndices += _facetStride) {
for (int j = 0; j < (int)_facetSize; ++j) {
int & index = facetIndices[j];
if (index >= 0) {
index = (index < _numBoundaryPoints)
? boundaryIndices[index]
: interiorIndices[index - _numBoundaryPoints];
}
}
}
}
//
// Explicit instantiation for multiple precision coordinate pairs:
//
template int
Tessellation::GetBoundaryCoords<float>(float coordBuffer[]) const;
template int
Tessellation::GetBoundaryCoords<double>(double coordBuffer[]) const;
template int
Tessellation::GetInteriorCoords<float>(float coordBuffer[]) const;
template int
Tessellation::GetInteriorCoords<double>(double coordBuffer[]) const;
template int
Tessellation::GetEdgeCoords<float>(int edge, float coordBuffer[]) const;
template int
Tessellation::GetEdgeCoords<double>(int edge, double coordBuffer[]) const;
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv