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" #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.8f, 0.0f, 0.0f, 1.0f}, // boundary
{0.0f, 1.0f, 0.0f, 1.0f}, // corner {0.0f, 1.0f, 0.0f, 1.0f}, // corner
{1.0f, 1.0f, 0.0f, 1.0f}, // gregory {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 {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.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.0f, 0.75f, 1.0f}, // boundary pattern 0
{0.0f, 0.2f, 0.75f, 1.0f}, // boundary pattern 1 {0.0f, 0.2f, 0.75f, 1.0f}, // boundary pattern 1
{0.0f, 0.4f, 0.75f, 1.0f}, // boundary pattern 2 {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_VIEW_LOD,
kHUD_CB_FRACTIONAL_SPACING, kHUD_CB_FRACTIONAL_SPACING,
kHUD_CB_PATCH_CULL, 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; ObjAnim const * g_objAnim = 0;
@ -164,16 +165,18 @@ float g_animTime = 0;
int g_fullscreen = 0, int g_fullscreen = 0,
g_freeze = 0, g_freeze = 0,
g_displayStyle = kWireShaded, g_displayStyle = kWireShaded,
g_adaptive = 0, g_adaptive = 1,
g_singleCreasePatch = 1,
g_drawCageEdges = 1, g_drawCageEdges = 1,
g_drawCageVertices = 0, g_drawCageVertices = 0,
g_mbutton[3] = {0, 0, 0}, g_mbutton[3] = {0, 0, 0},
g_running = 1; g_running = 1;
int g_displayPatchColor = 1, int g_displayPatchColor = 1,
g_screenSpaceTess = 0, g_screenSpaceTess = 1,
g_fractionalSpacing = 0, g_fractionalSpacing = 1,
g_patchCull = 0; g_patchCull = 0,
g_displayPatchCounts = 0;
float g_rotate[2] = {0, 0}, float g_rotate[2] = {0, 0},
g_dolly = 5, 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 // Adaptive refinement currently supported only for catmull-clark scheme
bool doAdaptive = (g_adaptive!=0 and g_scheme==kCatmark), 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; OpenSubdiv::Osd::MeshBitset bits;
bits.set(OpenSubdiv::Osd::MeshAdaptive, doAdaptive); bits.set(OpenSubdiv::Osd::MeshAdaptive, doAdaptive);
bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, doSingleCreasePatch);
bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, interleaveVarying); bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, interleaveVarying);
bits.set(OpenSubdiv::Osd::MeshFVarData, g_displayStyle == kFaceVaryingColor); bits.set(OpenSubdiv::Osd::MeshFVarData, g_displayStyle == kFaceVaryingColor);
@ -1148,7 +1153,9 @@ display() {
g_mesh->GetDrawContext()->GetPatchArrays(); g_mesh->GetDrawContext()->GetPatchArrays();
// patch drawing // 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)); memset(patchCount, 0, sizeof(patchCount));
// primitive counting // primitive counting
@ -1169,6 +1176,7 @@ display() {
if (subPatch == 0) { if (subPatch == 0) {
patchCount[patchType][patchPattern][patchRotation] += patch.GetNumPatches(); patchCount[patchType][patchPattern][patchRotation] += patch.GetNumPatches();
} }
numTotalPatches += patch.GetNumPatches();
GLenum primType; GLenum primType;
@ -1223,6 +1231,7 @@ display() {
glDrawElements(primType, patch.GetNumIndices(), GL_UNSIGNED_INT, glDrawElements(primType, patch.GetNumIndices(), GL_UNSIGNED_INT,
(void *)(patch.GetVertIndex() * sizeof(unsigned int))); (void *)(patch.GetVertIndex() * sizeof(unsigned int)));
++numDrawCalls;
if (g_displayStyle == kWire) { if (g_displayStyle == kWire) {
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
} }
@ -1268,47 +1277,64 @@ display() {
double fps = 1.0/elapsed; double fps = 1.0/elapsed;
int x = -280; if (g_displayPatchCounts) {
g_hud.DrawString(x, -360, "NonPatch : %d", int x = -280;
patchCount[OpenSubdiv::Far::PatchTables::QUADS][0][0]); int y = -480;
g_hud.DrawString(x, -340, "Regular : %d", g_hud.DrawString(x, y, "NonPatch : %d",
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][0][0]); patchCount[OpenSubdiv::Far::PatchTables::QUADS][0][0]); y += 20;
g_hud.DrawString(x, -320, "Boundary : %d", g_hud.DrawString(x, y, "Regular : %d",
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][0][0]); patchCount[OpenSubdiv::Far::PatchTables::REGULAR][0][0]); y+= 20;
g_hud.DrawString(x, -300, "Corner : %d", g_hud.DrawString(x, y, "Boundary : %d",
patchCount[OpenSubdiv::Far::PatchTables::CORNER][0][0]); patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][0][0]); y+= 20;
g_hud.DrawString(x, -280, "Gregory : %d", g_hud.DrawString(x, y, "Corner : %d",
patchCount[OpenSubdiv::Far::PatchTables::GREGORY][0][0]); patchCount[OpenSubdiv::Far::PatchTables::CORNER][0][0]); y+= 20;
g_hud.DrawString(x, -260, "Boundary Gregory : %d", g_hud.DrawString(x, y, "Single Crease : %d",
patchCount[OpenSubdiv::Far::PatchTables::GREGORY_BOUNDARY][0][0]); patchCount[OpenSubdiv::Far::PatchTables::SINGLE_CREASE][0][0]); y+= 20;
g_hud.DrawString(x, -240, "Trans. Regular : %d %d %d %d %d", g_hud.DrawString(x, y, "Gregory : %d",
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN0][0], patchCount[OpenSubdiv::Far::PatchTables::GREGORY][0][0]); y+= 20;
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN1][0], g_hud.DrawString(x, y, "Boundary Gregory : %d",
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN2][0], patchCount[OpenSubdiv::Far::PatchTables::GREGORY_BOUNDARY][0][0]); y+= 20;
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN3][0], g_hud.DrawString(x, y, "Trans. Regular : %d %d %d %d %d",
patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN4][0]); patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN0][0],
for (int i=0; i < 5; i++) patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN1][0],
g_hud.DrawString(x, -220+i*20, "Trans. Boundary%d : %d %d %d %d", i, patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN2][0],
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][0], patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN3][0],
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][1], patchCount[OpenSubdiv::Far::PatchTables::REGULAR][OpenSubdiv::Far::PatchTables::PATTERN4][0]); y+= 20;
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][2], for (int i=0; i < 5; i++) {
patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][3]); g_hud.DrawString(x, y, "Trans. Boundary%d : %d %d %d %d", i,
for (int i=0; i < 5; i++) patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][0],
g_hud.DrawString(x, -100+i*20, "Trans. Corner%d : %d %d %d %d", i, patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][1],
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][0], patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][2],
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][1], patchCount[OpenSubdiv::Far::PatchTables::BOUNDARY][i+1][3]); y+= 20;
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][2], }
patchCount[OpenSubdiv::Far::PatchTables::CORNER][i+1][3]); 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); int y = -220;
g_hud.DrawString(10, -160, "Primitives : %d", numPrimsGenerated); g_hud.DrawString(10, y, "Tess level : %d", g_tessLevel); y+= 20;
g_hud.DrawString(10, -140, "Vertices : %d", g_mesh->GetNumVertices()); g_hud.DrawString(10, y, "Patches : %d", numTotalPatches); y+= 20;
g_hud.DrawString(10, -120, "Scheme : %s", g_scheme==kBilinear ? "BILINEAR" : (g_scheme == kLoop ? "LOOP" : "CATMARK")); g_hud.DrawString(10, y, "Draw calls : %d", numDrawCalls); y+= 20;
g_hud.DrawString(10, -100, "GPU Kernel : %.3f ms", g_gpuTime); g_hud.DrawString(10, y, "Primitives : %d", numPrimsGenerated); y+= 20;
g_hud.DrawString(10, -80, "CPU Kernel : %.3f ms", g_cpuTime); g_hud.DrawString(10, y, "Vertices : %d", g_mesh->GetNumVertices()); y+= 20;
g_hud.DrawString(10, -60, "GPU Draw : %.3f ms", drawGpuTime); g_hud.DrawString(10, y, "Scheme : %s", g_scheme==kBilinear ? "BILINEAR" : (g_scheme == kLoop ? "LOOP" : "CATMARK")); y+= 20;
g_hud.DrawString(10, -40, "CPU Draw : %.3f ms", drawCpuTime); g_hud.DrawString(10, y, "GPU Kernel : %.3f ms", g_gpuTime); y+= 20;
g_hud.DrawString(10, -20, "FPS : %3.1f", fps); 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(); 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 static void
callbackCheckBox(bool checked, int button) callbackCheckBox(bool checked, int button)
{ {
@ -1547,6 +1582,9 @@ callbackCheckBox(bool checked, int button)
case kHUD_CB_FREEZE: case kHUD_CB_FREEZE:
g_freeze = checked; g_freeze = checked;
break; break;
case kHUD_CB_DISPLAY_PATCH_COUNTS:
g_displayPatchCounts = checked;
break;
} }
} }
@ -1614,8 +1652,10 @@ initHUD()
g_hud.AddPullDownButton(compute_pulldown, "GLSL Compute", kGLSLCompute); g_hud.AddPullDownButton(compute_pulldown, "GLSL Compute", kGLSLCompute);
} }
#endif #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("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) { for (int i = 1; i < 11; ++i) {
char level[16]; char level[16];
@ -1628,6 +1668,8 @@ initHUD()
g_hud.AddPullDownButton(shapes_pulldown, g_defaultShapes[i].name.c_str(),i); 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); 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(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
min(inpt.edgeDistance[2], inpt.edgeDistance[3])); min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
#endif #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); float p = exp2(-2 * d * d);
#if defined(GEOMETRY_OUT_WIRE) #if defined(GEOMETRY_OUT_WIRE)

View File

@ -253,19 +253,23 @@ PatchTables::getAdaptiveCatmarkDescriptors() {
if (_descriptors.empty()) { if (_descriptors.empty()) {
_descriptors.reserve(50); _descriptors.reserve(71);
// non-transition patches // non-transition patches : 6
for (int i=REGULAR; i<=GREGORY_BOUNDARY; ++i) { for (int i=REGULAR; i<=GREGORY_BOUNDARY; ++i) {
_descriptors.push_back( Descriptor(i, NON_TRANSITION, 0) ); _descriptors.push_back( Descriptor(i, NON_TRANSITION, 0) );
} }
// transition patches // transition patches (1 + 4 * 3) * 5 = 65
for (int i=PATTERN0; i<=PATTERN4; ++i) { for (int i=PATTERN0; i<=PATTERN4; ++i) {
_descriptors.push_back( Descriptor(REGULAR, i, 0) ); _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) { for (int j=0; j<4; ++j) {
_descriptors.push_back( Descriptor(BOUNDARY, i, j) ); _descriptors.push_back( Descriptor(BOUNDARY, i, j) );
} }

View File

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

View File

@ -46,6 +46,7 @@ struct PatchTypes {
NUM_ROTATIONS=4; NUM_ROTATIONS=4;
TYPE R[NUM_TRANSITIONS], // regular patch TYPE R[NUM_TRANSITIONS], // regular patch
S[NUM_TRANSITIONS][NUM_ROTATIONS], // single-crease patch
B[NUM_TRANSITIONS][NUM_ROTATIONS], // boundary patch (4 rotations) B[NUM_TRANSITIONS][NUM_ROTATIONS], // boundary patch (4 rotations)
C[NUM_TRANSITIONS][NUM_ROTATIONS], // corner patch (4 rotations) C[NUM_TRANSITIONS][NUM_ROTATIONS], // corner patch (4 rotations)
G, // gregory patch G, // gregory patch
@ -57,6 +58,7 @@ struct PatchTypes {
TYPE & getValue( PatchTables::Descriptor desc ) { TYPE & getValue( PatchTables::Descriptor desc ) {
switch (desc.GetType()) { switch (desc.GetType()) {
case PatchTables::REGULAR : return R[desc.GetPattern()]; 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::BOUNDARY : return B[desc.GetPattern()][desc.GetRotation()];
case PatchTables::CORNER : return C[desc.GetPattern()][desc.GetRotation()]; case PatchTables::CORNER : return C[desc.GetPattern()][desc.GetRotation()];
case PatchTables::GREGORY : return G; case PatchTables::GREGORY : return G;
@ -76,6 +78,7 @@ struct PatchTypes {
if (R[i]) ++result; if (R[i]) ++result;
for (int j=0; j<4; ++j) { for (int j=0; j<4; ++j) {
if (S[i][j]) ++result;
if (B[i][j]) ++result; if (B[i][j]) ++result;
if (C[i][j]) ++result; if (C[i][j]) ++result;
@ -85,10 +88,21 @@ struct PatchTypes {
if (GB) ++result; if (GB) ++result;
return 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<Index *> PatchCVPointers;
typedef PatchTypes<PatchParam *> PatchParamPointers; typedef PatchTypes<PatchParam *> PatchParamPointers;
typedef PatchTypes<Index *> SharpnessIndexPointers;
typedef PatchTypes<int> PatchCounters; typedef PatchTypes<int> PatchCounters;
typedef PatchTypes<Index **> PatchFVarPointers; typedef PatchTypes<Index **> PatchFVarPointers;
@ -131,6 +145,7 @@ public:
unsigned int _boundaryIndex : 2; unsigned int _boundaryIndex : 2;
unsigned int _boundaryCount : 3; unsigned int _boundaryCount : 3;
unsigned int _hasBoundaryEdge : 3; unsigned int _hasBoundaryEdge : 3;
unsigned int _isSingleCrease : 1;
void clear() { std::memset(this, 0, sizeof(*this)); } 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) { void assignTransitionPropertiesFromEdgeMask(int transitionEdgeMask) {
// //
// Note the transition rotations will be a function of the boundary rotations, and // 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... // results below are a function of both transition and boundary properties...
if (transitionEdgeMask == 0) { if (transitionEdgeMask == 0) {
_transitionRot = 0; _transitionRot = 0;
} else if (_boundaryCount == 0 and _isSingleCrease) {
assignTransitionRotationForSingleCrease(transitionEdgeMask);
} else if (_boundaryCount == 0) { } else if (_boundaryCount == 0) {
// XXXX manuelk Rotations are mostly a direct map of the transitionEdgeMask // XXXX manuelk Rotations are mostly a direct map of the transitionEdgeMask
// Except for: // Except for:
@ -335,7 +385,7 @@ namespace {
// Reserves tables based on the contents of the PatchArrayVector in the PatchTables: // Reserves tables based on the contents of the PatchArrayVector in the PatchTables:
// //
void void
PatchTablesFactory::allocateTables(PatchTables * tables, int /* nlevels */) { PatchTablesFactory::allocateTables(PatchTables * tables, int /* nlevels */, bool hasSharpness) {
PatchTables::PatchArrayVector const & parrays = tables->GetPatchArrayVector(); PatchTables::PatchArrayVector const & parrays = tables->GetPatchArrayVector();
@ -352,6 +402,10 @@ PatchTablesFactory::allocateTables(PatchTables * tables, int /* nlevels */) {
tables->_patches.resize( nverts ); tables->_patches.resize( nverts );
tables->_paramTable.resize( npatches ); tables->_paramTable.resize( npatches );
if (hasSharpness) {
tables->_sharpnessIndexTable.resize( npatches );
}
} }
PatchTables::FVarPatchTables * 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 // 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, // 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 // Allocate various tables
allocateTables( tables, 0 ); allocateTables( tables, 0, /*hasSharpness=*/false );
if (options.generateFVarTables) { if (options.generateFVarTables) {
tables->_fvarPatchTables = allocateFVarTables( refiner, *tables, options ); tables->_fvarPatchTables = allocateFVarTables( refiner, *tables, options );
@ -701,7 +769,7 @@ PatchTablesFactory::createAdaptive( TopologyRefiner const & refiner, Options opt
PatchCounters patchInventory; PatchCounters patchInventory;
std::vector<PatchFaceTag> patchTags; 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 // 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(); tables->_numPtexFaces = refiner.GetNumPtexFaces();
// Allocate various tables // Allocate various tables
allocateTables( tables, 0 ); bool hasSharpness = patchInventory.hasSingleCreasedPatches();
allocateTables( tables, 0, hasSharpness );
if (options.generateFVarTables) { if (options.generateFVarTables) {
tables->_fvarPatchTables = allocateFVarTables( refiner, *tables, options ); tables->_fvarPatchTables = allocateFVarTables( refiner, *tables, options );
@ -743,7 +812,7 @@ PatchTablesFactory::createAdaptive( TopologyRefiner const & refiner, Options opt
// //
// Now populate the patches: // Now populate the patches:
// //
populateAdaptivePatches(refiner, patchInventory, patchTags, tables); populateAdaptivePatches(refiner, patchInventory, patchTags, tables, options);
return tables; return tables;
} }
@ -755,9 +824,9 @@ PatchTablesFactory::createAdaptive( TopologyRefiner const & refiner, Options opt
// //
void void
PatchTablesFactory::identifyAdaptivePatches( TopologyRefiner const & refiner, PatchTablesFactory::identifyAdaptivePatches( TopologyRefiner const & refiner,
PatchCounters & patchInventory, PatchCounters & patchInventory,
PatchTagVector & patchTags ) { PatchTagVector & patchTags,
Options options ) {
// //
// Iterate through the levels of refinement to inspect and tag components with information // 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 // 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; int boundaryEdgeMask = 0;
bool hasBoundaryVertex = compFaceVertTag._boundary; 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) { if (hasBoundaryVertex) {
Vtr::IndexArray const& fEdges = level->getFaceEdges(faceIndex); Vtr::IndexArray const& fEdges = level->getFaceEdges(faceIndex);
@ -924,8 +1011,10 @@ PatchTablesFactory::identifyAdaptivePatches( TopologyRefiner const & refiner,
int transIndex = patchTag._transitionType; int transIndex = patchTag._transitionType;
int transRot = patchTag._transitionRot; int transRot = patchTag._transitionRot;
if (patchTag._boundaryCount == 0) { if (!patchTag._isSingleCrease && patchTag._boundaryCount == 0) {
patchInventory.R[transIndex]++; patchInventory.R[transIndex]++;
} else if (patchTag._isSingleCrease && patchTag._boundaryCount == 0) {
patchInventory.S[transIndex][transRot]++;
} else if (patchTag._boundaryCount == 1) { } else if (patchTag._boundaryCount == 1) {
patchInventory.B[transIndex][transRot]++; patchInventory.B[transIndex][transRot]++;
} else { } else {
@ -952,7 +1041,8 @@ void
PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner, PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
PatchCounters const & patchInventory, PatchCounters const & patchInventory,
PatchTagVector const & patchTags, PatchTagVector const & patchTags,
PatchTables * tables ) { PatchTables * tables,
Options options ) {
// //
// Setup convenience pointers at the beginning of each patch array for each // Setup convenience pointers at the beginning of each patch array for each
@ -961,6 +1051,7 @@ PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
PatchCVPointers iptrs; PatchCVPointers iptrs;
PatchParamPointers pptrs; PatchParamPointers pptrs;
PatchFVarPointers fptrs; PatchFVarPointers fptrs;
SharpnessIndexPointers sptrs;
typedef PatchTables::DescriptorVector DescVec; typedef PatchTables::DescriptorVector DescVec;
@ -974,6 +1065,9 @@ PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
iptrs.getValue( *it ) = &tables->_patches[pa->GetVertIndex()]; iptrs.getValue( *it ) = &tables->_patches[pa->GetVertIndex()];
pptrs.getValue( *it ) = &tables->_paramTable[pa->GetPatchIndex()]; pptrs.getValue( *it ) = &tables->_paramTable[pa->GetPatchIndex()];
if (patchInventory.hasSingleCreasedPatches()) {
sptrs.getValue( *it ) = &tables->_sharpnessIndexTable[pa->GetPatchIndex()];
}
if (tables->_fvarPatchTables) { if (tables->_fvarPatchTables) {
int nchannels = refiner.GetNumFVarChannels(), int nchannels = refiner.GetNumFVarChannels(),
@ -1031,7 +1125,7 @@ PatchTablesFactory::populateAdaptivePatches( TopologyRefiner const & refiner,
int rIndex = patchTag._transitionRot; int rIndex = patchTag._transitionRot;
int bIndex = patchTag._boundaryIndex; 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 }; 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); 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 // It may be that a separate "face rotation" flag is warranted if we need something
// else dependent on the boundary orientation. // 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 }; int const permuteBoundary[12] = { 11, 3, 0, 4, 10, 2, 1, 5, 9, 8, 7, 6 };
level->gatherQuadRegularBoundaryPatchVertices(faceIndex, patchVerts, bIndex); level->gatherQuadRegularBoundaryPatchVertices(faceIndex, patchVerts, bIndex);

View File

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

View File

@ -41,7 +41,8 @@ TopologyRefiner::TopologyRefiner(Sdc::Type schemeType, Sdc::Options schemeOption
_subdivType(schemeType), _subdivType(schemeType),
_subdivOptions(schemeOptions), _subdivOptions(schemeOptions),
_isUniform(true), _isUniform(true),
_maxLevel(0) { _maxLevel(0),
_useSingleCreasePatch(false) {
// Need to revisit allocation scheme here -- want to use smart-ptrs for these // Need to revisit allocation scheme here -- want to use smart-ptrs for these
// but will probably have to settle for explicit new/delete... // but will probably have to settle for explicit new/delete...
@ -318,7 +319,7 @@ TopologyRefiner::RefineUniform(int maxLevel, bool fullTopology) {
void 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(_levels[0].getNumVertices() > 0); // Make sure the base level has been initialized
assert(_subdivType == Sdc::TYPE_CATMARK); assert(_subdivType == Sdc::TYPE_CATMARK);
@ -328,6 +329,7 @@ TopologyRefiner::RefineAdaptive(int subdivLevel, bool fullTopology) {
// //
_isUniform = false; _isUniform = false;
_maxLevel = subdivLevel; _maxLevel = subdivLevel;
_useSingleCreasePatch = useSingleCreasePatch;
// Should we presize all or grow one at a time as needed? // Should we presize all or grow one at a time as needed?
_levels.resize(subdivLevel + 1); _levels.resize(subdivLevel + 1);
@ -435,7 +437,7 @@ TopologyRefiner::catmarkFeatureAdaptiveSelector(Vtr::SparseSelector& selector) {
} }
bool selectFace = false; bool selectFace = false;
if (compFaceTag._xordinary || compFaceTag._semiSharp) { if (compFaceTag._xordinary) {
selectFace = true; selectFace = true;
} else if (compFaceTag._rule & Sdc::Crease::RULE_DART) { } else if (compFaceTag._rule & Sdc::Crease::RULE_DART) {
// Get this case out of the way before testing hard features // 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 // though some may be regular patches, this currently warrants isolation as we only
// support regular patches with one corner or one boundary. // support regular patches with one corner or one boundary.
selectFace = true; 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 { } else {
// This leaves us with at least one Smooth vertex (and so two smooth adjacent edges // 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 // 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 /// @param fullTopologyInLastLevel Skip secondary topological relationships
/// at the highest level of refinement. /// 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) /// \brief Unrefine the topology (keep control cage)
void Unrefine(); void Unrefine();
@ -496,6 +498,7 @@ private:
bool _isUniform; bool _isUniform;
int _maxLevel; int _maxLevel;
bool _useSingleCreasePatch;
std::vector<Vtr::Level> _levels; std::vector<Vtr::Level> _levels;
std::vector<Vtr::Refinement> _refinements; std::vector<Vtr::Refinement> _refinements;

View File

@ -167,8 +167,27 @@ GLDrawContext::create(Far::PatchTables const & patchTables, int numVertexElement
Far::PatchTables::PatchParamTable const & Far::PatchTables::PatchParamTable const &
patchParamTables = patchTables.GetPatchParamTable(); patchParamTables = patchTables.GetPatchParamTable();
if (not patchParamTables.empty()) if (not patchParamTables.empty()) {
_patchParamTextureBuffer = createTextureBuffer(patchParamTables, GL_RG32I); 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); glBindBuffer(GL_TEXTURE_BUFFER, 0);
#endif #endif

View File

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

View File

@ -68,14 +68,14 @@ public:
_drawContext(0) _drawContext(0)
{ {
GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive)); GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive), bits.test(MeshUseSingleCreasePatch));
int numElements = int numElements =
initializeVertexBuffers(numVertexElements, numVaryingElements, bits); initializeVertexBuffers(numVertexElements, numVaryingElements, bits);
initializeComputeContext(numVertexElements, numVaryingElements); initializeComputeContext(numVertexElements, numVaryingElements);
initializeDrawContext(numElements, bits); initializeDrawContext(numElements, level, bits);
} }
Mesh(ComputeController * computeController, Mesh(ComputeController * computeController,
@ -201,12 +201,13 @@ private:
delete varyingStencils; delete varyingStencils;
} }
void initializeDrawContext(int numElements, MeshBitset bits) { void initializeDrawContext(int numElements, int level, MeshBitset bits) {
assert(_refiner and _vertexBuffer); assert(_refiner and _vertexBuffer);
Far::PatchTablesFactory::Options options; Far::PatchTablesFactory::Options options(level);
options.generateFVarTables = bits.test(MeshFVarData); options.generateFVarTables = bits.test(MeshFVarData);
options.useSingleCreasePatch = bits.test(MeshUseSingleCreasePatch);
_patchTables = Far::PatchTablesFactory::Create(*_refiner, options); _patchTables = Far::PatchTablesFactory::Create(*_refiner, options);
@ -279,7 +280,7 @@ public:
{ {
assert(_refiner); assert(_refiner);
GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive)); GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive), bits.test(MeshUseSingleCreasePatch));
int numElements = int numElements =
initializeVertexBuffers(numVertexElements, numVaryingElements, bits); 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 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 // Boundary / Corner
uniform mat4x3 B = mat4x3( uniform mat4x3 B = mat4x3(
1.f, 0.f, 0.f, 1.f, 0.f, 0.f,
@ -74,11 +82,34 @@ in block {
out block { out block {
ControlVertex v; ControlVertex v;
vec4 P1;
vec4 P2;
float sharpness;
OSD_USER_VARYING_DECLARE OSD_USER_VARYING_DECLARE
} outpt[]; } outpt[];
#define ID gl_InvocationID #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() void main()
{ {
int i = ID%4; int i = ID%4;
@ -98,6 +129,8 @@ void main()
pos += B[j][k]*H[k]; pos += B[j][k]*H[k];
} }
outpt[ID].v.position = vec4(pos, 1.0);
#elif defined OSD_PATCH_CORNER #elif defined OSD_PATCH_CORNER
vec3 H[3]; vec3 H[3];
for (int l=0; l<3; ++l) { for (int l=0; l<3; ++l) {
@ -112,6 +145,8 @@ void main()
pos += B[j][k]*H[k]; pos += B[j][k]*H[k];
} }
outpt[ID].v.position = vec4(pos, 1.0);
#else // not OSD_PATCH_BOUNDARY, not OSD_PATCH_CORNER #else // not OSD_PATCH_BOUNDARY, not OSD_PATCH_CORNER
vec3 H[4]; vec3 H[4];
for (int l=0; l<4; ++l) { 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); vec3 pos = vec3(0,0,0);
for (int k=0; k<4; ++k) { for (int k=0; k<4; ++k) {
pos += Q[j][k]*H[k]; pos += Q[j][k]*H[k];
} }
outpt[ID].v.position = vec4(pos, 1.0);
#endif
#endif #endif
outpt[ID].v.position = vec4(pos, 1.0);
#if defined OSD_PATCH_BOUNDARY #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 ); 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 { in block {
ControlVertex v; ControlVertex v;
vec4 P1;
vec4 P2;
float sharpness;
OSD_USER_VARYING_DECLARE OSD_USER_VARYING_DECLARE
} inpt[]; } inpt[];
@ -244,8 +307,35 @@ void main()
Univar4x4(UV.x, B, D); Univar4x4(UV.x, B, D);
#endif #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 i=0; i<4; ++i) {
for (int j=0; j<4; ++j) { 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 #if OSD_TRANSITION_ROTATE == 1
vec3 A = inpt[4*(3-j) + i].v.position.xyz; vec3 A = inpt[4*(3-j) + i].v.position.xyz;
#elif OSD_TRANSITION_ROTATE == 2 #elif OSD_TRANSITION_ROTATE == 2
@ -254,6 +344,7 @@ void main()
vec3 A = inpt[4*j + (3-i)].v.position.xyz; vec3 A = inpt[4*j + (3-i)].v.position.xyz;
#else // OSD_TRANSITION_ROTATE == 0, or non-transition patch #else // OSD_TRANSITION_ROTATE == 0, or non-transition patch
vec3 A = inpt[4*i + j].v.position.xyz; vec3 A = inpt[4*i + j].v.position.xyz;
#endif
#endif #endif
BUCP[i] += A * B[j]; BUCP[i] += A * B[j];
DUCP[i] += A * D[j]; DUCP[i] += A * D[j];

View File

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

View File

@ -137,8 +137,29 @@ SetTransitionTessLevels(vec3 cp[OSD_PATCH_INPUT_SIZE], int patchLevel)
vv0 = pv5; vv0 = pv5;
vv1 = (pv5 + pv7) * 0.125 + pv6 * 0.75; vv1 = (pv5 + pv7) * 0.125 + pv6 * 0.75;
#endif #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
#endif
#ifdef OSD_TRANSITION_PATTERN00 #ifdef OSD_TRANSITION_PATTERN00
gl_TessLevelOuter[0] = TessAdaptive(ev01, pv9) * 0.5; gl_TessLevelOuter[0] = TessAdaptive(ev01, pv9) * 0.5;
gl_TessLevelOuter[1] = TessAdaptive(ev01, pv10) * 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_TessLevelInner[0] =
(gl_TessLevelOuter[0] + gl_TessLevelOuter[1] + gl_TessLevelOuter[2]) * 0.25; (gl_TessLevelOuter[0] + gl_TessLevelOuter[1] + gl_TessLevelOuter[2]) * 0.25;
#endif #endif
#ifdef OSD_TRANSITION_PATTERN02 #ifdef OSD_TRANSITION_PATTERN02
gl_TessLevelOuter[0] = TessAdaptive(ev01, vv0); gl_TessLevelOuter[0] = TessAdaptive(ev01, vv0);

View File

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

View File

@ -782,7 +782,93 @@ Level::gatherQuadRegularCornerPatchVertices(
return 9; 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 // 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; int gatherManifoldVertexRingFromIncidentQuads(Index vIndex, int vOffset, int ringVerts[]) const;
bool isSingleCreasePatch(Index face, float* sharpnessOut=NULL, int* rotationOut=NULL) const;
protected: protected:
friend class Refinement; friend class Refinement;