Merge pull request #1098 from davidgyu/loop_patches_dx11_drawing

Drawing Loop patches with HLSL tessellation shaders
This commit is contained in:
Pol 2019-04-26 16:54:26 -07:00 committed by GitHub
commit 1844d620f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1981 additions and 934 deletions

View File

@ -59,6 +59,7 @@
#include <opensubdiv/osd/d3d11LegacyGregoryPatchTable.h>
OpenSubdiv::Osd::D3D11MeshInterface *g_mesh = NULL;
OpenSubdiv::Osd::D3D11LegacyGregoryPatchTable *g_legacyGregoryPatchTable = NULL;
bool g_legacyGregoryEnabled = false;
#include "../../regression/common/far_utils.h"
#include "../common/argOptions.h"
@ -99,10 +100,11 @@ enum DisplayStyle { kDisplayStyleWire = 0,
enum ShadingMode { kShadingMaterial,
kShadingPatchType,
kShadingPatchDepth,
kShadingPatchCoord,
kShadingNormal };
enum EndCap { kEndCapNone = 0,
enum EndCap { kEndCapBilinearBasis = 0,
kEndCapBSplineBasis,
kEndCapGregoryBasis,
kEndCapLegacyGregory };
@ -116,7 +118,11 @@ enum HudCheckBox { kHUD_CB_DISPLAY_CONTROL_MESH_EDGES,
kHUD_CB_FRACTIONAL_SPACING,
kHUD_CB_PATCH_CULL,
kHUD_CB_FREEZE,
kHUD_CB_DISPLAY_PATCH_COUNTS };
kHUD_CB_DISPLAY_PATCH_COUNTS,
kHUD_CB_ADAPTIVE,
kHUD_CB_SMOOTH_CORNER_PATCH,
kHUD_CB_SINGLE_CREASE_PATCH,
kHUD_CB_INF_SHARP_PATCH };
int g_currentShape = 0;
@ -128,7 +134,8 @@ int g_freeze = 0,
g_shadingMode = kShadingPatchType,
g_displayStyle = kDisplayStyleWireOnShaded,
g_adaptive = 1,
g_endCap = kEndCapBSplineBasis,
g_endCap = kEndCapGregoryBasis,
g_smoothCornerPatch = 0,
g_singleCreasePatch = 1,
g_infSharpPatch = 0,
g_drawNormals = 0,
@ -249,7 +256,7 @@ updateGeom() {
static const char *
getKernelName(int kernel) {
if (kernel == kCPU)
if (kernel == kCPU)
return "CPU";
else if (kernel == kOPENMP)
return "OpenMP";
@ -296,18 +303,15 @@ createOsdMesh(ShapeDesc const & shapeDesc, int level, int kernel, Scheme scheme=
g_scheme = scheme;
// Adaptive refinement currently supported only for catmull-clark scheme
bool doAdaptive = (g_adaptive!=0 && g_scheme==kCatmark),
doSingleCreasePatch = (g_singleCreasePatch!=0 && g_scheme==kCatmark),
doInfSharpPatch = (g_infSharpPatch!=0 && g_scheme==kCatmark);
Osd::MeshBitset bits;
bits.set(Osd::MeshAdaptive, doAdaptive);
bits.set(Osd::MeshUseSingleCreasePatch, doSingleCreasePatch);
bits.set(Osd::MeshUseInfSharpPatch, doInfSharpPatch);
bits.set(Osd::MeshEndCapBSplineBasis, g_endCap == kEndCapBSplineBasis);
bits.set(Osd::MeshEndCapGregoryBasis, g_endCap == kEndCapGregoryBasis);
bits.set(Osd::MeshEndCapLegacyGregory, g_endCap == kEndCapLegacyGregory);
bits.set(Osd::MeshAdaptive, g_adaptive != 0);
bits.set(Osd::MeshUseSmoothCornerPatch, g_smoothCornerPatch != 0);
bits.set(Osd::MeshUseSingleCreasePatch, g_singleCreasePatch != 0);
bits.set(Osd::MeshUseInfSharpPatch, g_infSharpPatch != 0);
bits.set(Osd::MeshEndCapBilinearBasis, g_endCap == kEndCapBilinearBasis);
bits.set(Osd::MeshEndCapBSplineBasis, g_endCap == kEndCapBSplineBasis);
bits.set(Osd::MeshEndCapGregoryBasis, g_endCap == kEndCapGregoryBasis);
bits.set(Osd::MeshEndCapLegacyGregory, g_endCap == kEndCapLegacyGregory);
int numVertexElements = 6;
int numVaryingElements = 0;
@ -519,7 +523,8 @@ public:
if (effectDesc.effect.patchCull) {
ss << "#define OSD_ENABLE_PATCH_CULL\n";
}
if (effectDesc.effect.singleCreasePatch) {
if (effectDesc.effect.singleCreasePatch &&
type == Far::PatchDescriptor::REGULAR) {
ss << "#define OSD_PATCH_ENABLE_SINGLE_CREASE\n";
}
// for legacy gregory
@ -550,6 +555,9 @@ public:
case kShadingMaterial:
ss << "#define SHADING_MATERIAL\n";
break;
case kShadingPatchDepth:
ss << "#define SHADING_PATCH_DEPTH\n";
break;
case kShadingPatchType:
ss << "#define SHADING_PATCH_TYPE\n";
break;
@ -568,6 +576,10 @@ public:
ss << "#define OSD_PATCH_GREGORY_BOUNDARY\n";
} else if (type == Far::PatchDescriptor::GREGORY_BASIS) {
ss << "#define OSD_PATCH_GREGORY_BASIS\n";
} else if (type == Far::PatchDescriptor::LOOP) {
ss << "#define OSD_PATCH_LOOP\n";
} else if (type == Far::PatchDescriptor::GREGORY_TRIANGLE) {
ss << "#define OSD_PATCH_GREGORY_TRIANGLE\n";
}
// include osd PatchCommon
@ -850,9 +862,10 @@ display() {
g_mesh->GetPatchTable()->GetPatchIndexBuffer(), DXGI_FORMAT_R32_UINT, 0);
// patch drawing
int patchCount[12]; // [Type] (see far/patchTable.h)
int patchCount[13]; // [Type] (see far/patchTable.h)
int numTotalPatches = 0;
int numDrawCalls = 0;
memset(patchCount, 0, sizeof(patchCount));
for (int i=0; i<(int)patches.size(); ++i) {
OpenSubdiv::Osd::PatchArray const & patch = patches[i];
@ -883,9 +896,15 @@ display() {
case 12:
topology = D3D11_PRIMITIVE_TOPOLOGY_12_CONTROL_POINT_PATCHLIST;
break;
case 15:
topology = D3D11_PRIMITIVE_TOPOLOGY_15_CONTROL_POINT_PATCHLIST;
break;
case 16:
topology = D3D11_PRIMITIVE_TOPOLOGY_16_CONTROL_POINT_PATCHLIST;
break;
case 18:
topology = D3D11_PRIMITIVE_TOPOLOGY_18_CONTROL_POINT_PATCHLIST;
break;
case 20:
topology = D3D11_PRIMITIVE_TOPOLOGY_20_CONTROL_POINT_PATCHLIST;
break;
@ -919,18 +938,26 @@ display() {
double fps = 1.0/g_fpsTimer.GetElapsed();
if (g_displayPatchCounts) {
int x = -280;
int y = -180;
g_hud->DrawString(x, y, "NonPatch : %d",
int x = -420;
int y = g_legacyGregoryEnabled ? -180 : -140;
g_hud->DrawString(x, y, "Quads : %d",
patchCount[Descriptor::QUADS]); y += 20;
g_hud->DrawString(x, y, "Triangles : %d",
patchCount[Descriptor::TRIANGLES]); y += 20;
g_hud->DrawString(x, y, "Regular : %d",
patchCount[Descriptor::REGULAR]); y+= 20;
g_hud->DrawString(x, y, "Gregory : %d",
patchCount[Descriptor::GREGORY]); y+= 20;
g_hud->DrawString(x, y, "Boundary Gregory : %d",
patchCount[Descriptor::GREGORY_BOUNDARY]); y+= 20;
g_hud->DrawString(x, y, "Loop : %d",
patchCount[Descriptor::LOOP]); y+= 20;
if (g_legacyGregoryEnabled) {
g_hud->DrawString(x, y, "Gregory : %d",
patchCount[Descriptor::GREGORY]); y+= 20;
g_hud->DrawString(x, y, "Boundary Gregory : %d",
patchCount[Descriptor::GREGORY_BOUNDARY]); y+= 20;
}
g_hud->DrawString(x, y, "Gregory Basis : %d",
patchCount[Descriptor::GREGORY_BASIS]); y+= 20;
g_hud->DrawString(x, y, "Gregory Triangle : %d",
patchCount[Descriptor::GREGORY_TRIANGLE]); y+= 20;
}
g_hud->DrawString(10, -120, "Tess level : %d", g_tessLevel);
@ -1105,34 +1132,6 @@ callbackShadingMode(int b) {
}
static void
callbackAnimate(bool checked, int m) {
g_moveScale = checked;
}
static void
callbackFreeze(bool checked, int f) {
g_freeze = checked;
}
static void
callbackAdaptive(bool checked, int a) {
g_adaptive = checked;
rebuildOsdMesh();
}
static void
callbackSingleCreasePatch(bool checked, int /* a */) {
g_singleCreasePatch = checked;
rebuildOsdMesh();
}
static void
callbackInfSharpPatch(bool checked, int /* a */) {
g_infSharpPatch = checked;
rebuildOsdMesh();
}
static void
callbackCheckBox(bool checked, int button) {
switch (button) {
@ -1160,6 +1159,22 @@ callbackCheckBox(bool checked, int button) {
case kHUD_CB_DISPLAY_PATCH_COUNTS:
g_displayPatchCounts = checked;
break;
case kHUD_CB_ADAPTIVE:
g_adaptive = checked;
rebuildOsdMesh();
break;
case kHUD_CB_SMOOTH_CORNER_PATCH:
g_smoothCornerPatch = checked;
rebuildOsdMesh();
break;
case kHUD_CB_SINGLE_CREASE_PATCH:
g_singleCreasePatch = checked;
rebuildOsdMesh();
break;
case kHUD_CB_INF_SHARP_PATCH:
g_infSharpPatch = checked;
rebuildOsdMesh();
break;
}
}
@ -1206,6 +1221,9 @@ initHUD() {
g_hud->AddPullDownButton(shading_pulldown, "Patch Type",
kShadingPatchType,
g_shadingMode == kShadingPatchType);
g_hud->AddPullDownButton(shading_pulldown, "Patch Depth",
kShadingPatchDepth,
g_shadingMode == kShadingPatchCoord);
g_hud->AddPullDownButton(shading_pulldown, "Patch Coord",
kShadingPatchCoord,
g_shadingMode == kShadingPatchCoord);
@ -1240,24 +1258,31 @@ initHUD() {
10, y, callbackCheckBox, kHUD_CB_FREEZE, ' ');
y += 20;
g_hud->AddCheckBox("Adaptive (`)", true, 10, 190, callbackAdaptive, 0, '`');
g_hud->AddCheckBox("Single Crease Patch (S)", g_singleCreasePatch!=0, 10, 210, callbackSingleCreasePatch, 0, 'S');
g_hud->AddCheckBox("Inf Sharp Patch (I)", g_infSharpPatch!=0, 10, 230, callbackInfSharpPatch, 0, 'I');
g_hud->AddCheckBox("Adaptive (`)", true,
10, 190, callbackCheckBox, kHUD_CB_ADAPTIVE, '`');
g_hud->AddCheckBox("Smooth Corner Patch (O)", g_smoothCornerPatch!=0,
10, 210, callbackCheckBox, kHUD_CB_SMOOTH_CORNER_PATCH, 'O');
g_hud->AddCheckBox("Single Crease Patch (S)", g_singleCreasePatch!=0,
10, 230, callbackCheckBox, kHUD_CB_SINGLE_CREASE_PATCH, 'S');
g_hud->AddCheckBox("Inf Sharp Patch (I)", g_infSharpPatch!=0,
10, 250, callbackCheckBox, kHUD_CB_INF_SHARP_PATCH, 'I');
int endcap_pulldown = g_hud->AddPullDown(
"End cap (E)", 10, 250, 200, callbackEndCap, 'E');
g_hud->AddPullDownButton(endcap_pulldown,"None",
kEndCapNone,
g_endCap == kEndCapNone);
g_hud->AddPullDownButton(endcap_pulldown, "BSpline",
"End cap (E)", 10, 270, 200, callbackEndCap, 'E');
g_hud->AddPullDownButton(endcap_pulldown,"Linear",
kEndCapBilinearBasis,
g_endCap == kEndCapBilinearBasis);
g_hud->AddPullDownButton(endcap_pulldown, "Regular",
kEndCapBSplineBasis,
g_endCap == kEndCapBSplineBasis);
g_hud->AddPullDownButton(endcap_pulldown, "GregoryBasis",
g_hud->AddPullDownButton(endcap_pulldown, "Gregory",
kEndCapGregoryBasis,
g_endCap == kEndCapGregoryBasis);
g_hud->AddPullDownButton(endcap_pulldown, "LegacyGregory",
kEndCapLegacyGregory,
g_endCap == kEndCapLegacyGregory);
if (g_legacyGregoryEnabled) {
g_hud->AddPullDownButton(endcap_pulldown, "LegacyGregory",
kEndCapLegacyGregory,
g_endCap == kEndCapLegacyGregory);
}
for (int i = 1; i < 11; ++i) {
char level[16];
@ -1270,7 +1295,7 @@ 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->AddCheckBox("Show patch counts", g_displayPatchCounts!=0, -420, -20, callbackCheckBox, kHUD_CB_DISPLAY_PATCH_COUNTS);
callbackModel(g_currentShape);
}
@ -1560,7 +1585,14 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine,
// Parse the command line arguments
ArgOptions args;
args.Parse(__argc, __argv);
args.PrintUnrecognizedArgsWarnings();
const std::vector<const char *> &rargs = args.GetRemainingArgs();
for (size_t i = 0; i < rargs.size(); ++i) {
if (!strcmp(rargs[i], "-lg")) {
g_legacyGregoryEnabled = true;
} else {
args.PrintUnrecognizedArgWarning(rargs[i]);
}
}
g_yup = args.GetYUp();
g_adaptive = args.GetAdaptive();

View File

@ -403,13 +403,13 @@ getAdaptivePatchColor(int3 patchParam, float2 vSegments)
};
int patchType = 0;
int pattern = countbits(OsdGetPatchTransitionMask(patchParam));
int edgeCount = countbits(OsdGetPatchBoundaryMask(patchParam));
if (edgeCount == 1) {
patchType = 2; // BOUNDARY
}
if (edgeCount == 2) {
patchType = 3; // CORNER
if (edgeCount > 1) {
patchType = 3; // CORNER (not correct for patches that are not isolated)
}
#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
@ -426,12 +426,29 @@ getAdaptivePatchColor(int3 patchParam, float2 vSegments)
patchType = 5;
#elif defined OSD_PATCH_GREGORY_BASIS
patchType = 6;
#elif defined OSD_PATCH_GREGORY_TRIANGLE
patchType = 6;
#endif
int pattern = countbits(OsdGetPatchTransitionMask(patchParam));
return patchColors[6*patchType + pattern];
}
float4
getAdaptiveDepthColor(int3 patchParam)
{
// Represent depth with repeating cycle of four colors:
const float4 depthColors[4] = {
float4(0.0f, 0.5f, 0.5f, 1.0f),
float4(1.0f, 1.0f, 1.0f, 1.0f),
float4(0.0f, 1.0f, 1.0f, 1.0f),
float4(0.5f, 1.0f, 0.5f, 1.0f)
};
return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
}
// ---------------------------------------------------------------------------
// Pixel Shader
// ---------------------------------------------------------------------------
@ -451,6 +468,9 @@ ps_main( in OutputVertex input,
#if defined(SHADING_PATCH_TYPE)
float4 color = getAdaptivePatchColor(
OsdGetPatchParam(OsdGetPatchIndex(primitiveID)), vSegments);
#elif defined(SHADING_PATCH_DEPTH)
float4 color = getAdaptiveDepthColor(
OsdGetPatchParam(OsdGetPatchIndex(primitiveID)));
#elif defined(SHADING_PATCH_COORD)
float4 color = float4(input.patchCoord.x, input.patchCoord.y, 0, 1);
#elif defined(SHADING_MATERIAL)

View File

@ -213,9 +213,13 @@ if( DXSDK_FOUND )
list(APPEND KERNEL_FILES
hlslComputeKernel.hlsl
hlslPatchCommon.hlsl
hlslPatchCommonTess.hlsl
hlslPatchBoxSplineTriangle.hlsl
hlslPatchBSpline.hlsl
hlslPatchGregory.hlsl
hlslPatchGregoryBasis.hlsl
hlslPatchGregoryTriangle.hlsl
hlslPatchLegacy.hlsl
)
list(APPEND PLATFORM_GPU_LIBRARIES
${DXSDK_LIBRARIES}

View File

@ -80,7 +80,7 @@ HSConstFunc(
#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
#if 0
// XXX: this doesn't work on nvidia driver 34x.
OsdGetTessLevelsAdaptiveLimitPoints(bezierPatch, patchParam,
OsdEvalPatchBezierTessLevels(bezierPatch, patchParam,
tessLevelOuter, tessLevelInner,
tessOuterLo, tessOuterHi);
#else
@ -90,7 +90,7 @@ HSConstFunc(
cpBezier[i] = bezierPatch[i];
cpBezier[i].P += 0.0f;
}
OsdGetTessLevelsAdaptiveLimitPoints(cpBezier, patchParam,
OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
tessLevelOuter, tessLevelInner,
tessOuterLo, tessOuterHi);
#endif

View File

@ -0,0 +1,147 @@
//
// Copyright 2019 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
//----------------------------------------------------------
// Patches.VertexBoxSplineTriangle
//----------------------------------------------------------
void vs_main_patches( in InputVertex input,
out HullVertex output )
{
output.position = input.position;
OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(input.position);
}
//----------------------------------------------------------
// Patches.HullBoxSplineTriangle
//----------------------------------------------------------
[domain("tri")]
[partitioning(OSD_PARTITIONING)]
[outputtopology("triangle_cw")]
[outputcontrolpoints(15)]
[patchconstantfunc("HSConstFunc")]
OsdPerPatchVertexBezier hs_main_patches(
in InputPatch<HullVertex, 12> patch,
uint primitiveID : SV_PrimitiveID,
in uint ID : SV_OutputControlPointID )
{
OsdPerPatchVertexBezier output;
float3 cv[12];
for (int i=0; i<12; ++i) {
cv[i] = patch[i].position.xyz;
}
int3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(primitiveID));
OsdComputePerPatchVertexBoxSplineTriangle(patchParam, ID, cv, output);
return output;
}
HS_CONSTANT_FUNC_TRIANGLE_OUT
HSConstFunc(
InputPatch<HullVertex, 12> patch,
OutputPatch<OsdPerPatchVertexBezier, 15> bezierTrianglePatch,
uint primitiveID : SV_PrimitiveID)
{
HS_CONSTANT_FUNC_TRIANGLE_OUT output;
int3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(primitiveID));
float4 tessLevelOuter = float4(0,0,0,0);
float2 tessLevelInner = float2(0,0);
float4 tessOuterLo = float4(0,0,0,0);
float4 tessOuterHi = float4(0,0,0,0);
OSD_PATCH_CULL_TRIANGLE(12);
#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
float3 bezcv[15];
for (int i=0; i<15; ++i) {
bezcv[i] = bezierTrianglePatch[i].P;
}
OsdEvalPatchBezierTriangleTessLevels(bezcv, patchParam,
tessLevelOuter, tessLevelInner,
tessOuterLo, tessOuterHi);
#else
OsdGetTessLevelsUniformTriangle(patchParam, tessLevelOuter, tessLevelInner,
tessOuterLo, tessOuterHi);
#endif
output.tessLevelOuter[0] = tessLevelOuter[0];
output.tessLevelOuter[1] = tessLevelOuter[1];
output.tessLevelOuter[2] = tessLevelOuter[2];
output.tessLevelInner[0] = tessLevelInner[0];
output.tessOuterLo = tessOuterLo;
output.tessOuterHi = tessOuterHi;
return output;
}
//----------------------------------------------------------
// Patches.DomainBoxSplineTriangle
//----------------------------------------------------------
[domain("tri")]
void ds_main_patches(
in HS_CONSTANT_FUNC_TRIANGLE_OUT input,
in OutputPatch<OsdPerPatchVertexBezier, 15> patch,
in float3 domainCoord : SV_DomainLocation,
out OutputVertex output )
{
float3 P = float3(0,0,0), dPu = float3(0,0,0), dPv = float3(0,0,0);
float3 N = float3(0,0,0), dNu = float3(0,0,0), dNv = float3(0,0,0);
OsdPerPatchVertexBezier cv[15];
for (int i=0; i<15; ++i) {
cv[i] = patch[i];
}
float2 UV = OsdGetTessParameterizationTriangle(domainCoord.xy,
input.tessOuterLo,
input.tessOuterHi);
int3 patchParam = patch[0].patchParam;
OsdEvalPatchBezierTriangle(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);
// all code below here is client code
output.position = mul(OsdModelViewMatrix(), float4(P, 1.0f));
output.normal = mul(OsdModelViewMatrix(), float4(N, 0.0f)).xyz;
output.tangent = mul(OsdModelViewMatrix(), float4(dPu, 0.0f)).xyz;
output.bitangent = mul(OsdModelViewMatrix(), float4(dPv, 0.0f)).xyz;
#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
output.Nu = dNu;
output.Nv = dNv;
#endif
output.patchCoord = OsdInterpolatePatchCoordTriangle(UV, patchParam);
OSD_DISPLACEMENT_CALLBACK;
output.positionOut = mul(OsdProjectionMatrix(), output.position);
output.edgeDistance = 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,750 @@
//
// Copyright 2013-2019 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
// ----------------------------------------------------------------------------
// Tessellation
// ----------------------------------------------------------------------------
// For now, fractional spacing is supported only with screen space tessellation
#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
#undef OSD_FRACTIONAL_EVEN_SPACING
#undef OSD_FRACTIONAL_ODD_SPACING
#endif
#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
struct HS_CONSTANT_FUNC_OUT {
float tessLevelInner[2] : SV_InsideTessFactor;
float tessLevelOuter[4] : SV_TessFactor;
float4 tessOuterLo : TRANSITIONLO;
float4 tessOuterHi : TRANSITIONHI;
};
struct HS_CONSTANT_FUNC_TRIANGLE_OUT {
float tessLevelInner[1] : SV_InsideTessFactor;
float tessLevelOuter[3] : SV_TessFactor;
float4 tessOuterLo : TRANSITIONLO;
float4 tessOuterHi : TRANSITIONHI;
};
//
// Organization of B-spline and Bezier control points.
//
// Each patch is defined by 16 control points (labeled 0-15).
//
// The patch will be evaluated across the domain from (0,0) at
// the lower-left to (1,1) at the upper-right. When computing
// adaptive tessellation metrics, we consider refined vertex-vertex
// and edge-vertex points along the transition edges of the patch
// (labeled vv* and ev* respectively).
//
// The two segments of each transition edge are labeled Lo and Hi,
// with the Lo segment occurring before the Hi segment along the
// transition edge's domain parameterization. These Lo and Hi segment
// tessellation levels determine how domain evaluation coordinates
// are remapped along transition edges. The Hi segment value will
// be zero for a non-transition edge.
//
// (0,1) (1,1)
//
// vv3 ev23 vv2
// | Lo3 | Hi3 |
// --O-----------O-----+-----O-----------O--
// | 12 | 13 14 | 15 |
// | | | |
// | | | |
// Hi0 | | | | Hi2
// | | | |
// O-----------O-----------O-----------O
// | 8 | 9 10 | 11 |
// | | | |
// ev03 --+ | | +-- ev12
// | | | |
// | 4 | 5 6 | 7 |
// O-----------O-----------O-----------O
// | | | |
// Lo0 | | | | Lo2
// | | | |
// | | | |
// | 0 | 1 2 | 3 |
// --O-----------O-----+-----O-----------O--
// | Lo1 | Hi1 |
// vv0 ev01 vv1
//
// (0,0) (1,0)
//
#define OSD_MAX_TESS_LEVEL 64
float OsdComputePostProjectionSphereExtent(float3 center, float diameter)
{
float4 p = mul(OsdProjectionMatrix(), float4(center, 1.0));
return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
}
float OsdComputeTessLevel(float3 p0, float3 p1)
{
// Adaptive factor can be any computation that depends only on arg values.
// Project the diameter of the edge's bounding sphere instead of using the
// length of the projected edge itself to avoid problems near silhouettes.
p0 = mul(OsdModelViewMatrix(), float4(p0, 1.0)).xyz;
p1 = mul(OsdModelViewMatrix(), float4(p1, 1.0)).xyz;
float3 center = (p0 + p1) / 2.0;
float diameter = distance(p0, p1);
float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
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
// halves and the sum of the two corresponding levels must not exceed
// the device maximum. We impose this limit even for non-transition
// edges because a non-transition edge must be able to match up with
// one half of the transition edge of an adjacent transition patch.
return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
}
void
OsdGetTessLevelsUniform(int3 patchParam,
out float4 tessOuterLo, out float4 tessOuterHi)
{
// Uniform factors are simple powers of two for each level.
// The maximum here can be increased if we know the maximum
// refinement level of the mesh:
// min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
pow(2, refinementLevel-1);
// tessLevels of transition edge should be clamped to 2.
int transitionMask = OsdGetPatchTransitionMask(patchParam);
float4 tessLevelMin = float4(1,1,1,1)
+ float4(((transitionMask & 8) >> 3),
((transitionMask & 1) >> 0),
((transitionMask & 2) >> 1),
((transitionMask & 4) >> 2));
tessOuterLo = max(float4(tessLevel,tessLevel,tessLevel,tessLevel),
tessLevelMin);
tessOuterHi = float4(0,0,0,0);
}
void
OsdGetTessLevelsUniformTriangle(int3 patchParam,
out float4 tessOuterLo, out float4 tessOuterHi)
{
// Uniform factors are simple powers of two for each level.
// The maximum here can be increased if we know the maximum
// refinement level of the mesh:
// min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
pow(2, refinementLevel-1);
// tessLevels of transition edge should be clamped to 2.
int transitionMask = OsdGetPatchTransitionMask(patchParam);
float4 tessLevelMin = float4(1,1,1,1)
+ float4(((transitionMask & 4) >> 2),
((transitionMask & 1) >> 0),
((transitionMask & 2) >> 1),
0);
tessOuterLo = max(float4(tessLevel,tessLevel,tessLevel,tessLevel),
tessLevelMin);
tessOuterHi = float4(0,0,0,0);
}
void
OsdGetTessLevelsRefinedPoints(float3 cp[16], int3 patchParam,
out float4 tessOuterLo, out float4 tessOuterHi)
{
// Each edge of a transition patch is adjacent to one or two patches
// at the next refined level of subdivision. We compute the corresponding
// vertex-vertex and edge-vertex refined points along the edges of the
// patch using Catmull-Clark subdivision stencil weights.
// For simplicity, we let the optimizer discard unused computation.
float3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
(cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
float3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
(cp[5] + cp[6]) * 0.375;
float3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
(cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
float3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
(cp[6] + cp[10]) * 0.375;
float3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
(cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
float3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
(cp[9] + cp[10]) * 0.375;
float3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
(cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
float3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
(cp[5] + cp[9]) * 0.375;
tessOuterLo = float4(0,0,0,0);
tessOuterHi = float4(0,0,0,0);
int transitionMask = OsdGetPatchTransitionMask(patchParam);
if ((transitionMask & 8) != 0) {
tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
} else {
tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
}
if ((transitionMask & 1) != 0) {
tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
} else {
tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
}
if ((transitionMask & 2) != 0) {
tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
} else {
tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
}
if ((transitionMask & 4) != 0) {
tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
} else {
tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
}
}
//
// Patch boundary corners are ordered counter-clockwise from the first
// corner while patch boundary edges and their midpoints are similarly
// ordered counter-clockwise beginning at the edge preceding corner[0].
//
void
Osd_GetTessLevelsFromPatchBoundaries4(float3 corners[4], float3 midpoints[4],
int3 patchParam, out float4 tessOuterLo, out float4 tessOuterHi)
{
tessOuterLo = float4(0,0,0,0);
tessOuterHi = float4(0,0,0,0);
int transitionMask = OsdGetPatchTransitionMask(patchParam);
if ((transitionMask & 8) != 0) {
tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
} else {
tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
}
if ((transitionMask & 1) != 0) {
tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
} else {
tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
}
if ((transitionMask & 2) != 0) {
tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
} else {
tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
}
if ((transitionMask & 4) != 0) {
tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
} else {
tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
}
}
void
Osd_GetTessLevelsFromPatchBoundaries3(float3 corners[3], float3 midpoints[3],
int3 patchParam, out float4 tessOuterLo, out float4 tessOuterHi)
{
tessOuterLo = float4(0,0,0,0);
tessOuterHi = float4(0,0,0,0);
int transitionMask = OsdGetPatchTransitionMask(patchParam);
if ((transitionMask & 4) != 0) {
tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
} else {
tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
}
if ((transitionMask & 1) != 0) {
tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
} else {
tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
}
if ((transitionMask & 2) != 0) {
tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
} else {
tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
}
}
float3
Osd_EvalBezierCurveMidPoint(float3 p0, float3 p1, float3 p2, float3 p3)
{
// Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
}
float3
Osd_EvalQuarticBezierCurveMidPoint(float3 p0, float3 p1, float3 p2, float3 p3, float3 p4)
{
// Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
}
void
OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
int3 patchParam, out float4 tessOuterLo, out float4 tessOuterHi)
{
// Each edge of a transition patch is adjacent to one or two patches
// at the next refined level of subdivision. When the patch control
// points have been converted to the Bezier basis, the control points
// at the four corners are on the limit surface (since a Bezier patch
// interpolates its corner control points). We can compute an adaptive
// tessellation level for transition edges on the limit surface by
// evaluating a limit position at the mid point of each transition edge.
tessOuterLo = float4(0,0,0,0);
tessOuterHi = float4(0,0,0,0);
float3 corners[4];
float3 midpoints[4];
int transitionMask = OsdGetPatchTransitionMask(patchParam);
#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
corners[0] = OsdEvalBezier(cpBezier, patchParam, float2(0.0, 0.0));
corners[1] = OsdEvalBezier(cpBezier, patchParam, float2(1.0, 0.0));
corners[2] = OsdEvalBezier(cpBezier, patchParam, float2(1.0, 1.0));
corners[3] = OsdEvalBezier(cpBezier, patchParam, float2(0.0, 1.0));
midpoints[0] = ((transitionMask & 8) == 0) ? float3(0,0,0) :
OsdEvalBezier(cpBezier, patchParam, float2(0.0, 0.5));
midpoints[1] = ((transitionMask & 1) == 0) ? float3(0,0,0) :
OsdEvalBezier(cpBezier, patchParam, float2(0.5, 0.0));
midpoints[2] = ((transitionMask & 2) == 0) ? float3(0,0,0) :
OsdEvalBezier(cpBezier, patchParam, float2(1.0, 0.5));
midpoints[3] = ((transitionMask & 4) == 0) ? float3(0,0,0) :
OsdEvalBezier(cpBezier, patchParam, float2(0.5, 1.0));
#else
corners[0] = cpBezier[ 0].P;
corners[1] = cpBezier[ 3].P;
corners[2] = cpBezier[15].P;
corners[3] = cpBezier[12].P;
midpoints[0] = ((transitionMask & 8) == 0) ? float3(0,0,0) :
Osd_EvalBezierCurveMidPoint(
cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
midpoints[1] = ((transitionMask & 1) == 0) ? float3(0,0,0) :
Osd_EvalBezierCurveMidPoint(
cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
midpoints[2] = ((transitionMask & 2) == 0) ? float3(0,0,0) :
Osd_EvalBezierCurveMidPoint(
cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
midpoints[3] = ((transitionMask & 4) == 0) ? float3(0,0,0) :
Osd_EvalBezierCurveMidPoint(
cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
#endif
Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
patchParam, tessOuterLo, tessOuterHi);
}
void
OsdEvalPatchBezierTriangleTessLevels(float3 cv[15],
int3 patchParam, out float4 tessOuterLo, out float4 tessOuterHi)
{
// Each edge of a transition patch is adjacent to one or two patches
// at the next refined level of subdivision. When the patch control
// points have been converted to the Bezier basis, the control points
// at the corners are on the limit surface (since a Bezier patch
// interpolates its corner control points). We can compute an adaptive
// tessellation level for transition edges on the limit surface by
// evaluating a limit position at the mid point of each transition edge.
tessOuterLo = float4(0,0,0,0);
tessOuterHi = float4(0,0,0,0);
int transitionMask = OsdGetPatchTransitionMask(patchParam);
float3 corners[3];
corners[0] = cv[0];
corners[1] = cv[4];
corners[2] = cv[14];
float3 midpoints[3];
midpoints[0] = ((transitionMask & 4) == 0) ? float3(0,0,0) :
Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
midpoints[1] = ((transitionMask & 1) == 0) ? float3(0,0,0) :
Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
midpoints[2] = ((transitionMask & 2) == 0) ? float3(0,0,0) :
Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
patchParam, tessOuterLo, tessOuterHi);
}
// 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
OsdComputeTessLevelsTriangle(inout float4 tessOuterLo, inout float4 tessOuterHi,
out float4 tessLevelOuter, out float2 tessLevelInner)
{
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
// Inner level is the max of the three outer levels.
tessLevelInner[0] = max(max(tessLevelOuter[0],
tessLevelOuter[1]),
tessLevelOuter[2]);
}
void
OsdGetTessLevelsUniform(int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner,
out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
OsdGetTessLevelsUniformTriangle(int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner,
out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner,
out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
tessOuterLo, tessOuterHi);
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
OsdEvalPatchBezierTriangleTessLevels(float3 cv[15],
int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner,
out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
tessOuterLo, tessOuterHi);
OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
OsdGetTessLevelsAdaptiveRefinedPoints(float3 cpRefined[16], int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner,
out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
tessOuterLo, tessOuterHi);
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
// Deprecated -- prefer use of newer Bezier patch equivalend:
void
OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
int3 patchParam, out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
}
// Deprecated -- prefer use of newer Bezier patch equivalent:
void
OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner,
out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
tessOuterLo, tessOuterHi);
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
OsdGetTessLevels(float3 cp0, float3 cp1, float3 cp2, float3 cp3,
int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner)
{
float4 tessOuterLo = float4(0,0,0,0);
float4 tessOuterHi = float4(0,0,0,0);
#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
tessOuterHi = float4(0,0,0,0);
#else
OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
#endif
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
float
OsdGetTessFractionalSplit(float t, float level, float levelUp)
{
// 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 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 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)
{
float2 UV = uv;
if (UV.x == 0 && tessOuterHi[0] > 0) {
UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
} else
if (UV.y == 0 && tessOuterHi[1] > 0) {
UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
} else
if (UV.x == 1 && tessOuterHi[2] > 0) {
UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
} else
if (UV.y == 1 && tessOuterHi[3] > 0) {
UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
}
return UV;
}
float2
OsdGetTessParameterizationTriangle(float2 uv, float4 tessOuterLo, float4 tessOuterHi)
{
float2 UV = uv;
if (UV.x == 0 && tessOuterHi[0] > 0) {
UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
} else
if (UV.y == 0 && tessOuterHi[1] > 0) {
UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
} else
if (UV.x+UV.y == 1 && tessOuterHi[2] > 0) {
UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
UV.y = 1.0 - UV.x;
}
return UV;
}

View File

@ -0,0 +1,158 @@
//
// Copyright 2019 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
//----------------------------------------------------------
// Patches.VertexGregoryTriangle
//----------------------------------------------------------
void vs_main_patches( in InputVertex input,
out HullVertex output )
{
output.position = input.position;
OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(input.position);
}
//----------------------------------------------------------
// Patches.HullGregoryTriangle
//----------------------------------------------------------
[domain("tri")]
[partitioning(OSD_PARTITIONING)]
[outputtopology("triangle_cw")]
[outputcontrolpoints(18)]
[patchconstantfunc("HSConstFunc")]
OsdPerPatchVertexBezier hs_main_patches(
in InputPatch<HullVertex, 18> patch,
uint primitiveID : SV_PrimitiveID,
in uint ID : SV_OutputControlPointID )
{
OsdPerPatchVertexBezier output;
float3 cv = patch[ID].position.xyz;
int3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(primitiveID));
output.patchParam = patchParam;
output.P = cv;
return output;
}
HS_CONSTANT_FUNC_TRIANGLE_OUT
HSConstFunc(
InputPatch<HullVertex, 18> patch,
OutputPatch<OsdPerPatchVertexBezier, 18> bezierPatch,
uint primitiveID : SV_PrimitiveID)
{
HS_CONSTANT_FUNC_TRIANGLE_OUT output;
int3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(primitiveID));
float4 tessLevelOuter = float4(0,0,0,0);
float2 tessLevelInner = float2(0,0);
float4 tessOuterLo = float4(0,0,0,0);
float4 tessOuterHi = float4(0,0,0,0);
OSD_PATCH_CULL_TRIANGLE(18);
#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
float3 cv[15];
cv[ 0] = bezierPatch[ 0].P;
cv[ 1] = bezierPatch[ 1].P;
cv[ 2] = bezierPatch[15].P;
cv[ 3] = bezierPatch[ 7].P;
cv[ 4] = bezierPatch[ 5].P;
cv[ 5] = bezierPatch[ 2].P;
cv[ 6] = bezierPatch[ 3].P;
cv[ 7] = bezierPatch[ 8].P;
cv[ 8] = bezierPatch[ 6].P;
cv[ 9] = bezierPatch[17].P;
cv[10] = bezierPatch[13].P;
cv[11] = bezierPatch[16].P;
cv[12] = bezierPatch[11].P;
cv[13] = bezierPatch[12].P;
cv[14] = bezierPatch[10].P;
OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
tessLevelOuter, tessLevelInner,
tessOuterLo, tessOuterHi);
#else
OsdGetTessLevelsUniformTriangle(patchParam, tessLevelOuter, tessLevelInner,
tessOuterLo, tessOuterHi);
#endif
output.tessLevelOuter[0] = tessLevelOuter[0];
output.tessLevelOuter[1] = tessLevelOuter[1];
output.tessLevelOuter[2] = tessLevelOuter[2];
output.tessLevelInner[0] = tessLevelInner[0];
output.tessOuterLo = tessOuterLo;
output.tessOuterHi = tessOuterHi;
return output;
}
//----------------------------------------------------------
// Patches.DomainGregoryTriangle
//----------------------------------------------------------
[domain("tri")]
void ds_main_patches(
in HS_CONSTANT_FUNC_TRIANGLE_OUT input,
in OutputPatch<OsdPerPatchVertexBezier, 18> patch,
in float3 domainCoord : SV_DomainLocation,
out OutputVertex output )
{
float2 UV = OsdGetTessParameterizationTriangle(domainCoord.xy,
input.tessOuterLo,
input.tessOuterHi);
float3 P = float3(0,0,0), dPu = float3(0,0,0), dPv = float3(0,0,0);
float3 N = float3(0,0,0), dNu = float3(0,0,0), dNv = float3(0,0,0);
float3 cv[18];
for (int i = 0; i < 18; ++i) {
cv[i] = patch[i].P;
}
int3 patchParam = patch[0].patchParam;
OsdEvalPatchGregoryTriangle(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);
// all code below here is client code
output.position = mul(OsdModelViewMatrix(), float4(P, 1.0f));
output.normal = mul(OsdModelViewMatrix(), float4(N, 0.0f)).xyz;
output.tangent = mul(OsdModelViewMatrix(), float4(dPu, 0.0f)).xyz;
output.bitangent = mul(OsdModelViewMatrix(), float4(dPv, 0.0f)).xyz;
#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
output.Nu = dNu;
output.Nv = dNv;
#endif
output.patchCoord = OsdInterpolatePatchCoordTriangle(UV, patchParam);
OSD_DISPLACEMENT_CALLBACK;
output.positionOut = mul(OsdProjectionMatrix(), output.position);
output.edgeDistance = 0;
}

View File

@ -0,0 +1,404 @@
//
// Copyright 2013-2019 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
// ----------------------------------------------------------------------------
// Legacy Gregory
// ----------------------------------------------------------------------------
#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
#define M_PI 3.14159265359f
// precomputed catmark coefficient table up to valence 29
static float OsdCatmarkCoefficient[30] = {
0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
0.238688, 0.204544, 0.179229, 0.159657,
0.144042, 0.131276, 0.120632, 0.111614,
0.103872, 0.09715, 0.0912559, 0.0860444,
0.0814022, 0.0772401, 0.0734867, 0.0700842,
0.0669851, 0.0641504, 0.0615475, 0.0591488,
0.0569311, 0.0548745, 0.0529621
};
float
OsdComputeCatmarkCoefficient(int valence)
{
#if OSD_MAX_VALENCE < 30
return OsdCatmarkCoefficient[valence];
#else
if (valence < 30) {
return OsdCatmarkCoefficient[valence];
} else {
float t = 2.0f * float(M_PI) / float(valence);
return 1.0f / (valence * (cos(t) + 5.0f +
sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
}
#endif
}
float cosfn(int n, int j) {
return cos((2.0f * M_PI * j)/float(n));
}
float sinfn(int n, int j) {
return sin((2.0f * M_PI * j)/float(n));
}
#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
#undef OSD_MAX_VALENCE
#define OSD_MAX_VALENCE 4
#endif
struct OsdPerVertexGregory {
float3 P : POSITION0;
int3 clipFlag : CLIPFLAG;
int valence : BLENDINDICE0;
float3 e0 : POSITION1;
float3 e1 : POSITION2;
#ifdef OSD_PATCH_GREGORY_BOUNDARY
int zerothNeighbor : BLENDINDICE1;
float3 org : POSITION3;
#endif
float3 r[OSD_MAX_VALENCE] : POSITION4;
};
struct OsdPerPatchVertexGregory {
int3 patchParam: PATCHPARAM;
float3 P : POSITION0;
float3 Ep : POSITION1;
float3 Em : POSITION2;
float3 Fp : POSITION3;
float3 Fm : POSITION4;
};
#ifndef OSD_NUM_ELEMENTS
#define OSD_NUM_ELEMENTS 3
#endif
Buffer<float> OsdVertexBuffer : register( t2 );
Buffer<int> OsdValenceBuffer : register( t3 );
float3 OsdReadVertex(int vertexIndex)
{
int index = int(OSD_NUM_ELEMENTS * (vertexIndex /*+ OsdBaseVertex()*/));
return float3(OsdVertexBuffer[index],
OsdVertexBuffer[index+1],
OsdVertexBuffer[index+2]);
}
int OsdReadVertexValence(int vertexID)
{
int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
return OsdValenceBuffer[index];
}
int OsdReadVertexIndex(int vertexID, int valenceVertex)
{
int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
return OsdValenceBuffer[index];
}
Buffer<int> OsdQuadOffsetBuffer : register( t4 );
int OsdReadQuadOffset(int primitiveID, int offsetVertex)
{
int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
return OsdQuadOffsetBuffer[index];
}
void
OsdComputePerVertexGregory(int vID, float3 P, out OsdPerVertexGregory v)
{
v.clipFlag = int3(0,0,0);
int ivalence = OsdReadVertexValence(vID);
v.valence = ivalence;
int valence = abs(ivalence);
float3 f[OSD_MAX_VALENCE];
float3 pos = P;
float3 opos = float3(0,0,0);
#ifdef OSD_PATCH_GREGORY_BOUNDARY
v.org = pos;
int boundaryEdgeNeighbors[2];
int currNeighbor = 0;
int ibefore = 0;
int zerothNeighbor = 0;
#endif
for (int i=0; i<valence; ++i) {
int im = (i+valence-1)%valence;
int ip = (i+1)%valence;
int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
#ifdef OSD_PATCH_GREGORY_BOUNDARY
bool isBoundaryNeighbor = false;
int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
if (valenceNeighbor < 0) {
isBoundaryNeighbor = true;
if (currNeighbor<2) {
boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
}
currNeighbor++;
if (currNeighbor == 1) {
ibefore = i;
zerothNeighbor = i;
} else {
if (i-ibefore == 1) {
int tmp = boundaryEdgeNeighbors[0];
boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
boundaryEdgeNeighbors[1] = tmp;
zerothNeighbor = i;
}
}
}
#endif
float3 neighbor = OsdReadVertex(idx_neighbor);
int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
float3 diagonal = OsdReadVertex(idx_diagonal);
int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
float3 neighbor_p = OsdReadVertex(idx_neighbor_p);
int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
float3 neighbor_m = OsdReadVertex(idx_neighbor_m);
int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
float3 diagonal_m = OsdReadVertex(idx_diagonal_m);
f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
opos += f[i];
v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
}
opos /= valence;
v.P = float4(opos, 1.0f).xyz;
float3 e;
v.e0 = float3(0,0,0);
v.e1 = float3(0,0,0);
for(int iv=0; iv<valence; ++iv) {
int im = (iv + valence -1) % valence;
e = 0.5f * (f[iv] + f[im]);
v.e0 += cosfn(valence, iv)*e;
v.e1 += sinfn(valence, iv)*e;
}
float ef = OsdComputeCatmarkCoefficient(valence);
v.e0 *= ef;
v.e1 *= ef;
#ifdef OSD_PATCH_GREGORY_BOUNDARY
v.zerothNeighbor = zerothNeighbor;
if (currNeighbor == 1) {
boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
}
if (ivalence < 0) {
if (valence > 2) {
v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
OsdReadVertex(boundaryEdgeNeighbors[1]) +
4.0f * pos)/6.0f;
} else {
v.P = pos;
}
v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
float k = float(float(valence) - 1.0f); //k is the number of faces
float c = cos(M_PI/k);
float s = sin(M_PI/k);
float gamma = -(4.0f*s)/(3.0f*k+c);
float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
float beta_0 = s/(3.0f*k + c);
int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
float3 diagonal = OsdReadVertex(idx_diagonal);
v.e1 = gamma * pos +
alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
beta_0 * diagonal;
for (int x=1; x<valence - 1; ++x) {
int curri = ((x + zerothNeighbor)%valence);
float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
float3 neighbor = OsdReadVertex(idx_neighbor);
idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
diagonal = OsdReadVertex(idx_diagonal);
v.e1 += alpha * neighbor + beta * diagonal;
}
v.e1 /= 3.0f;
}
#endif
}
void
OsdComputePerPatchVertexGregory(int3 patchParam, int ID, int primitiveID,
in OsdPerVertexGregory v[4],
out OsdPerPatchVertexGregory result)
{
result.patchParam = patchParam;
result.P = v[ID].P;
int i = ID;
int ip = (i+1)%4;
int im = (i+3)%4;
int valence = abs(v[i].valence);
int n = valence;
int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
int np = abs(v[ip].valence);
int nm = abs(v[im].valence);
// Control Vertices based on :
// "Approximating Subdivision Surfaces with Gregory Patches
// for Hardware Tessellation"
// Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
//
// P3 e3- e2+ P2
// O--------O--------O--------O
// | | | |
// | | | |
// | | f3- | f2+ |
// | O O |
// e3+ O------O O------O e2-
// | f3+ f2- |
// | |
// | |
// | f0- f1+ |
// e0- O------O O------O e1+
// | O O |
// | | f0+ | f1- |
// | | | |
// | | | |
// O--------O--------O--------O
// P0 e0+ e1- P1
//
#ifdef OSD_PATCH_GREGORY_BOUNDARY
float3 Em_ip;
if (v[ip].valence < -2) {
int j = (np + prev_p - v[ip].zerothNeighbor) % np;
Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
} else {
Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p) + v[ip].e1*sinfn(np, prev_p);
}
float3 Ep_im;
if (v[im].valence < -2) {
int j = (nm + start_m - v[im].zerothNeighbor) % nm;
Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
} else {
Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
}
if (v[i].valence < 0) {
n = (n-1)*2;
}
if (v[im].valence < 0) {
nm = (nm-1)*2;
}
if (v[ip].valence < 0) {
np = (np-1)*2;
}
if (v[i].valence > 2) {
result.Ep = v[i].P + (v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start));
result.Em = v[i].P + (v[i].e0*cosfn(n, prev) + v[i].e1*sinfn(n, prev));
float s1=3-2*cosfn(n,1)-cosfn(np,1);
float s2=2*cosfn(n,1);
result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
} else if (v[i].valence < -2) {
int j = (valence + start - v[i].zerothNeighbor) % valence;
result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
j = (valence + prev - v[i].zerothNeighbor) % valence;
result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
float3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
float3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
float s2 = 2*cosfn(n,1);
result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
if (v[im].valence < 0) {
s1 = 3-2*cosfn(n,1)-cosfn(np,1);
result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
} else if (v[ip].valence < 0) {
s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
}
} else if (v[i].valence == -2) {
result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
}
#else // not OSD_PATCH_GREGORY_BOUNDARY
result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev );
float3 Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p) + v[ip].e1*sinfn(np, prev_p);
float3 Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
float s2 = 2*cosfn(n,1);
result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em +s2*Ep_im - v[i].r[prev])/3.0f;
#endif
}
#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY

View File

@ -36,6 +36,12 @@ namespace Osd {
static const char *commonShaderSource =
#include "hlslPatchCommon.gen.h"
;
static const char *commonTessShaderSource =
#include "hlslPatchCommonTess.gen.h"
;
static const char *patchLegacyShaderSource =
#include "hlslPatchLegacy.gen.h"
;
static const char *patchBasisTypesShaderSource =
#include "patchBasisCommonTypes.gen.h"
;
@ -45,6 +51,9 @@ static const char *patchBasisShaderSource =
static const char *patchBasisEvalShaderSource =
#include "patchBasisCommonEval.gen.h"
;
static const char *boxSplineTriangleShaderSource =
#include "hlslPatchBoxSplineTriangle.gen.h"
;
static const char *bsplineShaderSource =
#include "hlslPatchBSpline.gen.h"
;
@ -54,11 +63,18 @@ static const char *gregoryShaderSource =
static const char *gregoryBasisShaderSource =
#include "hlslPatchGregoryBasis.gen.h"
;
static const char *gregoryTriangleShaderSource =
#include "hlslPatchGregoryTriangle.gen.h"
;
/*static*/
std::string
HLSLPatchShaderSource::GetCommonShaderSource() {
return std::string(commonShaderSource);
std::stringstream ss;
ss << std::string(commonShaderSource);
ss << std::string(commonTessShaderSource);
ss << std::string(patchLegacyShaderSource);
return ss.str();
}
/*static*/
@ -80,6 +96,8 @@ HLSLPatchShaderSource::GetVertexShaderSource(Far::PatchDescriptor::Type type) {
switch (type) {
case Far::PatchDescriptor::REGULAR:
return bsplineShaderSource;
case Far::PatchDescriptor::LOOP:
return boxSplineTriangleShaderSource;
case Far::PatchDescriptor::GREGORY:
return gregoryShaderSource;
case Far::PatchDescriptor::GREGORY_BOUNDARY:
@ -87,6 +105,8 @@ HLSLPatchShaderSource::GetVertexShaderSource(Far::PatchDescriptor::Type type) {
+ std::string(gregoryShaderSource);
case Far::PatchDescriptor::GREGORY_BASIS:
return gregoryBasisShaderSource;
case Far::PatchDescriptor::GREGORY_TRIANGLE:
return gregoryTriangleShaderSource;
default:
break; // returns empty (points, lines, quads, ...)
}
@ -99,6 +119,8 @@ HLSLPatchShaderSource::GetHullShaderSource(Far::PatchDescriptor::Type type) {
switch (type) {
case Far::PatchDescriptor::REGULAR:
return bsplineShaderSource;
case Far::PatchDescriptor::LOOP:
return boxSplineTriangleShaderSource;
case Far::PatchDescriptor::GREGORY:
return gregoryShaderSource;
case Far::PatchDescriptor::GREGORY_BOUNDARY:
@ -106,6 +128,8 @@ HLSLPatchShaderSource::GetHullShaderSource(Far::PatchDescriptor::Type type) {
+ std::string(gregoryShaderSource);
case Far::PatchDescriptor::GREGORY_BASIS:
return gregoryBasisShaderSource;
case Far::PatchDescriptor::GREGORY_TRIANGLE:
return gregoryTriangleShaderSource;
default:
break; // returns empty (points, lines, quads, ...)
}
@ -118,6 +142,8 @@ HLSLPatchShaderSource::GetDomainShaderSource(Far::PatchDescriptor::Type type) {
switch (type) {
case Far::PatchDescriptor::REGULAR:
return bsplineShaderSource;
case Far::PatchDescriptor::LOOP:
return boxSplineTriangleShaderSource;
case Far::PatchDescriptor::GREGORY:
return gregoryShaderSource;
case Far::PatchDescriptor::GREGORY_BOUNDARY:
@ -125,6 +151,8 @@ HLSLPatchShaderSource::GetDomainShaderSource(Far::PatchDescriptor::Type type) {
+ std::string(gregoryShaderSource);
case Far::PatchDescriptor::GREGORY_BASIS:
return gregoryBasisShaderSource;
case Far::PatchDescriptor::GREGORY_TRIANGLE:
return gregoryTriangleShaderSource;
default:
break; // returns empty (points, lines, quads, ...)
}