Introduces 'single-crease patch' : implements "Efficient Evaluation of Semi-Smooth Creases in

Catmull-Clark Subdivision Surfaces", Niessner et al, Eurographics 2012.

This change includes;
-topology identification for single-crease patch during adaptive refinement.
-patch array population (similar to boundary)
-sharpness buffer generation
-glsl shader

Eval stuffs will be coming.
This commit is contained in:
Takahito Tejima 2014-10-13 08:52:09 -07:00
parent c30087cb52
commit c0907c7bc1
18 changed files with 548 additions and 101 deletions

View File

@ -24,7 +24,8 @@
#include "patchColors.h"
static float _colors[4][5][4] = {{{1.0f, 1.0f, 1.0f, 1.0f}, // regular
static float _colors[5][6][4] = {{{1.0f, 1.0f, 1.0f, 1.0f}, // regular
{1.0f, 0.5f, 0.5f, 1.0f}, // single crease
{0.8f, 0.0f, 0.0f, 1.0f}, // boundary
{0.0f, 1.0f, 0.0f, 1.0f}, // corner
{1.0f, 1.0f, 0.0f, 1.0f}, // gregory
@ -36,6 +37,12 @@ static float _colors[4][5][4] = {{{1.0f, 1.0f, 1.0f, 1.0f}, // regular
{0.5f, 0.0f, 1.0f, 1.0f}, // regular pattern 3
{1.0f, 0.5f, 1.0f, 1.0f}}, // regular pattern 4
{{1.0f, 0.7f, 0.6f, 1.0f}, // single crease pattern 0
{1.0f, 0.7f, 0.6f, 1.0f}, // single crease pattern 1
{1.0f, 0.7f, 0.6f, 1.0f}, // single crease pattern 2
{1.0f, 0.7f, 0.6f, 1.0f}, // single crease pattern 3
{1.0f, 0.7f, 0.6f, 1.0f}}, // single crease pattern 4
{{0.0f, 0.0f, 0.75f, 1.0f}, // boundary pattern 0
{0.0f, 0.2f, 0.75f, 1.0f}, // boundary pattern 1
{0.0f, 0.4f, 0.75f, 1.0f}, // boundary pattern 2

View File

@ -148,9 +148,10 @@ enum HudCheckBox { kHUD_CB_DISPLAY_CAGE_EDGES,
kHUD_CB_VIEW_LOD,
kHUD_CB_FRACTIONAL_SPACING,
kHUD_CB_PATCH_CULL,
kHUD_CB_FREEZE };
kHUD_CB_FREEZE,
kHUD_CB_DISPLAY_PATCH_COUNTS };
int g_currentShape = 0;
int g_currentShape = 8;
ObjAnim const * g_objAnim = 0;
@ -164,16 +165,18 @@ float g_animTime = 0;
int g_fullscreen = 0,
g_freeze = 0,
g_displayStyle = kWireShaded,
g_adaptive = 0,
g_adaptive = 1,
g_singleCreasePatch = 1,
g_drawCageEdges = 1,
g_drawCageVertices = 0,
g_mbutton[3] = {0, 0, 0},
g_running = 1;
int g_displayPatchColor = 1,
g_screenSpaceTess = 0,
g_fractionalSpacing = 0,
g_patchCull = 0;
g_screenSpaceTess = 1,
g_fractionalSpacing = 1,
g_patchCull = 0,
g_displayPatchCounts = 0;
float g_rotate[2] = {0, 0},
g_dolly = 5,
@ -523,10 +526,12 @@ createOsdMesh(ShapeDesc const & shapeDesc, int level, int kernel, Scheme scheme=
// Adaptive refinement currently supported only for catmull-clark scheme
bool doAdaptive = (g_adaptive!=0 and g_scheme==kCatmark),
interleaveVarying = g_displayStyle == kInterleavedVaryingColor;
interleaveVarying = g_displayStyle == kInterleavedVaryingColor,
doSingleCreasePatch = (g_singleCreasePatch!=0 and g_scheme==kCatmark);
OpenSubdiv::Osd::MeshBitset bits;
bits.set(OpenSubdiv::Osd::MeshAdaptive, doAdaptive);
bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, doSingleCreasePatch);
bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, interleaveVarying);
bits.set(OpenSubdiv::Osd::MeshFVarData, g_displayStyle == kFaceVaryingColor);
@ -1148,7 +1153,9 @@ display() {
g_mesh->GetDrawContext()->GetPatchArrays();
// patch drawing
int patchCount[11][6][4]; // [Type][Pattern][Rotation] (see far/patchTables.h)
int patchCount[12][6][4]; // [Type][Pattern][Rotation] (see far/patchTables.h)
int numTotalPatches = 0;
int numDrawCalls = 0;
memset(patchCount, 0, sizeof(patchCount));
// primitive counting
@ -1169,6 +1176,7 @@ display() {
if (subPatch == 0) {
patchCount[patchType][patchPattern][patchRotation] += patch.GetNumPatches();
}
numTotalPatches += patch.GetNumPatches();
GLenum primType;
@ -1223,6 +1231,7 @@ display() {
glDrawElements(primType, patch.GetNumIndices(), GL_UNSIGNED_INT,
(void *)(patch.GetVertIndex() * sizeof(unsigned int)));
++numDrawCalls;
if (g_displayStyle == kWire) {
glEnable(GL_CULL_FACE);
}
@ -1268,47 +1277,64 @@ display() {
double fps = 1.0/elapsed;
int x = -280;
g_hud.DrawString(x, -360, "NonPatch : %d",
patchCount[OpenSubdiv::Far::PatchTables::QUADS][0][0]);
g_hud.DrawString(x, -340, "Regular : %d",
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][0][0]);
g_hud.DrawString(x, -320, "Boundary : %d",
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][0][0]);
g_hud.DrawString(x, -300, "Corner : %d",
patchCount[OpenSubdiv::Far::PatchTables::CORNER][0][0]);
g_hud.DrawString(x, -280, "Gregory : %d",
patchCount[OpenSubdiv::Far::PatchTables::GREGORY][0][0]);
g_hud.DrawString(x, -260, "Boundary Gregory : %d",
patchCount[OpenSubdiv::Far::PatchTables::GREGORY_BOUNDARY][0][0]);
g_hud.DrawString(x, -240, "Trans. Regular : %d %d %d %d %d",
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN0][0],
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN1][0],
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN2][0],
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN3][0],
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN4][0]);
for (int i=0; i < 5; i++)
g_hud.DrawString(x, -220+i*20, "Trans. Boundary%d : %d %d %d %d", i,
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][0],
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][1],
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][2],
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][3]);
for (int i=0; i < 5; i++)
g_hud.DrawString(x, -100+i*20, "Trans. Corner%d : %d %d %d %d", i,
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][0],
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][1],
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][2],
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][3]);
if (g_displayPatchCounts) {
int x = -280;
int y = -480;
g_hud.DrawString(x, y, "NonPatch : %d",
patchCount[OpenSubdiv::Far::PatchTables::QUADS][0][0]); y += 20;
g_hud.DrawString(x, y, "Regular : %d",
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][0][0]); y+= 20;
g_hud.DrawString(x, y, "Boundary : %d",
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][0][0]); y+= 20;
g_hud.DrawString(x, y, "Corner : %d",
patchCount[OpenSubdiv::Far::PatchTables::CORNER][0][0]); y+= 20;
g_hud.DrawString(x, y, "Single Crease : %d",
patchCount[OpenSubdiv::Far::PatchTables::SINGLE_CREASE][0][0]); y+= 20;
g_hud.DrawString(x, y, "Gregory : %d",
patchCount[OpenSubdiv::Far::PatchTables::GREGORY][0][0]); y+= 20;
g_hud.DrawString(x, y, "Boundary Gregory : %d",
patchCount[OpenSubdiv::Far::PatchTables::GREGORY_BOUNDARY][0][0]); y+= 20;
g_hud.DrawString(x, y, "Trans. Regular : %d %d %d %d %d",
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN0][0],
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN1][0],
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN2][0],
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN3][0],
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN4][0]); y+= 20;
for (int i=0; i < 5; i++) {
g_hud.DrawString(x, y, "Trans. Boundary%d : %d %d %d %d", i,
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][0],
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][1],
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][2],
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][3]); y+= 20;
}
for (int i=0; i < 5; i++) {
g_hud.DrawString(x, y, "Trans. Corner%d : %d %d %d %d", i,
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][0],
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][1],
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][2],
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][3]); y+= 20;
}
for (int i=0; i < 5; i++) {
g_hud.DrawString(x, y, "Trans. Single Crease%d : %d %d %d %d", i,
patchCount[OpenSubdiv::Far::PatchTables::SINGLE_CREASE][i+1][0],
patchCount[OpenSubdiv::Far::PatchTables::SINGLE_CREASE][i+1][1],
patchCount[OpenSubdiv::Far::PatchTables::SINGLE_CREASE][i+1][2],
patchCount[OpenSubdiv::Far::PatchTables::SINGLE_CREASE][i+1][3]); y+= 20;
}
}
g_hud.DrawString(10, -180, "Tess level : %d", g_tessLevel);
g_hud.DrawString(10, -160, "Primitives : %d", numPrimsGenerated);
g_hud.DrawString(10, -140, "Vertices : %d", g_mesh->GetNumVertices());
g_hud.DrawString(10, -120, "Scheme : %s", g_scheme==kBilinear ? "BILINEAR" : (g_scheme == kLoop ? "LOOP" : "CATMARK"));
g_hud.DrawString(10, -100, "GPU Kernel : %.3f ms", g_gpuTime);
g_hud.DrawString(10, -80, "CPU Kernel : %.3f ms", g_cpuTime);
g_hud.DrawString(10, -60, "GPU Draw : %.3f ms", drawGpuTime);
g_hud.DrawString(10, -40, "CPU Draw : %.3f ms", drawCpuTime);
g_hud.DrawString(10, -20, "FPS : %3.1f", fps);
int y = -220;
g_hud.DrawString(10, y, "Tess level : %d", g_tessLevel); y+= 20;
g_hud.DrawString(10, y, "Patches : %d", numTotalPatches); y+= 20;
g_hud.DrawString(10, y, "Draw calls : %d", numDrawCalls); y+= 20;
g_hud.DrawString(10, y, "Primitives : %d", numPrimsGenerated); y+= 20;
g_hud.DrawString(10, y, "Vertices : %d", g_mesh->GetNumVertices()); y+= 20;
g_hud.DrawString(10, y, "Scheme : %s", g_scheme==kBilinear ? "BILINEAR" : (g_scheme == kLoop ? "LOOP" : "CATMARK")); y+= 20;
g_hud.DrawString(10, y, "GPU Kernel : %.3f ms", g_gpuTime); y+= 20;
g_hud.DrawString(10, y, "CPU Kernel : %.3f ms", g_cpuTime); y+= 20;
g_hud.DrawString(10, y, "GPU Draw : %.3f ms", drawGpuTime); y+= 20;
g_hud.DrawString(10, y, "CPU Draw : %.3f ms", drawCpuTime); y+= 20;
g_hud.DrawString(10, y, "FPS : %3.1f", fps); y+= 20;
g_hud.Flush();
}
@ -1519,6 +1545,15 @@ callbackAdaptive(bool checked, int /* a */)
}
}
static void
callbackSingleCreasePatch(bool checked, int /* a */)
{
if (OpenSubdiv::Osd::GLDrawContext::SupportsAdaptiveTessellation()) {
g_singleCreasePatch = checked;
rebuildOsdMesh();
}
}
static void
callbackCheckBox(bool checked, int button)
{
@ -1547,6 +1582,9 @@ callbackCheckBox(bool checked, int button)
case kHUD_CB_FREEZE:
g_freeze = checked;
break;
case kHUD_CB_DISPLAY_PATCH_COUNTS:
g_displayPatchCounts = checked;
break;
}
}
@ -1614,8 +1652,10 @@ initHUD()
g_hud.AddPullDownButton(compute_pulldown, "GLSL Compute", kGLSLCompute);
}
#endif
if (OpenSubdiv::Osd::GLDrawContext::SupportsAdaptiveTessellation())
if (OpenSubdiv::Osd::GLDrawContext::SupportsAdaptiveTessellation()) {
g_hud.AddCheckBox("Adaptive (`)", g_adaptive!=0, 10, 190, callbackAdaptive, 0, '`');
g_hud.AddCheckBox("Single Crease Patch (S)", g_singleCreasePatch!=0, 10, 210, callbackSingleCreasePatch, 0, 's');
}
for (int i = 1; i < 11; ++i) {
char level[16];
@ -1628,6 +1668,8 @@ initHUD()
g_hud.AddPullDownButton(shapes_pulldown, g_defaultShapes[i].name.c_str(),i);
}
g_hud.AddCheckBox("Show patch counts", g_displayPatchCounts!=0, -280, -20, callbackCheckBox, kHUD_CB_DISPLAY_PATCH_COUNTS);
g_hud.Rebuild(g_width, g_height);
}

View File

@ -358,7 +358,9 @@ edgeColor(vec4 Cfill, vec4 edgeDistance)
min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
#endif
vec4 Cedge = vec4(1.0, 1.0, 0.0, 1.0);
//vec4 Cedge = vec4(1.0, 1.0, 0.0, 1.0);
float v = 0.8;
vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
float p = exp2(-2 * d * d);
#if defined(GEOMETRY_OUT_WIRE)

View File

@ -253,19 +253,23 @@ PatchTables::getAdaptiveCatmarkDescriptors() {
if (_descriptors.empty()) {
_descriptors.reserve(50);
_descriptors.reserve(71);
// non-transition patches
// non-transition patches : 6
for (int i=REGULAR; i<=GREGORY_BOUNDARY; ++i) {
_descriptors.push_back( Descriptor(i, NON_TRANSITION, 0) );
}
// transition patches
// transition patches (1 + 4 * 3) * 5 = 65
for (int i=PATTERN0; i<=PATTERN4; ++i) {
_descriptors.push_back( Descriptor(REGULAR, i, 0) );
// 4 rotations for boundary & corner patches
// 4 rotations for single-crease, boundary and corner patches
for (int j=0; j<4; ++j) {
_descriptors.push_back( Descriptor(SINGLE_CREASE, i, j) );
}
for (int j=0; j<4; ++j) {
_descriptors.push_back( Descriptor(BOUNDARY, i, j) );
}

View File

@ -70,6 +70,7 @@ public:
LOOP, ///< Loop patch (currently unsupported)
REGULAR, ///< feature-adaptive bicubic patches
SINGLE_CREASE,
BOUNDARY,
CORNER,
GREGORY,
@ -339,6 +340,12 @@ public:
/// \brief Returns a PatchParamTable for each type of patch
PatchParamTable const & GetPatchParamTable() const { return _paramTable; }
/// \brief Returns a sharpness index table for each type of patch (if exists)
std::vector<int> const &GetSharpnessIndexTable() const { return _sharpnessIndexTable; }
/// \brief Returns sharpness values (if exists)
std::vector<float> const &GetSharpnessValues() const { return _sharpnessValues; }
/// \brief Number of control vertices of Regular Patches in table.
static short GetRegularPatchSize() { return 16; }
@ -577,6 +584,10 @@ private:
FVarPatchTables const * _fvarPatchTables; // sparse face-varying patch table (one per patch)
std::vector<int> _sharpnessIndexTable;// Indices of single-crease sharpness (one per patch)
std::vector<float> _sharpnessValues; // Sharpness values.
// highest vertex valence allowed in the mesh (used for Gregory
// vertexValance & quadOffset tables)
int _maxValence;
@ -591,6 +602,7 @@ inline short
PatchTables::Descriptor::GetNumControlVertices( PatchTables::Type type ) {
switch (type) {
case REGULAR : return PatchTables::GetRegularPatchSize();
case SINGLE_CREASE : return PatchTables::GetRegularPatchSize();
case QUADS : return 4;
case GREGORY :
case GREGORY_BOUNDARY : return PatchTables::GetGregoryPatchSize();
@ -608,6 +620,7 @@ inline short
PatchTables::Descriptor::GetNumFVarControlVertices( PatchTables::Type type ) {
switch (type) {
case REGULAR : // We only support bilinear interpolation for now,
case SINGLE_CREASE :
case QUADS : // so all these patches only carry 4 CVs.
case GREGORY :
case GREGORY_BOUNDARY :
@ -811,6 +824,10 @@ PatchTables::Limit(PatchHandle const & handle, float s, float t,
case REGULAR:
InterpolateRegularPatch(cvs, Q, Qd1, Qd2, src, dst);
break;
case SINGLE_CREASE:
// TODO: implement InterpolateSingleCreasePatch().
//InterpolateRegularPatch(cvs, Q, Qd1, Qd2, src, dst);
break;
case BOUNDARY:
InterpolateBoundaryPatch(cvs, Q, Qd1, Qd2, src, dst);
break;

View File

@ -46,6 +46,7 @@ struct PatchTypes {
NUM_ROTATIONS=4;
TYPE R[NUM_TRANSITIONS], // regular patch
S[NUM_TRANSITIONS][NUM_ROTATIONS], // single-crease patch
B[NUM_TRANSITIONS][NUM_ROTATIONS], // boundary patch (4 rotations)
C[NUM_TRANSITIONS][NUM_ROTATIONS], // corner patch (4 rotations)
G, // gregory patch
@ -57,6 +58,7 @@ struct PatchTypes {
TYPE & getValue( PatchTables::Descriptor desc ) {
switch (desc.GetType()) {
case PatchTables::REGULAR : return R[desc.GetPattern()];
case PatchTables::SINGLE_CREASE : return S[desc.GetPattern()][desc.GetRotation()];
case PatchTables::BOUNDARY : return B[desc.GetPattern()][desc.GetRotation()];
case PatchTables::CORNER : return C[desc.GetPattern()][desc.GetRotation()];
case PatchTables::GREGORY : return G;
@ -76,6 +78,7 @@ struct PatchTypes {
if (R[i]) ++result;
for (int j=0; j<4; ++j) {
if (S[i][j]) ++result;
if (B[i][j]) ++result;
if (C[i][j]) ++result;
@ -85,10 +88,21 @@ struct PatchTypes {
if (GB) ++result;
return result;
}
// Returns true if there's any single-crease patch
bool hasSingleCreasedPatches() const {
for (int i=0; i<6; ++i) {
for (int j=0; j<4; ++j) {
if (S[i][j]) return true;
}
}
return false;
}
};
typedef PatchTypes<Index *> PatchCVPointers;
typedef PatchTypes<PatchParam *> PatchParamPointers;
typedef PatchTypes<Index *> SharpnessIndexPointers;
typedef PatchTypes<int> PatchCounters;
typedef PatchTypes<Index **> PatchFVarPointers;
@ -131,6 +145,7 @@ public:
unsigned int _boundaryIndex : 2;
unsigned int _boundaryCount : 3;
unsigned int _hasBoundaryEdge : 3;
unsigned int _isSingleCrease : 1;
void clear() { std::memset(this, 0, sizeof(*this)); }
@ -251,6 +266,39 @@ public:
}
}
void assignTransitionRotationForSingleCrease(int transitionEdgeMask) {
//
// Single crease transition patches.
//
// rotate edgemask by boundaryIndex to align the creased edge
//
transitionEdgeMask = ((transitionEdgeMask >> _boundaryIndex) |
(transitionEdgeMask << (4-_boundaryIndex))) % 16;
/*
edgemask type : rotation to match to shader
0000 0 : NONE : 0
0001 1 : ONE : 0
0010 2 : ONE : 3
0011 3 : TWO_ADJ : 3
0100 4 : ONE : 2
0101 5 : TWO_OPP : 0
0110 6 : TWO_ADJ : 2
0111 7 : THREE : 1 (needs verify)
1000 8 : ONE : 1
1001 9 : TWO_ADJ : 0
1010 10 : TWO_OPP : 1
1011 11 : THREE : 2 (needs verify)
1100 12 : TWO_ADJ : 1
1101 13 : THREE : 3
1110 14 : THREE : 0 (needs verify)
1111 15 : ALL : 0
*/
static int transitionRots[16] = {0, 0, 3, 3, 2, 0, 2, 1, 1, 0, 1, 2, 1, 3, 0, 0 };
_transitionRot = transitionRots[transitionEdgeMask];
}
void assignTransitionPropertiesFromEdgeMask(int transitionEdgeMask) {
//
// Note the transition rotations will be a function of the boundary rotations, and
@ -286,6 +334,8 @@ public:
// results below are a function of both transition and boundary properties...
if (transitionEdgeMask == 0) {
_transitionRot = 0;
} else if (_boundaryCount == 0 and _isSingleCrease) {
assignTransitionRotationForSingleCrease(transitionEdgeMask);
} else if (_boundaryCount == 0) {
// XXXX manuelk Rotations are mostly a direct map of the transitionEdgeMask
// Except for:
@ -335,7 +385,7 @@ namespace {
// Reserves tables based on the contents of the PatchArrayVector in the PatchTables:
//
void
PatchTablesFactory::allocateTables(PatchTables * tables, int /* nlevels */) {
PatchTablesFactory::allocateTables(PatchTables * tables, int /* nlevels */, bool hasSharpness) {
PatchTables::PatchArrayVector const & parrays = tables->GetPatchArrayVector();
@ -352,6 +402,10 @@ PatchTablesFactory::allocateTables(PatchTables * tables, int /* nlevels */) {
tables->_patches.resize( nverts );
tables->_paramTable.resize( npatches );
if (hasSharpness) {
tables->_sharpnessIndexTable.resize( npatches );
}
}
PatchTables::FVarPatchTables *
@ -517,6 +571,20 @@ PatchTablesFactory::getQuadOffsets(Vtr::Level const& level, Vtr::Index fIndex, I
}
}
//
// Indexing sharpnesses
//
int
PatchTablesFactory::assignSharpnessIndex( PatchTables *tables, float sharpness ) {
// linear search
for (int i=0; i<(int)tables->_sharpnessValues.size(); ++i) {
if (tables->_sharpnessValues[i] == sharpness) return i;
}
tables->_sharpnessValues.push_back(sharpness);
return (int)tables->_sharpnessValues.size()-1;
}
//
// We should be able to use a single Create() method for both the adaptive and uniform
// cases. In the past, more additional arguments were passed to the uniform version,
@ -606,7 +674,7 @@ PatchTablesFactory::createUniform( TopologyRefiner const & refiner, Options opti
}
// Allocate various tables
allocateTables( tables, 0 );
allocateTables( tables, 0, /*hasSharpness=*/false );
if (options.generateFVarTables) {
tables->_fvarPatchTables = allocateFVarTables( refiner, *tables, options );
@ -701,7 +769,7 @@ PatchTablesFactory::createAdaptive( TopologyRefiner const & refiner, Options opt
PatchCounters patchInventory;
std::vector<PatchFaceTag> patchTags;
identifyAdaptivePatches(refiner, patchInventory, patchTags);
identifyAdaptivePatches(refiner, patchInventory, patchTags, options);
//
// Create the instance of the tables and allocate and initialize its members based on
@ -729,7 +797,8 @@ PatchTablesFactory::createAdaptive( TopologyRefiner const & refiner, Options opt
tables->_numPtexFaces = refiner.GetNumPtexFaces();
// Allocate various tables
allocateTables( tables, 0 );
bool hasSharpness = patchInventory.hasSingleCreasedPatches();
allocateTables( tables, 0, hasSharpness );
if (options.generateFVarTables) {
tables->_fvarPatchTables = allocateFVarTables( refiner, *tables, options );
@ -743,7 +812,7 @@ PatchTablesFactory::createAdaptive( TopologyRefiner const & refiner, Options opt
//
// Now populate the patches:
//
populateAdaptivePatches(refiner, patchInventory, patchTags, tables);
populateAdaptivePatches(refiner, patchInventory, patchTags, tables, options);
return tables;
}
@ -755,9 +824,9 @@ PatchTablesFactory::createAdaptive( TopologyRefiner const & refiner, Options opt
//
void
PatchTablesFactory::identifyAdaptivePatches( TopologyRefiner const & refiner,
PatchCounters & patchInventory,
PatchTagVector & patchTags ) {
PatchCounters & patchInventory,
PatchTagVector & patchTags,
Options options ) {
//
// Iterate through the levels of refinement to inspect and tag components with information
// relative to patch generation. We allocate all of the tags locally and use them to
@ -866,6 +935,24 @@ PatchTablesFactory::identifyAdaptivePatches( TopologyRefiner const & refiner,
int boundaryEdgeMask = 0;
bool hasBoundaryVertex = compFaceVertTag._boundary;
// single crease patch optimization
if (options.useSingleCreasePatch and
!compFaceVertTag._xordinary and compFaceVertTag._semiSharp and not hasBoundaryVertex) {
float sharpness = 0;
int rotation = 0;
if (level->isSingleCreasePatch(faceIndex, &sharpness, &rotation)) {
// cap sharpness to the max isolation level
float cappedSharpness = std::min(sharpness, (float)(options.maxIsolationLevel-i));
if (cappedSharpness > 0) {
patchTag._isSingleCrease = true;
patchTag._boundaryIndex = (rotation + 2) % 4;
}
}
}
if (hasBoundaryVertex) {
Vtr::IndexArray const& fEdges = level->getFaceEdges(faceIndex);
@ -924,8 +1011,10 @@ PatchTablesFactory::identifyAdaptivePatches( TopologyRefiner const & refiner,
int transIndex = patchTag._transitionType;
int transRot = patchTag._transitionRot;
if (patchTag._boundaryCount == 0) {
if (!patchTag._isSingleCrease && patchTag._boundaryCount == 0) {
patchInventory.R[transIndex]++;
} else if (patchTag._isSingleCrease && patchTag._boundaryCount == 0) {
patchInventory.S[transIndex][transRot]++;
} else if (patchTag._boundaryCount == 1) {
patchInventory.B[transIndex][transRot]++;
} else {
@ -952,7 +1041,8 @@ void
PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
PatchCounters const & patchInventory,
PatchTagVector const & patchTags,
PatchTables * tables ) {
PatchTables * tables,
Options options ) {
//
// Setup convenience pointers at the beginning of each patch array for each
@ -961,6 +1051,7 @@ PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
PatchCVPointers iptrs;
PatchParamPointers pptrs;
PatchFVarPointers fptrs;
SharpnessIndexPointers sptrs;
typedef PatchTables::DescriptorVector DescVec;
@ -974,6 +1065,9 @@ PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
iptrs.getValue( *it ) = &tables->_patches[pa->GetVertIndex()];
pptrs.getValue( *it ) = &tables->_paramTable[pa->GetPatchIndex()];
if (patchInventory.hasSingleCreasedPatches()) {
sptrs.getValue( *it ) = &tables->_sharpnessIndexTable[pa->GetPatchIndex()];
}
if (tables->_fvarPatchTables) {
int nchannels = refiner.GetNumFVarChannels(),
@ -1031,7 +1125,7 @@ PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
int rIndex = patchTag._transitionRot;
int bIndex = patchTag._boundaryIndex;
if (patchTag._boundaryCount == 0) {
if (!patchTag._isSingleCrease && patchTag._boundaryCount == 0) {
int const permuteInterior[16] = { 5, 6, 7, 8, 4, 0, 1, 9, 15, 3, 2, 10, 14, 13, 12, 11 };
level->gatherQuadRegularInteriorPatchVertices(faceIndex, patchVerts, rIndex);
@ -1063,7 +1157,23 @@ PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
// It may be that a separate "face rotation" flag is warranted if we need something
// else dependent on the boundary orientation.
//
if (patchTag._boundaryCount == 1) {
if (patchTag._isSingleCrease && patchTag._boundaryCount==0) {
int const permuteInterior[16] = { 5, 6, 7, 8, 4, 0, 1, 9, 15, 3, 2, 10, 14, 13, 12, 11 };
level->gatherQuadRegularInteriorPatchVertices(faceIndex, patchVerts, bIndex);
offsetAndPermuteIndices(patchVerts, 16, levelVertOffset, permuteInterior, iptrs.S[tIndex][rIndex]);
int creaseEdge = (bIndex+2)%4;
float sharpness = level->getEdgeSharpness((level->getFaceEdges(faceIndex)[creaseEdge]));
sharpness = std::min(sharpness, (float)(options.maxIsolationLevel-i));
iptrs.S[tIndex][rIndex] += 16;
pptrs.S[tIndex][rIndex] = computePatchParam(refiner, i, faceIndex, rIndex, pptrs.S[tIndex][rIndex]);
*sptrs.S[tIndex][rIndex]++ = assignSharpnessIndex(tables, sharpness);
if (tables->_fvarPatchTables) {
gatherFVarPatchVertices(refiner, i, faceIndex, rIndex, levelFVarVertOffsets, fptrs.S[tIndex][rIndex]);
}
} else if (patchTag._boundaryCount == 1) {
int const permuteBoundary[12] = { 11, 3, 0, 4, 10, 2, 1, 5, 9, 8, 7, 6 };
level->gatherQuadRegularBoundaryPatchVertices(faceIndex, patchVerts, bIndex);

View File

@ -53,13 +53,18 @@ public:
struct Options {
Options() : generateAllLevels(false),
triangulateQuads(false),
generateFVarTables(false) { }
Options(unsigned int maxIsolationLevel=10) : generateAllLevels(false),
triangulateQuads(false),
generateFVarTables(false),
useSingleCreasePatch(false),
maxIsolationLevel(maxIsolationLevel)
{ }
unsigned int generateAllLevels : 1, ///< Include levels from 'firstLevel' to 'maxLevel' (Uniform mode only)
triangulateQuads : 1, ///< Triangulate 'QUADS' primitives (Uniform mode only)
generateFVarTables : 1; ///< Generate face-varying patch tables
unsigned int generateAllLevels : 1, ///< Include levels from 'firstLevel' to 'maxLevel' (Uniform mode only)
triangulateQuads : 1, ///< Triangulate 'QUADS' primitives (Uniform mode only)
generateFVarTables : 1, ///< Generate face-varying patch tables
useSingleCreasePatch : 1, ///< Use single crease patch
maxIsolationLevel : 4; ///< Cap the sharpnness of single creased patches to be consistent to other feature isolations.
};
/// \brief Factory constructor for PatchTables
@ -84,15 +89,17 @@ private:
// High-level methods for identifying and populating patches associated with faces:
static void identifyAdaptivePatches( TopologyRefiner const & refiner,
PatchTypes<int> & patchInventory,
std::vector<PatchFaceTag> & patchTags);
std::vector<PatchFaceTag> & patchTags,
Options options );
static void populateAdaptivePatches( TopologyRefiner const & refiner,
PatchTypes<int> const & patchInventory,
std::vector<PatchFaceTag> const & patchTags,
PatchTables * tables);
PatchTables * tables,
Options options );
// Methods for allocating and managing the patch table data arrays:
static void allocateTables( PatchTables * tables, int nlevels );
static void allocateTables( PatchTables * tables, int nlevels, bool hasSharpness );
static FVarPatchTables * allocateFVarTables( TopologyRefiner const & refiner,
PatchTables const & tables,
@ -103,10 +110,12 @@ private:
int npatches, int * voffset, int * poffset, int * qoffset );
static PatchParam * computePatchParam( TopologyRefiner const & refiner, int level,
int face, int rotation, PatchParam * coord );
int face, int rotation, PatchParam * coord );
static void getQuadOffsets(Vtr::Level const & level, int face, Index * result);
static int assignSharpnessIndex( PatchTables *tables, float sharpness );
private:
};

View File

@ -41,7 +41,8 @@ TopologyRefiner::TopologyRefiner(Sdc::Type schemeType, Sdc::Options schemeOption
_subdivType(schemeType),
_subdivOptions(schemeOptions),
_isUniform(true),
_maxLevel(0) {
_maxLevel(0),
_useSingleCreasePatch(false) {
// Need to revisit allocation scheme here -- want to use smart-ptrs for these
// but will probably have to settle for explicit new/delete...
@ -318,7 +319,7 @@ TopologyRefiner::RefineUniform(int maxLevel, bool fullTopology) {
void
TopologyRefiner::RefineAdaptive(int subdivLevel, bool fullTopology) {
TopologyRefiner::RefineAdaptive(int subdivLevel, bool fullTopology, bool useSingleCreasePatch) {
assert(_levels[0].getNumVertices() > 0); // Make sure the base level has been initialized
assert(_subdivType == Sdc::TYPE_CATMARK);
@ -328,6 +329,7 @@ TopologyRefiner::RefineAdaptive(int subdivLevel, bool fullTopology) {
//
_isUniform = false;
_maxLevel = subdivLevel;
_useSingleCreasePatch = useSingleCreasePatch;
// Should we presize all or grow one at a time as needed?
_levels.resize(subdivLevel + 1);
@ -435,7 +437,7 @@ TopologyRefiner::catmarkFeatureAdaptiveSelector(Vtr::SparseSelector& selector) {
}
bool selectFace = false;
if (compFaceTag._xordinary || compFaceTag._semiSharp) {
if (compFaceTag._xordinary) {
selectFace = true;
} else if (compFaceTag._rule & Sdc::Crease::RULE_DART) {
// Get this case out of the way before testing hard features
@ -449,6 +451,15 @@ TopologyRefiner::catmarkFeatureAdaptiveSelector(Vtr::SparseSelector& selector) {
// though some may be regular patches, this currently warrants isolation as we only
// support regular patches with one corner or one boundary.
selectFace = true;
} else if (compFaceTag._semiSharp) {
// if this is regular and the adjacent edges have same sharpness
// and no vertex corner sharpness,
// we can stop refinning and use single-crease patch.
if (_useSingleCreasePatch) {
selectFace = ! level.isSingleCreasePatch(face);
} else {
selectFace = true;
}
} else {
// This leaves us with at least one Smooth vertex (and so two smooth adjacent edges
// of the quad) and the rest hard Creases or Corners. This includes the regular

View File

@ -114,7 +114,9 @@ public:
/// @param fullTopologyInLastLevel Skip secondary topological relationships
/// at the highest level of refinement.
///
void RefineAdaptive(int maxLevel, bool fullTopologyInLastLevel = false);
/// @param useSingleCreasePatch Use single crease patch and stop isolation if it's applicable
///
void RefineAdaptive(int maxLevel, bool fullTopologyInLastLevel = false, bool useSingleCreasePatch = false);
/// \brief Unrefine the topology (keep control cage)
void Unrefine();
@ -496,6 +498,7 @@ private:
bool _isUniform;
int _maxLevel;
bool _useSingleCreasePatch;
std::vector<Vtr::Level> _levels;
std::vector<Vtr::Refinement> _refinements;

View File

@ -167,8 +167,27 @@ GLDrawContext::create(Far::PatchTables const & patchTables, int numVertexElement
Far::PatchTables::PatchParamTable const &
patchParamTables = patchTables.GetPatchParamTable();
if (not patchParamTables.empty())
_patchParamTextureBuffer = createTextureBuffer(patchParamTables, GL_RG32I);
if (not patchParamTables.empty()) {
std::vector<int> const &sharpnessIndexTable = patchTables.GetSharpnessIndexTable();
if (sharpnessIndexTable.empty()) {
_patchParamTextureBuffer = createTextureBuffer(patchParamTables, GL_RG32I);
} else {
// if indexed sharpnesses exists, flatten them and interleave into 3-component buffer
std::vector<float> const &sharpnessValues = patchTables.GetSharpnessValues();
size_t nPatches = patchParamTables.size();
// PatchParam = sizeof(int)*2, 1 float for sharpness
std::vector<unsigned int> buffer(nPatches * 3);
for (size_t i = 0; i < nPatches; ++i) {
float sharpness = sharpnessValues[sharpnessIndexTable[i]];
buffer[i*3+0] = patchParamTables[i].faceIndex;
buffer[i*3+1] = patchParamTables[i].bitField.field;
buffer[i*3+2] = *((unsigned int *)&sharpness);
}
_patchParamTextureBuffer = createTextureBuffer(buffer, GL_RGB32I);
}
}
glBindBuffer(GL_TEXTURE_BUFFER, 0);
#endif

View File

@ -94,6 +94,19 @@ GLDrawRegistryBase::_CreateDrawSourceConfig(
sconfig->tessEvalShader.version = "#version 410\n";
sconfig->tessEvalShader.AddDefine("OSD_PATCH_TESS_EVAL_BSPLINE_SHADER");
break;
case Far::PatchTables::SINGLE_CREASE:
sconfig->vertexShader.source = bsplineShaderSource;
sconfig->vertexShader.version = "#version 410\n";
sconfig->vertexShader.AddDefine("OSD_PATCH_VERTEX_BSPLINE_SHADER");
sconfig->tessControlShader.source = bsplineShaderSource;
sconfig->tessControlShader.version = "#version 410\n";
sconfig->tessControlShader.AddDefine("OSD_PATCH_TESS_CONTROL_BSPLINE_SHADER");
sconfig->tessControlShader.AddDefine("OSD_PATCH_SINGLE_CREASE");
sconfig->tessEvalShader.source = bsplineShaderSource;
sconfig->tessEvalShader.version = "#version 410\n";
sconfig->tessEvalShader.AddDefine("OSD_PATCH_TESS_EVAL_BSPLINE_SHADER");
sconfig->tessEvalShader.AddDefine("OSD_PATCH_SINGLE_CREASE");
break;
case Far::PatchTables::BOUNDARY:
sconfig->vertexShader.source = bsplineShaderSource;
sconfig->vertexShader.version = "#version 410\n";
@ -176,7 +189,10 @@ GLDrawRegistryBase::_CreateDrawSourceConfig(
sconfig->tessControlShader.AddDefine("OSD_TRANSITION_ROTATE", ss.str());
sconfig->tessEvalShader.AddDefine("OSD_TRANSITION_ROTATE", ss.str());
if (desc.GetType() == Far::PatchTables::BOUNDARY) {
if (desc.GetType() == Far::PatchTables::SINGLE_CREASE) {
sconfig->tessControlShader.AddDefine("OSD_PATCH_SINGLE_CREASE");
sconfig->tessEvalShader.AddDefine("OSD_PATCH_SINGLE_CREASE");
} else if (desc.GetType() == Far::PatchTables::BOUNDARY) {
sconfig->tessControlShader.AddDefine("OSD_PATCH_BOUNDARY");
} else if (desc.GetType() == Far::PatchTables::CORNER) {
sconfig->tessControlShader.AddDefine("OSD_PATCH_CORNER");

View File

@ -68,14 +68,14 @@ public:
_drawContext(0)
{
GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive));
GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive), bits.test(MeshUseSingleCreasePatch));
int numElements =
initializeVertexBuffers(numVertexElements, numVaryingElements, bits);
initializeComputeContext(numVertexElements, numVaryingElements);
initializeDrawContext(numElements, bits);
initializeDrawContext(numElements, level, bits);
}
Mesh(ComputeController * computeController,
@ -201,12 +201,13 @@ private:
delete varyingStencils;
}
void initializeDrawContext(int numElements, MeshBitset bits) {
void initializeDrawContext(int numElements, int level, MeshBitset bits) {
assert(_refiner and _vertexBuffer);
Far::PatchTablesFactory::Options options;
Far::PatchTablesFactory::Options options(level);
options.generateFVarTables = bits.test(MeshFVarData);
options.useSingleCreasePatch = bits.test(MeshUseSingleCreasePatch);
_patchTables = Far::PatchTablesFactory::Create(*_refiner, options);
@ -279,7 +280,7 @@ public:
{
assert(_refiner);
GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive));
GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive), bits.test(MeshUseSingleCreasePatch));
int numElements =
initializeVertexBuffers(numVertexElements, numVaryingElements, bits);

View File

@ -57,6 +57,14 @@ uniform mat4 Q = mat4(
0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
);
// Infinite sharp
uniform mat4 Mi = mat4(
1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
0.f, 4.f/6.f, 2.f/6.f, 0.f,
0.f, 2.f/6.f, 4.f/6.f, 0.f,
0.f, 0.f, 1.f, 0.f
);
// Boundary / Corner
uniform mat4x3 B = mat4x3(
1.f, 0.f, 0.f,
@ -74,11 +82,34 @@ in block {
out block {
ControlVertex v;
vec4 P1;
vec4 P2;
float sharpness;
OSD_USER_VARYING_DECLARE
} outpt[];
#define ID gl_InvocationID
// compute single-crease patch matrix
mat4
ComputeMatrixSimplified(float sharpness)
{
float s = pow(2.0f, sharpness);
float s2 = s*s;
float s3 = s2*s;
mat4 m = mat4(
0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
0, 1+s, 6*s - 2, 1-s,
0, 1, 6*s - 2, 1);
m /= (s*6.0);
m[0][0] = 1.0/6.0;
return m;
}
void main()
{
int i = ID%4;
@ -98,6 +129,8 @@ void main()
pos += B[j][k]*H[k];
}
outpt[ID].v.position = vec4(pos, 1.0);
#elif defined OSD_PATCH_CORNER
vec3 H[3];
for (int l=0; l<3; ++l) {
@ -112,6 +145,8 @@ void main()
pos += B[j][k]*H[k];
}
outpt[ID].v.position = vec4(pos, 1.0);
#else // not OSD_PATCH_BOUNDARY, not OSD_PATCH_CORNER
vec3 H[4];
for (int l=0; l<4; ++l) {
@ -121,14 +156,39 @@ void main()
}
}
#if defined OSD_PATCH_SINGLE_CREASE
float sharpness = GetSharpness();
float Sf = floor(sharpness);
float Sc = ceil(sharpness);
float Sr = fract(sharpness);
mat4 Mf = ComputeMatrixSimplified(Sf);
mat4 Mc = ComputeMatrixSimplified(Sc);
mat4 Mj = (1-Sr) * Mf + Sr * Mi;
mat4 Ms = (1-Sr) * Mf + Sr * Mc;
vec3 P = vec3(0);
vec3 P1 = vec3(0);
vec3 P2 = vec3(0);
for (int k=0; k<4; ++k) {
P += Mi[j][k]*H[k]; // 0 to 1-2^(-Sf)
P1 += Mj[j][k]*H[k]; // 1-2^(-Sf) to 1-2^(-Sc)
P2 += Ms[j][k]*H[k]; // 1-2^(-Sc) to 1
}
outpt[ID].v.position = vec4(P, 1.0);
outpt[ID].P1 = vec4(P1, 1.0);
outpt[ID].P2 = vec4(P2, 1.0);
outpt[ID].sharpness = sharpness;
#else // REGULAR
vec3 pos = vec3(0,0,0);
for (int k=0; k<4; ++k) {
pos += Q[j][k]*H[k];
}
outpt[ID].v.position = vec4(pos, 1.0);
#endif
#endif
outpt[ID].v.position = vec4(pos, 1.0);
#if defined OSD_PATCH_BOUNDARY
const int p[16] = int[]( 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 );
@ -215,6 +275,9 @@ void main()
in block {
ControlVertex v;
vec4 P1;
vec4 P2;
float sharpness;
OSD_USER_VARYING_DECLARE
} inpt[];
@ -244,8 +307,35 @@ void main()
Univar4x4(UV.x, B, D);
#endif
#if defined OSD_PATCH_SINGLE_CREASE
float sharpness = inpt[0].sharpness;
float s0 = 1.0 - pow(2.0f, -floor(sharpness));
float s1 = 1.0 - pow(2.0f, -ceil(sharpness));
#endif
for (int i=0; i<4; ++i) {
for (int j=0; j<4; ++j) {
#if defined OSD_PATCH_SINGLE_CREASE
#if OSD_TRANSITION_ROTATE == 1
int k = 4*(3-j) + i;
float s = 1-UV.x;
#elif OSD_TRANSITION_ROTATE == 2
int k = 4*(3-i) + (3-j);
float s = 1-UV.y;
#elif OSD_TRANSITION_ROTATE == 3
int k = 4*j + (3-i);
float s = UV.x;
#else // ROTATE=0 or non-transition
int k = 4*i + j;
float s = UV.y;
#endif
vec3 A = (s < s0) ?
inpt[k].v.position.xyz :
((s < s1) ?
inpt[k].P1.xyz :
inpt[k].P2.xyz);
#else // !SINGLE_CREASE
#if OSD_TRANSITION_ROTATE == 1
vec3 A = inpt[4*(3-j) + i].v.position.xyz;
#elif OSD_TRANSITION_ROTATE == 2
@ -254,6 +344,7 @@ void main()
vec3 A = inpt[4*j + (3-i)].v.position.xyz;
#else // OSD_TRANSITION_ROTATE == 0, or non-transition patch
vec3 A = inpt[4*i + j].v.position.xyz;
#endif
#endif
BUCP[i] += A * B[j];
DUCP[i] += A * D[j];

View File

@ -184,6 +184,9 @@ uniform isamplerBuffer OsdPatchParamBuffer;
#define GetPatchLevel() \
(texelFetch(OsdPatchParamBuffer, GetPrimitiveID()).y & 0xf)
#define GetSharpness() \
(intBitsToFloat(texelFetch(OsdPatchParamBuffer, GetPrimitiveID()).z))
#define OSD_COMPUTE_PTEX_COORD_TESSCONTROL_SHADER \
{ \
ivec2 ptexIndex = texelFetch(OsdPatchParamBuffer, \

View File

@ -137,8 +137,29 @@ SetTransitionTessLevels(vec3 cp[OSD_PATCH_INPUT_SIZE], int patchLevel)
vv0 = pv5;
vv1 = (pv5 + pv7) * 0.125 + pv6 * 0.75;
#endif
#elif defined OSD_PATCH_SINGLE_CREASE
// apply smooth, sharp or fractional-semisharp (linear interpolate) rules
float weight = min(1, GetSharpness());
// XXX: current rotation of single-crease patch is inconsistent
// to boundary patch. should be fixed.
#if OSD_TRANSITION_ROTATE == 2
vv0 = mix(vv0, (pv4 + pv6) * 0.125 + pv5 * 0.75, weight);
vv1 = mix(vv1, (pv5 + pv7) * 0.125 + pv6 * 0.75, weight);
#elif OSD_TRANSITION_ROTATE == 3
vv1 = mix(vv1, (pv2 + pv10) * 0.125 + pv6 * 0.75, weight);
vv2 = mix(vv2, (pv6 + pv14) * 0.125 + pv10 * 0.75, weight);
#elif OSD_TRANSITION_ROTATE == 0
vv2 = mix(vv2, (pv9 + pv11) * 0.125 + pv10 * 0.75, weight);
vv3 = mix(vv3, (pv8 + pv10) * 0.125 + pv9 * 0.75, weight);
#elif OSD_TRANSITION_ROTATE == 1
vv3 = mix(vv3, (pv5 + pv13) * 0.125 + pv9 * 0.75, weight);
vv0 = mix(vv0, (pv1 + pv9) * 0.125 + pv5 * 0.75, weight);
#endif
#endif
#ifdef OSD_TRANSITION_PATTERN00
gl_TessLevelOuter[0] = TessAdaptive(ev01, pv9) * 0.5;
gl_TessLevelOuter[1] = TessAdaptive(ev01, pv10) * 0.5;
@ -154,6 +175,7 @@ SetTransitionTessLevels(vec3 cp[OSD_PATCH_INPUT_SIZE], int patchLevel)
gl_TessLevelInner[0] =
(gl_TessLevelOuter[0] + gl_TessLevelOuter[1] + gl_TessLevelOuter[2]) * 0.25;
#endif
#ifdef OSD_TRANSITION_PATTERN02
gl_TessLevelOuter[0] = TessAdaptive(ev01, vv0);

View File

@ -47,11 +47,12 @@ namespace OPENSUBDIV_VERSION {
namespace Osd {
enum MeshBits {
MeshAdaptive = 0,
MeshInterleaveVarying = 1,
MeshPtexData = 2,
MeshFVarData = 3,
NUM_MESH_BITS = 4,
MeshAdaptive = 0,
MeshInterleaveVarying = 1,
MeshPtexData = 2,
MeshFVarData = 3,
MeshUseSingleCreasePatch = 4,
NUM_MESH_BITS = 5,
};
typedef std::bitset<NUM_MESH_BITS> MeshBitset;
@ -96,12 +97,12 @@ protected:
refiner.GetNumVerticesTotal();
}
static inline void refineMesh(Far::TopologyRefiner & refiner, int level, bool adaptive) {
static inline void refineMesh(Far::TopologyRefiner & refiner, int level, bool adaptive, bool singleCreasePatch) {
bool fullTopologyInLastLevel = refiner.GetNumFVarChannels()>0;
if (adaptive) {
refiner.RefineAdaptive(level, fullTopologyInLastLevel);
refiner.RefineAdaptive(level, fullTopologyInLastLevel, singleCreasePatch);
} else {
refiner.RefineUniform(level, fullTopologyInLastLevel);
}
@ -136,13 +137,13 @@ public:
assert(_refiner);
MeshInterface<DRAW_CONTEXT>::refineMesh(*_refiner, level, bits.test(MeshAdaptive));
MeshInterface<DRAW_CONTEXT>::refineMesh(*_refiner, level, bits.test(MeshAdaptive), bits.test(MeshUseSingleCreasePatch));
initializeVertexBuffers(numVertexElements, numVaryingElements, bits);
initializeComputeContext(numVertexElements, numVaryingElements);
initializeDrawContext(numVertexElements, bits);
initializeDrawContext(numVertexElements, level, bits);
}
Mesh(ComputeController * computeController,
@ -248,12 +249,13 @@ private:
delete varyingStencils;
}
void initializeDrawContext(int numElements, MeshBitset bits) {
void initializeDrawContext(int numElements, int level, MeshBitset bits) {
assert(_refiner and _vertexBuffer);
Far::PatchTablesFactory::Options options;
Far::PatchTablesFactory::Options options(level);
options.generateFVarTables = bits.test(MeshFVarData);
options.useSingleCreasePatch = bits.test(MeshUseSingleCreasePatch);
_patchTables = Far::PatchTablesFactory::Create(*_refiner);

View File

@ -782,7 +782,93 @@ Level::gatherQuadRegularCornerPatchVertices(
return 9;
}
bool
Level::isSingleCreasePatch(Index face, float *sharpnessOut, int *rotationOut) const {
// Note: this function is called twice for the same patch, at topologyRefiner and patchTablesFactory.
// we may want to cache the result to improve the Far performance.
// To do so, FTag needs to be extended to store isSingleCrease(bool), sharpness(float) and rotation(0-3).
//
Vtr::IndexArray const fVerts = this->getFaceVertices(face);
// the face has to be quad
if (fVerts.size() != 4) return false;
// if there's any corner vertex, return false.
for (int i = 0; i < fVerts.size(); ++i) {
if (this->getVertexSharpness(fVerts[i]) > 0)
return false;
}
// make sure there's only one edge with sharpness
Vtr::IndexArray const fEdges = this->getFaceEdges(face);
float sharpness = 0.0f;
int rotation = 0;
for (int i = 0; i < fEdges.size(); ++i) {
float s = this->getEdgeSharpness(fEdges[i]);
if (s > 0.0f) {
if (sharpness > 0.0f) {
// found more than one sharp edges.
return false;
}
sharpness = s;
rotation = i;
}
}
// rotation = 0
// | | | |
// ---5-----4-----15----14---
// | || | |
// | || | |
// ---6-----0-----3-----13---
// | || | |
// | || | |
// ---7-----1-----2-----12---
// | || | |
// | || | |
// ---8-----9-----10----11---
// | | | |
int v[4];
v[0] = fVerts[(0+rotation)%4]; // crease
v[1] = fVerts[(1+rotation)%4]; // crease
v[2] = fVerts[(2+rotation)%4]; // smooth
v[3] = fVerts[(3+rotation)%4]; // smooth
// check the edges around v[0], v[1]
for (int i = 0; i < 2; ++i) {
Vtr::IndexArray const vEdges = this->getVertexEdges(v[i]);
if (vEdges.size() != 4) return false;
int nSharpEdges = 0;
float sharpnesses[4];
for (int j = 0; j < 4; ++j) {
sharpnesses[j] = this->getEdgeSharpness(vEdges[j]);
if (sharpnesses[j] > 0.0f) {
if (++nSharpEdges == 3) return false;
}
}
// sharpnesses have to be [0, x, 0, x] or [x, 0, x, 0]
if (sharpnesses[0] != sharpnesses[2] or
sharpnesses[1] != sharpnesses[3]) {
return false;
}
}
// check the edges around v[2], v[3]
for (int i = 2; i < 4; ++i) {
Vtr::IndexArray const vEdges = this->getVertexEdges(v[i]);
if (vEdges.size() != 4) return false;
// all edges have to be smooth
for (int j = 0; j < 4; ++j) {
float sharpness = this->getEdgeSharpness(vEdges[j]);
if (sharpness > 0.0f) return false;
}
}
if (sharpnessOut) *sharpnessOut = sharpness;
if (rotationOut) *rotationOut = rotation;
return true;
}
//
// What follows is an internal/anonymous class and protected methods to complete all

View File

@ -238,6 +238,8 @@ public:
int gatherManifoldVertexRingFromIncidentQuads(Index vIndex, int vOffset, int ringVerts[]) const;
bool isSingleCreasePatch(Index face, float* sharpnessOut=NULL, int* rotationOut=NULL) const;
protected:
friend class Refinement;