From 8987e091021292f22aa3550605ca372419a598ab Mon Sep 17 00:00:00 2001 From: "David G. Yu" Date: Tue, 20 Oct 2015 19:48:51 -0700 Subject: [PATCH] Restored support for fractional tessellation This change includes support for both fractional_even_spacing and fractional_odd_spacing. The implementation follows the existing pattern of re-parameterizing the tessellation domain only along transition boundary edges. This allows for crack-free tessellation, but it might be better to consistently re-parameterize all of the outer edges of all patches, which also would be required for numerically watertight tessellation. This is implemented in a way that requires no changes to the client shader API. It should be more efficient to move some computations to the control/hull shaders and reduce divergence in the execution of eval/domain shaders. --- opensubdiv/osd/glslPatchCommon.glsl | 231 ++++++++++++++++++++++------ opensubdiv/osd/hlslPatchCommon.hlsl | 231 ++++++++++++++++++++++------ 2 files changed, 376 insertions(+), 86 deletions(-) diff --git a/opensubdiv/osd/glslPatchCommon.glsl b/opensubdiv/osd/glslPatchCommon.glsl index 3c78827e..85507ca2 100644 --- a/opensubdiv/osd/glslPatchCommon.glsl +++ b/opensubdiv/osd/glslPatchCommon.glsl @@ -72,14 +72,10 @@ // mix(input[c].var, input[d].var, UV.x), UV.y) #endif -// XXXdyu-patch-drawing support for fractional spacing -#undef OSD_FRACTIONAL_ODD_SPACING -#undef OSD_FRACTIONAL_EVEN_SPACING - -#if defined OSD_FRACTIONAL_ODD_SPACING - #define OSD_SPACING fractional_odd_spacing -#elif defined OSD_FRACTIONAL_EVEN_SPACING +#if defined OSD_FRACTIONAL_EVEN_SPACING #define OSD_SPACING fractional_even_spacing +#elif defined OSD_FRACTIONAL_ODD_SPACING + #define OSD_SPACING fractional_odd_spacing #else #define OSD_SPACING equal_spacing #endif @@ -621,7 +617,7 @@ float OsdComputeTessLevel(vec3 p0, vec3 p1) vec3 center = (p0 + p1) / 2.0; float diameter = distance(p0, p1); float projLength = OsdComputePostProjectionSphereExtent(center, diameter); - float tessLevel = round(max(1.0, OsdTessLevel() * projLength)); + float tessLevel = max(1.0, OsdTessLevel() * projLength); // We restrict adaptive tessellation levels to half of the device // supported maximum because transition edges are split into two @@ -799,6 +795,97 @@ OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16], #endif } +// Round up to the nearest even integer +float OsdRoundUpEven(float x) { + return 2*ceil(x/2); +} + +// Round up to the nearest odd integer +float OsdRoundUpOdd(float x) { + return 2*ceil((x+1)/2)-1; +} + +// Compute outer and inner tessellation levels taking into account the +// current tessellation spacing mode. +void +OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi, + out vec4 tessLevelOuter, out vec2 tessLevelInner) +{ + // Outer levels are the sum of the Lo and Hi segments where the Hi + // segments will have lengths of zero for non-transition edges. + +#if defined OSD_FRACTIONAL_EVEN_SPACING + // Combine fractional outer transition edge levels before rounding. + vec4 combinedOuter = tessOuterLo + tessOuterHi; + + // Round the segments of transition edges separately. We will recover the + // fractional parameterization of transition edges after tessellation. + + tessLevelOuter = combinedOuter; + if (tessOuterHi[0] > 0) { + tessLevelOuter[0] = + OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]); + } + if (tessOuterHi[1] > 0) { + tessLevelOuter[1] = + OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]); + } + if (tessOuterHi[2] > 0) { + tessLevelOuter[2] = + OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]); + } + if (tessOuterHi[3] > 0) { + tessLevelOuter[3] = + OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]); + } +#elif defined OSD_FRACTIONAL_ODD_SPACING + // Combine fractional outer transition edge levels before rounding. + vec4 combinedOuter = tessOuterLo + tessOuterHi; + + // Round the segments of transition edges separately. We will recover the + // fractional parameterization of transition edges after tessellation. + // + // The sum of the two outer odd segment lengths will be an even number + // which the tessellator will increase by +1 so that there will be a + // total odd number of segments. We clamp the combinedOuter tess levels + // (used to compute the inner tess levels) so that the outer transition + // edges will be sampled without degenerate triangles. + + tessLevelOuter = combinedOuter; + if (tessOuterHi[0] > 0) { + tessLevelOuter[0] = + OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]); + combinedOuter = max(vec4(3), combinedOuter); + } + if (tessOuterHi[1] > 0) { + tessLevelOuter[1] = + OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]); + combinedOuter = max(vec4(3), combinedOuter); + } + if (tessOuterHi[2] > 0) { + tessLevelOuter[2] = + OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]); + combinedOuter = max(vec4(3), combinedOuter); + } + if (tessOuterHi[3] > 0) { + tessLevelOuter[3] = + OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]); + combinedOuter = max(vec4(3), combinedOuter); + } +#else + // Round equally spaced transition edge levels before combining. + tessOuterLo = round(tessOuterLo); + tessOuterHi = round(tessOuterHi); + + vec4 combinedOuter = tessOuterLo + tessOuterHi; + tessLevelOuter = combinedOuter; +#endif + + // Inner levels are the averages the corresponding outer levels. + tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5; + tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5; +} + void OsdGetTessLevelsUniform(ivec3 patchParam, out vec4 tessLevelOuter, out vec2 tessLevelInner, @@ -807,13 +894,8 @@ OsdGetTessLevelsUniform(ivec3 patchParam, // uniform tessellation OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi); - // Outer levels are the sum of the Lo and Hi segments where the Hi - // segments will have a length of zero for non-transition edges. - tessLevelOuter = tessOuterLo + tessOuterHi; - - // Inner levels are the average the corresponding outer levels. - tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5; - tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5; + OsdComputeTessLevels(tessOuterLo, tessOuterHi, + tessLevelOuter, tessLevelInner); } void @@ -821,15 +903,11 @@ OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam, out vec4 tessLevelOuter, out vec2 tessLevelInner, out vec4 tessOuterLo, out vec4 tessOuterHi) { - OsdGetTessLevelsRefinedPoints(cpRefined, patchParam, tessOuterLo, tessOuterHi); + OsdGetTessLevelsRefinedPoints(cpRefined, patchParam, + tessOuterLo, tessOuterHi); - // Outer levels are the sum of the Lo and Hi segments where the Hi - // segments will have a length of zero for non-transition edges. - tessLevelOuter = tessOuterLo + tessOuterHi; - - // Inner levels are the average the corresponding outer levels. - tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5; - tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5; + OsdComputeTessLevels(tessOuterLo, tessOuterHi, + tessLevelOuter, tessLevelInner); } void @@ -838,15 +916,11 @@ OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16], out vec4 tessLevelOuter, out vec2 tessLevelInner, out vec4 tessOuterLo, out vec4 tessOuterHi) { - OsdGetTessLevelsLimitPoints(cpBezier, patchParam, tessOuterLo, tessOuterHi); + OsdGetTessLevelsLimitPoints(cpBezier, patchParam, + tessOuterLo, tessOuterHi); - // Outer levels are the sum of the Lo and Hi segments where the Hi - // segments will have a length of zero for non-transition edges. - tessLevelOuter = tessOuterLo + tessOuterHi; - - // Inner levels are the average the corresponding outer levels. - tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5; - tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5; + OsdComputeTessLevels(tessOuterLo, tessOuterHi, + tessLevelOuter, tessLevelInner); } void @@ -867,26 +941,97 @@ OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3, OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi); #endif - // Outer levels are the sum of the Lo and Hi segments where the Hi - // segments will have a length of zero for non-transition edges. - tessLevelOuter = tessOuterLo + tessOuterHi; - - // Inner levels are the average the corresponding outer levels. - tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5; - tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5; + OsdComputeTessLevels(tessOuterLo, tessOuterHi, + tessLevelOuter, tessLevelInner); } +#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING float -OsdGetTessTransitionSplit(float t, float n0, float n1) +OsdGetTessFractionalSplit(float t, float level, float levelUp) { - float ti = round(t * (n0 + n1)); + // Fractional tessellation of an edge will produce n segments where n + // is the tessellation level of the edge (level) rounded up to the + // nearest even or odd integer (levelUp). There will be n-2 segments of + // equal length (dx1) and two additional segments of equal length (dx0) + // that are typically shorter than the other segments. The two additional + // segments should be placed symmetrically on opposite sides of the + // edge (offset). - if (ti <= n0) { - return 0.5 * (ti / n0); +#if defined OSD_FRACTIONAL_EVEN_SPACING + if (level <= 2) return t; + + float base = pow(2.0,floor(log2(levelUp))); + float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1)); + +#elif defined OSD_FRACTIONAL_ODD_SPACING + if (level <= 1) return t; + + float base = pow(2.0,floor(log2(levelUp))); + float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1); +#endif + + float dx0 = (1.0 - (levelUp-level)/2) / levelUp; + float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0)); + + if (t < 0.5) { + float x = levelUp/2 - round(t*levelUp); + return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1)); + } else if (t > 0.5) { + float x = round(t*levelUp) - levelUp/2; + return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1)); } else { - return 0.5 * ((ti - n0) / n1) + 0.5; + return t; } } +#endif + +float +OsdGetTessTransitionSplit(float t, float lo, float hi) +{ +#if defined OSD_FRACTIONAL_EVEN_SPACING + float loRoundUp = OsdRoundUpEven(lo); + float hiRoundUp = OsdRoundUpEven(hi); + + // Convert the parametric t into a segment index along the combined edge. + float ti = round(t * (loRoundUp + hiRoundUp)); + + if (ti <= loRoundUp) { + float t0 = ti / loRoundUp; + return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5; + } else { + float t1 = (ti - loRoundUp) / hiRoundUp; + return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5; + } +#elif defined OSD_FRACTIONAL_ODD_SPACING + float loRoundUp = OsdRoundUpOdd(lo); + float hiRoundUp = OsdRoundUpOdd(hi); + + // Convert the parametric t into a segment index along the combined edge. + // The +1 below is to account for the extra segment produced by the + // tessellator since the sum of two odd tess levels will be rounded + // up by one to the next odd integer tess level. + float ti = round(t * (loRoundUp + hiRoundUp + 1)); + + if (ti <= loRoundUp) { + float t0 = ti / loRoundUp; + return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5; + } else if (ti > (loRoundUp+1)) { + float t1 = (ti - (loRoundUp+1)) / hiRoundUp; + return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5; + } else { + return 0.5; + } +#else + // Convert the parametric t into a segment index along the combined edge. + float ti = round(t * (lo + hi)); + + if (ti <= lo) { + return (ti / lo) * 0.5; + } else { + return ((ti - lo) / hi) * 0.5 + 0.5; + } +#endif +} vec2 OsdGetTessParameterization(vec2 uv, vec4 tessOuterLo, vec4 tessOuterHi) diff --git a/opensubdiv/osd/hlslPatchCommon.hlsl b/opensubdiv/osd/hlslPatchCommon.hlsl index e62c96a7..83391c93 100644 --- a/opensubdiv/osd/hlslPatchCommon.hlsl +++ b/opensubdiv/osd/hlslPatchCommon.hlsl @@ -26,14 +26,10 @@ // Patches.Common //---------------------------------------------------------- -// XXXdyu-patch-drawing support for fractional spacing -#undef OSD_FRACTIONAL_ODD_SPACING -#undef OSD_FRACTIONAL_EVEN_SPACING - -#if defined OSD_FRACTIONAL_ODD_SPACING - #define OSD_PARTITIONING "fractional_odd" -#elif defined OSD_FRACTIONAL_EVEN_SPACING +#if defined OSD_FRACTIONAL_EVEN_SPACING #define OSD_PARTITIONING "fractional_even" +#elif defined OSD_FRACTIONAL_ODD_SPACING + #define OSD_PARTITIONING "fractional_odd" #else #define OSD_PARTITIONING "integer" #endif @@ -494,7 +490,7 @@ float OsdComputeTessLevel(float3 p0, float3 p1) float3 center = (p0 + p1) / 2.0; float diameter = distance(p0, p1); float projLength = OsdComputePostProjectionSphereExtent(center, diameter); - float tessLevel = round(max(1.0, OsdTessLevel() * projLength)); + float tessLevel = max(1.0, OsdTessLevel() * projLength); // We restrict adaptive tessellation levels to half of the device // supported maximum because transition edges are split into two @@ -674,6 +670,97 @@ OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16], #endif } +// Round up to the nearest even integer +float OsdRoundUpEven(float x) { + return 2*ceil(x/2); +} + +// Round up to the nearest odd integer +float OsdRoundUpOdd(float x) { + return 2*ceil((x+1)/2)-1; +} + +// Compute outer and inner tessellation levels taking into account the +// current tessellation spacing mode. +void +OsdComputeTessLevels(inout float4 tessOuterLo, inout float4 tessOuterHi, + out float4 tessLevelOuter, out float2 tessLevelInner) +{ + // Outer levels are the sum of the Lo and Hi segments where the Hi + // segments will have lengths of zero for non-transition edges. + +#if defined OSD_FRACTIONAL_EVEN_SPACING + // Combine fractional outer transition edge levels before rounding. + float4 combinedOuter = tessOuterLo + tessOuterHi; + + // Round the segments of transition edges separately. We will recover the + // fractional parameterization of transition edges after tessellation. + + tessLevelOuter = combinedOuter; + if (tessOuterHi[0] > 0) { + tessLevelOuter[0] = + OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]); + } + if (tessOuterHi[1] > 0) { + tessLevelOuter[1] = + OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]); + } + if (tessOuterHi[2] > 0) { + tessLevelOuter[2] = + OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]); + } + if (tessOuterHi[3] > 0) { + tessLevelOuter[3] = + OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]); + } +#elif defined OSD_FRACTIONAL_ODD_SPACING + // Combine fractional outer transition edge levels before rounding. + float4 combinedOuter = tessOuterLo + tessOuterHi; + + // Round the segments of transition edges separately. We will recover the + // fractional parameterization of transition edges after tessellation. + // + // The sum of the two outer odd segment lengths will be an even number + // which the tessellator will increase by +1 so that there will be a + // total odd number of segments. We clamp the combinedOuter tess levels + // (used to compute the inner tess levels) so that the outer transition + // edges will be sampled without degenerate triangles. + + tessLevelOuter = combinedOuter; + if (tessOuterHi[0] > 0) { + tessLevelOuter[0] = + OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]); + combinedOuter = max(float4(3,3,3,3), combinedOuter); + } + if (tessOuterHi[1] > 0) { + tessLevelOuter[1] = + OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]); + combinedOuter = max(float4(3,3,3,3), combinedOuter); + } + if (tessOuterHi[2] > 0) { + tessLevelOuter[2] = + OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]); + combinedOuter = max(float4(3,3,3,3), combinedOuter); + } + if (tessOuterHi[3] > 0) { + tessLevelOuter[3] = + OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]); + combinedOuter = max(float4(3,3,3,3), combinedOuter); + } +#else + // Round equally spaced transition edge levels before combining. + tessOuterLo = round(tessOuterLo); + tessOuterHi = round(tessOuterHi); + + float4 combinedOuter = tessOuterLo + tessOuterHi; + tessLevelOuter = combinedOuter; +#endif + + // Inner levels are the averages the corresponding outer levels. + tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5; + tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5; +} + void OsdGetTessLevelsUniform(int3 patchParam, out float4 tessLevelOuter, out float2 tessLevelInner, @@ -681,13 +768,8 @@ OsdGetTessLevelsUniform(int3 patchParam, { OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi); - // Outer levels are the sum of the Lo and Hi segments where the Hi - // segments will have a length of zero for non-transition edges. - tessLevelOuter = tessOuterLo + tessOuterHi; - - // Inner levels are the average the corresponding outer levels. - tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5; - tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5; + OsdComputeTessLevels(tessOuterLo, tessOuterHi, + tessLevelOuter, tessLevelInner); } void @@ -695,15 +777,11 @@ OsdGetTessLevelsAdaptiveRefinedPoints(float3 cpRefined[16], int3 patchParam, out float4 tessLevelOuter, out float2 tessLevelInner, out float4 tessOuterLo, out float4 tessOuterHi) { - OsdGetTessLevelsRefinedPoints(cpRefined, patchParam, tessOuterLo, tessOuterHi); + OsdGetTessLevelsRefinedPoints(cpRefined, patchParam, + tessOuterLo, tessOuterHi); - // Outer levels are the sum of the Lo and Hi segments where the Hi - // segments will have a length of zero for non-transition edges. - tessLevelOuter = tessOuterLo + tessOuterHi; - - // Inner levels are the average the corresponding outer levels. - tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5; - tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5; + OsdComputeTessLevels(tessOuterLo, tessOuterHi, + tessLevelOuter, tessLevelInner); } void @@ -712,15 +790,11 @@ OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16], out float4 tessLevelOuter, out float2 tessLevelInner, out float4 tessOuterLo, out float4 tessOuterHi) { - OsdGetTessLevelsLimitPoints(cpBezier, patchParam, tessOuterLo, tessOuterHi); + OsdGetTessLevelsLimitPoints(cpBezier, patchParam, + tessOuterLo, tessOuterHi); - // Outer levels are the sum of the Lo and Hi segments where the Hi - // segments will have a length of zero for non-transition edges. - tessLevelOuter = tessOuterLo + tessOuterHi; - - // Inner levels are the average the corresponding outer levels. - tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5; - tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5; + OsdComputeTessLevels(tessOuterLo, tessOuterHi, + tessLevelOuter, tessLevelInner); } void @@ -741,26 +815,97 @@ OsdGetTessLevels(float3 cp0, float3 cp1, float3 cp2, float3 cp3, OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi); #endif - // Outer levels are the sum of the Lo and Hi segments where the Hi - // segments will have a length of zero for non-transition edges. - tessLevelOuter = tessOuterLo + tessOuterHi; - - // Inner levels are the average the corresponding outer levels. - tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5; - tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5; + OsdComputeTessLevels(tessOuterLo, tessOuterHi, + tessLevelOuter, tessLevelInner); } +#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING float -OsdGetTessTransitionSplit(float t, float n0, float n1) +OsdGetTessFractionalSplit(float t, float level, float levelUp) { - float ti = round(t * (n0 + n1)); + // Fractional tessellation of an edge will produce n segments where n + // is the tessellation level of the edge (level) rounded up to the + // nearest even or odd integer (levelUp). There will be n-2 segments of + // equal length (dx1) and two additional segments of equal length (dx0) + // that are typically shorter than the other segments. The two additional + // segments should be placed symmetrically on opposite sides of the + // edge (offset). - if (ti <= n0) { - return 0.5 * (ti / n0); +#if defined OSD_FRACTIONAL_EVEN_SPACING + if (level <= 2) return t; + + float base = pow(2.0,floor(log2(levelUp))); + float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1)); + +#elif defined OSD_FRACTIONAL_ODD_SPACING + if (level <= 1) return t; + + float base = pow(2.0,floor(log2(levelUp))); + float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1); +#endif + + float dx0 = (1.0 - (levelUp-level)/2) / levelUp; + float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0)); + + if (t < 0.5) { + float x = levelUp/2 - round(t*levelUp); + return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1)); + } else if (t > 0.5) { + float x = round(t*levelUp) - levelUp/2; + return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1)); } else { - return 0.5 * ((ti - n0) / n1) + 0.5; + return t; } } +#endif + +float +OsdGetTessTransitionSplit(float t, float lo, float hi) +{ +#if defined OSD_FRACTIONAL_EVEN_SPACING + float loRoundUp = OsdRoundUpEven(lo); + float hiRoundUp = OsdRoundUpEven(hi); + + // Convert the parametric t into a segment index along the combined edge. + float ti = round(t * (loRoundUp + hiRoundUp)); + + if (ti <= loRoundUp) { + float t0 = ti / loRoundUp; + return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5; + } else { + float t1 = (ti - loRoundUp) / hiRoundUp; + return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5; + } +#elif defined OSD_FRACTIONAL_ODD_SPACING + float loRoundUp = OsdRoundUpOdd(lo); + float hiRoundUp = OsdRoundUpOdd(hi); + + // Convert the parametric t into a segment index along the combined edge. + // The +1 below is to account for the extra segment produced by the + // tessellator since the sum of two odd tess levels will be rounded + // up by one to the next odd integer tess level. + float ti = round(t * (loRoundUp + hiRoundUp + 1)); + + if (ti <= loRoundUp) { + float t0 = ti / loRoundUp; + return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5; + } else if (ti > (loRoundUp+1)) { + float t1 = (ti - (loRoundUp+1)) / hiRoundUp; + return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5; + } else { + return 0.5; + } +#else + // Convert the parametric t into a segment index along the combined edge. + float ti = round(t * (lo + hi)); + + if (ti <= lo) { + return (ti / lo) * 0.5; + } else { + return ((ti - lo) / hi) * 0.5 + 0.5; + } +#endif +} float2 OsdGetTessParameterization(float2 uv, float4 tessOuterLo, float4 tessOuterHi)