mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2025-01-03 05:31:12 +00:00
Merge pull request #1 from PixarAnimationStudios/master
sync to 3.0.5 release
This commit is contained in:
commit
3f82cb6666
@ -42,9 +42,9 @@ before_script:
|
||||
- sudo apt-get install cmake
|
||||
- cmake --version
|
||||
|
||||
# install GL related libs from utopic
|
||||
- sudo add-apt-repository 'deb http://us.archive.ubuntu.com/ubuntu/ utopic main restricted universe multiverse'
|
||||
- sudo add-apt-repository 'deb http://us.archive.ubuntu.com/ubuntu/ utopic-updates main restricted universe multiverse'
|
||||
# install GL related libs from wily
|
||||
- sudo add-apt-repository 'deb http://us.archive.ubuntu.com/ubuntu/ wily main restricted universe multiverse'
|
||||
- sudo add-apt-repository 'deb http://us.archive.ubuntu.com/ubuntu/ wily-updates main restricted universe multiverse'
|
||||
- sudo apt-get update -qq
|
||||
# install glut and xxf86vm (for GL libs)
|
||||
- sudo apt-get install freeglut3-dev
|
||||
|
@ -703,21 +703,27 @@ macro(_add_glfw_executable target)
|
||||
|
||||
_add_possibly_cuda_executable(${target} ${ARGN})
|
||||
|
||||
if(WIN32)
|
||||
# Windows needs some of its dependency dll's copied into the same
|
||||
# directory as the executable.
|
||||
set( LIBRARIES ${GLFW_LIBRARIES})
|
||||
foreach (LIB ${LIBRARIES} )
|
||||
string(REPLACE ".lib" ".dll" DLL ${LIB})
|
||||
string(REPLACE ".LIB" ".DLL" DLL ${DLL})
|
||||
add_custom_command(
|
||||
TARGET ${target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${DLL}
|
||||
$<TARGET_FILE_DIR:${target}>
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
#
|
||||
# manuelk : commenting this out as it seems to be causing more problems than
|
||||
# it is worth. Moving glfw as a native git recursive dependency will make this
|
||||
# redundant anyway.
|
||||
#
|
||||
|
||||
# if(WIN32)
|
||||
# # Windows needs some of its dependency dll's copied into the same
|
||||
# # directory as the executable.
|
||||
# set(LIBRARIES ${GLFW_LIBRARIES})
|
||||
# foreach (LIB ${LIBRARIES} )
|
||||
# string(REPLACE ".lib" ".dll" DLL ${LIB})
|
||||
# string(REPLACE ".LIB" ".DLL" DLL ${DLL})
|
||||
# add_custom_command(
|
||||
# TARGET ${target} POST_BUILD
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
# ${DLL}
|
||||
# $<TARGET_FILE_DIR:${target}>
|
||||
# )
|
||||
# endforeach()
|
||||
# endif()
|
||||
|
||||
endmacro()
|
||||
|
||||
|
@ -31,6 +31,34 @@
|
||||
|
||||
----
|
||||
|
||||
Release 3.0.5
|
||||
=============
|
||||
|
||||
Release 3.0.5 is a minor stability release with performance and correctness bug fixes.
|
||||
|
||||
**Bug Fixes**
|
||||
- The previous release reduced transient memory use during PatchTable construction, but increased the amount of memory consumed by the resulting PatchTable itself, this regression has been fixed.
|
||||
- The example Ptex texture sampling code has been fixed to prevent sampling beyond the texels for a face when multisample rasterization is enabled.
|
||||
|
||||
Release 3.0.4
|
||||
=============
|
||||
|
||||
Release 3.0.4 is a minor stability release which includes important performance
|
||||
and bug fixes.
|
||||
|
||||
**New Features**
|
||||
- Added accessor methods to Far::LimitStencilTable to retrieve limit stencil data including derivative weights
|
||||
- Added support for OpenCL event control to Osd::CLVertexBuffer and Osd::CLEvaluator
|
||||
|
||||
**Changes**
|
||||
- Major reduction in memory use during Far::PatchTable construction for topologies with large numbers of extraordinary features
|
||||
- Improved performance for GL and D3D11 tessellation control / hull shader execution when drawing BSpline patches with the single crease patch optimization enabled
|
||||
|
||||
**Bug Fixes**
|
||||
- Restored support for drawing with fractional tessellation
|
||||
- Fixed far_tutorial_6 to refine primvar data only up to the number of levels produced by topological refinement
|
||||
- Fixed build warnings and errors reported by Visual Studio 2015
|
||||
|
||||
Release 3.0.3
|
||||
=============
|
||||
|
||||
|
@ -114,7 +114,7 @@ vec4 PtexLookupNearest(vec4 patchCoord,
|
||||
sampler2DArray data,
|
||||
isamplerBuffer packings)
|
||||
{
|
||||
vec2 uv = patchCoord.xy;
|
||||
vec2 uv = clamp(patchCoord.xy, vec2(0), vec2(1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID);
|
||||
vec2 coords = vec2(uv.x * ppack.width + ppack.uOffset,
|
||||
@ -127,7 +127,7 @@ vec4 PtexLookupNearest(vec4 patchCoord,
|
||||
sampler2DArray data,
|
||||
isamplerBuffer packings)
|
||||
{
|
||||
vec2 uv = patchCoord.xy;
|
||||
vec2 uv = clamp(patchCoord.xy, vec2(0), vec2(1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID, level);
|
||||
vec2 coords = vec2(uv.x * ppack.width + ppack.uOffset,
|
||||
@ -139,7 +139,7 @@ vec4 PtexLookupFast(vec4 patchCoord,
|
||||
sampler2DArray data,
|
||||
isamplerBuffer packings)
|
||||
{
|
||||
vec2 uv = patchCoord.xy;
|
||||
vec2 uv = clamp(patchCoord.xy, vec2(0), vec2(1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID);
|
||||
|
||||
@ -154,7 +154,7 @@ vec4 PtexLookupFast(vec4 patchCoord,
|
||||
sampler2DArray data,
|
||||
isamplerBuffer packings)
|
||||
{
|
||||
vec2 uv = patchCoord.xy;
|
||||
vec2 uv = clamp(patchCoord.xy, vec2(0), vec2(1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID, level);
|
||||
|
||||
@ -169,7 +169,7 @@ vec4 PtexLookup(vec4 patchCoord,
|
||||
sampler2DArray data,
|
||||
isamplerBuffer packings)
|
||||
{
|
||||
vec2 uv = patchCoord.xy;
|
||||
vec2 uv = clamp(patchCoord.xy, vec2(0), vec2(1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID, level);
|
||||
|
||||
@ -203,7 +203,7 @@ vec4 PtexLookupQuadratic(out vec4 du,
|
||||
sampler2DArray data,
|
||||
isamplerBuffer packings)
|
||||
{
|
||||
vec2 uv = patchCoord.xy;
|
||||
vec2 uv = clamp(patchCoord.xy, vec2(0), vec2(1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID, level);
|
||||
|
||||
|
@ -114,7 +114,7 @@ float4 PtexLookupNearest(float4 patchCoord,
|
||||
Texture2DArray data,
|
||||
Buffer<uint> packings)
|
||||
{
|
||||
float2 uv = patchCoord.xy;
|
||||
float2 uv = clamp(patchCoord.xy, float2(0,0), float2(1,1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID);
|
||||
float2 coords = float2(uv.x * ppack.width + ppack.uOffset,
|
||||
@ -127,7 +127,7 @@ float4 PtexLookupNearest(float4 patchCoord,
|
||||
Texture2DArray data,
|
||||
Buffer<uint> packings)
|
||||
{
|
||||
float2 uv = patchCoord.xy;
|
||||
float2 uv = clamp(patchCoord.xy, float2(0,0), float2(1,1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID, level);
|
||||
float2 coords = float2(uv.x * ppack.width + ppack.uOffset,
|
||||
@ -140,7 +140,7 @@ float4 PtexLookup(float4 patchCoord,
|
||||
Texture2DArray data,
|
||||
Buffer<uint> packings)
|
||||
{
|
||||
float2 uv = patchCoord.xy;
|
||||
float2 uv = clamp(patchCoord.xy, float2(0,0), float2(1,1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID, level);
|
||||
|
||||
@ -174,7 +174,7 @@ float4 PtexLookupQuadratic(out float4 du,
|
||||
Texture2DArray data,
|
||||
Buffer<uint> packings)
|
||||
{
|
||||
float2 uv = patchCoord.xy;
|
||||
float2 uv = clamp(patchCoord.xy, float2(0,0), float2(1,1));
|
||||
int faceID = int(patchCoord.w);
|
||||
PtexPacking ppack = getPtexPacking(packings, faceID, level);
|
||||
|
||||
|
@ -1403,8 +1403,8 @@ initHUD() {
|
||||
450, 10, callbackCheckBox, HUD_CB_ANIMATE_VERTICES, 'm');
|
||||
g_hud->AddCheckBox("Screen space LOD (V)", g_screenSpaceTess,
|
||||
450, 30, callbackCheckBox, HUD_CB_VIEW_LOD, 'v');
|
||||
//g_hud->AddCheckBox("Fractional spacing (T)", g_fractionalSpacing,
|
||||
// 450, 50, callbackCheckBox, HUD_CB_FRACTIONAL_SPACING, 't');
|
||||
g_hud->AddCheckBox("Fractional spacing (T)", g_fractionalSpacing,
|
||||
450, 50, callbackCheckBox, HUD_CB_FRACTIONAL_SPACING, 't');
|
||||
g_hud->AddCheckBox("Frustum Patch Culling (B)", g_patchCull,
|
||||
450, 70, callbackCheckBox, HUD_CB_PATCH_CULL, 'b');
|
||||
g_hud->AddCheckBox("Freeze (spc)", g_freeze,
|
||||
|
@ -1213,9 +1213,9 @@ initHUD() {
|
||||
g_hud->AddCheckBox("Screen space LOD (V)", g_screenSpaceTess != 0,
|
||||
10, y, callbackCheckBox, kHUD_CB_VIEW_LOD, 'V');
|
||||
y += 20;
|
||||
//g_hud->AddCheckBox("Fractional spacing (T)", g_fractionalSpacing != 0,
|
||||
// 10, y, callbackCheckBox, kHUD_CB_FRACTIONAL_SPACING, 'T');
|
||||
//y += 20;
|
||||
g_hud->AddCheckBox("Fractional spacing (T)", g_fractionalSpacing != 0,
|
||||
10, y, callbackCheckBox, kHUD_CB_FRACTIONAL_SPACING, 'T');
|
||||
y += 20;
|
||||
g_hud->AddCheckBox("Frustum Patch Culling (B)", g_patchCull != 0,
|
||||
10, y, callbackCheckBox, kHUD_CB_PATCH_CULL, 'B');
|
||||
y += 20;
|
||||
|
@ -42,10 +42,6 @@
|
||||
GLFWwindow* g_window=0;
|
||||
GLFWmonitor* g_primary=0;
|
||||
|
||||
#if _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#include <osd/cpuGLVertexBuffer.h>
|
||||
|
||||
#include <far/patchTableFactory.h>
|
||||
@ -70,6 +66,12 @@ GLFWmonitor* g_primary=0;
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstdio>
|
||||
|
||||
#if _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int g_level = 3,
|
||||
|
@ -1879,8 +1879,8 @@ int main(int argc, char ** argv) {
|
||||
10, 30, callbackCheckBox, HUD_CB_ANIMATE_VERTICES, 'm');
|
||||
g_hud.AddCheckBox("Screen space LOD (V)", g_screenSpaceTess,
|
||||
10, 50, callbackCheckBox, HUD_CB_VIEW_LOD, 'v');
|
||||
//g_hud.AddCheckBox("Fractional spacing (T)", g_fractionalSpacing,
|
||||
// 10, 70, callbackCheckBox, HUD_CB_FRACTIONAL_SPACING, 't');
|
||||
g_hud.AddCheckBox("Fractional spacing (T)", g_fractionalSpacing,
|
||||
10, 70, callbackCheckBox, HUD_CB_FRACTIONAL_SPACING, 't');
|
||||
g_hud.AddCheckBox("Frustum Patch Culling (B)", g_patchCull,
|
||||
10, 90, callbackCheckBox, HUD_CB_PATCH_CULL, 'b');
|
||||
g_hud.AddCheckBox("Bloom (Y)", g_bloom,
|
||||
|
@ -454,15 +454,15 @@ public:
|
||||
|
||||
void EnableVertexAttributes( ) {
|
||||
|
||||
long int offset = 0;
|
||||
GLvoid * offset = 0;
|
||||
for (AttrList::iterator i=_attrs.begin(); i!=_attrs.end(); ++i) {
|
||||
|
||||
glEnableVertexAttribArray( i->location );
|
||||
|
||||
glVertexAttribPointer( i->location, i->size,
|
||||
GL_FLOAT, GL_FALSE, sizeof(GLfloat) * _attrStride, (void*)offset);
|
||||
GL_FLOAT, GL_FALSE, sizeof(GLfloat) * _attrStride, (GLvoid*)offset);
|
||||
|
||||
offset += sizeof(GLfloat) * i->size;
|
||||
offset = (GLubyte*)offset + sizeof(GLfloat) * i->size;
|
||||
}
|
||||
}
|
||||
GLuint GetUniformScale() const {
|
||||
@ -645,6 +645,8 @@ linkDefaultPrograms() {
|
||||
g_samplesProgram.SetFragShaderSource(fsSrc);
|
||||
|
||||
g_samplesProgram.AddAttribute( "position",3 );
|
||||
g_samplesProgram.AddAttribute( "uTangent",3 );
|
||||
g_samplesProgram.AddAttribute( "vTangent",3 );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1461,9 +1461,9 @@ initHUD() {
|
||||
g_hud.AddCheckBox("Screen space LOD (V)", g_screenSpaceTess != 0,
|
||||
10, y, callbackCheckBox, kHUD_CB_VIEW_LOD, 'v');
|
||||
y += 20;
|
||||
//g_hud.AddCheckBox("Fractional spacing (T)", g_fractionalSpacing != 0,
|
||||
// 10, y, callbackCheckBox, kHUD_CB_FRACTIONAL_SPACING, 't');
|
||||
//y += 20;
|
||||
g_hud.AddCheckBox("Fractional spacing (T)", g_fractionalSpacing != 0,
|
||||
10, y, callbackCheckBox, kHUD_CB_FRACTIONAL_SPACING, 't');
|
||||
y += 20;
|
||||
g_hud.AddCheckBox("Frustum Patch Culling (B)", g_patchCull != 0,
|
||||
10, y, callbackCheckBox, kHUD_CB_PATCH_CULL, 'b');
|
||||
y += 20;
|
||||
|
@ -49,7 +49,10 @@ namespace {
|
||||
}
|
||||
|
||||
EndCapBSplineBasisPatchFactory::EndCapBSplineBasisPatchFactory(
|
||||
TopologyRefiner const & refiner) :
|
||||
TopologyRefiner const & refiner,
|
||||
StencilTable * vertexStencils,
|
||||
StencilTable * varyingStencils) :
|
||||
_vertexStencils(vertexStencils), _varyingStencils(varyingStencils),
|
||||
_refiner(&refiner), _numVertices(0), _numPatches(0) {
|
||||
|
||||
// Sanity check: the mesh must be adaptively refined
|
||||
@ -61,8 +64,14 @@ EndCapBSplineBasisPatchFactory::EndCapBSplineBasisPatchFactory(
|
||||
// finest level.
|
||||
int numMaxLevelFaces = refiner.GetLevel(refiner.GetMaxLevel()).GetNumFaces();
|
||||
|
||||
_vertexStencils.reserve(numMaxLevelFaces*16);
|
||||
_varyingStencils.reserve(numMaxLevelFaces*16);
|
||||
// we typically use 7 patch points for each bspline endcap.
|
||||
int numPatchPointsExpected = numMaxLevelFaces * 7;
|
||||
// limits to 100M (=800M bytes) entries for the reserved size.
|
||||
int numStencilsExpected = std::min(numPatchPointsExpected * 16,
|
||||
100*1024*1024);
|
||||
_vertexStencils->reserve(numPatchPointsExpected, numStencilsExpected);
|
||||
// varying stencils use only 1 index with weight=1.0
|
||||
_varyingStencils->reserve(numPatchPointsExpected, numPatchPointsExpected);
|
||||
}
|
||||
|
||||
ConstIndexArray
|
||||
@ -169,7 +178,7 @@ EndCapBSplineBasisPatchFactory::getPatchPointsFromGregoryBasis(
|
||||
p.AddWithWeight(H[i*4+k], Q[j][k]);
|
||||
}
|
||||
}
|
||||
_vertexStencils.push_back(p);
|
||||
GregoryBasis::AppendToStencilTable(p, _vertexStencils);
|
||||
}
|
||||
}
|
||||
int varyingIndices[] = { 0, 0, 1, 1,
|
||||
@ -177,12 +186,12 @@ EndCapBSplineBasisPatchFactory::getPatchPointsFromGregoryBasis(
|
||||
3, 3, 2, 2,
|
||||
3, 3, 2, 2,};
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
GregoryBasis::Point p(1);
|
||||
p.AddWithWeight(facePoints[varyingIndices[i]] + levelVertOffset, 1.0f);
|
||||
_varyingStencils.push_back(p);
|
||||
int varyingIndex = facePoints[varyingIndices[i]] + levelVertOffset;
|
||||
_varyingStencils->_sizes.push_back(1);
|
||||
_varyingStencils->_indices.push_back(varyingIndex);
|
||||
_varyingStencils->_weights.push_back(1.0f);
|
||||
}
|
||||
|
||||
|
||||
++_numPatches;
|
||||
return ConstIndexArray(&_patchPoints[(_numPatches-1)*16], 16);
|
||||
}
|
||||
@ -455,39 +464,38 @@ EndCapBSplineBasisPatchFactory::getPatchPoints(
|
||||
|
||||
int offset = _refiner->GetNumVerticesTotal();
|
||||
|
||||
GregoryBasis::Point V0, V1, V3;
|
||||
V0.AddWithWeight(facePoints[vid] + levelVertOffset, 1.0f);
|
||||
V1.AddWithWeight(facePoints[(vid+1)&3] + levelVertOffset, 1.0f);
|
||||
V3.AddWithWeight(facePoints[(vid+3)&3] + levelVertOffset, 1.0f);
|
||||
int varyingIndex0 = facePoints[vid] + levelVertOffset;
|
||||
int varyingIndex1 = facePoints[(vid+1)&3] + levelVertOffset;
|
||||
int varyingIndex3 = facePoints[(vid+3)&3] + levelVertOffset;
|
||||
|
||||
// push back to stencils;
|
||||
patchPoints[3* vid + 6] = (_numVertices++) + offset;
|
||||
_vertexStencils.push_back(X6);
|
||||
_varyingStencils.push_back(V0);
|
||||
GregoryBasis::AppendToStencilTable(X6, _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(varyingIndex0, _varyingStencils);
|
||||
|
||||
patchPoints[3*((vid+1)%4) + 4] = (_numVertices++) + offset;
|
||||
_vertexStencils.push_back(X7);
|
||||
_varyingStencils.push_back(V1);
|
||||
GregoryBasis::AppendToStencilTable(X7, _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(varyingIndex1, _varyingStencils);
|
||||
|
||||
patchPoints[3*((vid+1)%4) + 5] = (_numVertices++) + offset;
|
||||
_vertexStencils.push_back(X8);
|
||||
_varyingStencils.push_back(V1);
|
||||
GregoryBasis::AppendToStencilTable(X8, _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(varyingIndex1, _varyingStencils);
|
||||
|
||||
patchPoints[3* vid + 4] = (_numVertices++) + offset;
|
||||
_vertexStencils.push_back(X4);
|
||||
_varyingStencils.push_back(V0);
|
||||
GregoryBasis::AppendToStencilTable(X4, _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(varyingIndex0, _varyingStencils);
|
||||
|
||||
patchPoints[3*((vid+3)%4) + 6] = (_numVertices++) + offset;
|
||||
_vertexStencils.push_back(X15);
|
||||
_varyingStencils.push_back(V3);
|
||||
GregoryBasis::AppendToStencilTable(X15, _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(varyingIndex3, _varyingStencils);
|
||||
|
||||
patchPoints[3*((vid+3)%4) + 5] = (_numVertices++) + offset;
|
||||
_vertexStencils.push_back(X14);
|
||||
_varyingStencils.push_back(V3);
|
||||
GregoryBasis::AppendToStencilTable(X14, _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(varyingIndex3, _varyingStencils);
|
||||
|
||||
patchPoints[3*vid + 5] = (_numVertices++) + offset;
|
||||
_vertexStencils.push_back(X5);
|
||||
_varyingStencils.push_back(V0);
|
||||
GregoryBasis::AppendToStencilTable(X5, _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(varyingIndex0, _varyingStencils);
|
||||
|
||||
// reorder into UV row-column
|
||||
static int const permuteRegular[16] =
|
||||
|
@ -51,11 +51,19 @@ public:
|
||||
///
|
||||
/// @param refiner TopologyRefiner from which to generate patches
|
||||
///
|
||||
/// @param vertexStencils Output stencil table for the patch points
|
||||
/// (vertex interpolation)
|
||||
///
|
||||
/// @param varyingStencils Output stencil table for the patch points
|
||||
/// (varying interpolation)
|
||||
///
|
||||
/// @param shareBoundaryVertices Use same boundary vertices for neighboring
|
||||
/// patches. It reduces the number of stencils
|
||||
/// to be used.
|
||||
///
|
||||
EndCapBSplineBasisPatchFactory(TopologyRefiner const & refiner);
|
||||
EndCapBSplineBasisPatchFactory(TopologyRefiner const & refiner,
|
||||
StencilTable * vertexStencils,
|
||||
StencilTable * varyingStencils);
|
||||
|
||||
/// \brief Returns end patch point indices for \a faceIndex of \a level.
|
||||
/// Note that end patch points are not included in the vertices in
|
||||
@ -75,21 +83,6 @@ public:
|
||||
PatchTableFactory::PatchFaceTag const * levelPatchTags,
|
||||
int levelVertOffset);
|
||||
|
||||
/// \brief Create a StencilTable for end patch points, relative to the max
|
||||
/// subdivision level.
|
||||
///
|
||||
StencilTable* CreateVertexStencilTable() const {
|
||||
return GregoryBasis::CreateStencilTable(_vertexStencils);
|
||||
}
|
||||
|
||||
/// \brief Create a StencilTable for end patch varying primvar.
|
||||
/// This table is used as a convenient way to get varying primvars
|
||||
/// populated on end patch points along with positions.
|
||||
///
|
||||
StencilTable* CreateVaryingStencilTable() const {
|
||||
return GregoryBasis::CreateStencilTable(_varyingStencils);
|
||||
}
|
||||
|
||||
private:
|
||||
ConstIndexArray getPatchPointsFromGregoryBasis(
|
||||
Vtr::internal::Level const * level, Index thisFace,
|
||||
@ -106,10 +99,10 @@ private:
|
||||
ConstIndexArray facePoints, int vid,
|
||||
GregoryBasis::Point *P, GregoryBasis::Point *Ep, GregoryBasis::Point *Em);
|
||||
|
||||
StencilTable * _vertexStencils;
|
||||
StencilTable * _varyingStencils;
|
||||
|
||||
TopologyRefiner const *_refiner;
|
||||
GregoryBasis::PointsVector _vertexStencils;
|
||||
GregoryBasis::PointsVector _varyingStencils;
|
||||
int _numVertices;
|
||||
int _numPatches;
|
||||
std::vector<Index> _patchPoints;
|
||||
|
@ -41,7 +41,11 @@ namespace Far {
|
||||
// EndCapGregoryBasisPatchFactory for Vertex StencilTable
|
||||
//
|
||||
EndCapGregoryBasisPatchFactory::EndCapGregoryBasisPatchFactory(
|
||||
TopologyRefiner const & refiner, bool shareBoundaryVertices) :
|
||||
TopologyRefiner const & refiner,
|
||||
StencilTable * vertexStencils,
|
||||
StencilTable * varyingStencils,
|
||||
bool shareBoundaryVertices) :
|
||||
_vertexStencils(vertexStencils), _varyingStencils(varyingStencils),
|
||||
_refiner(&refiner), _shareBoundaryVertices(shareBoundaryVertices),
|
||||
_numGregoryBasisVertices(0), _numGregoryBasisPatches(0) {
|
||||
|
||||
@ -54,8 +58,13 @@ EndCapGregoryBasisPatchFactory::EndCapGregoryBasisPatchFactory(
|
||||
// finest level.
|
||||
int numMaxLevelFaces = refiner.GetLevel(refiner.GetMaxLevel()).GetNumFaces();
|
||||
|
||||
_vertexStencils.reserve(numMaxLevelFaces*20);
|
||||
_varyingStencils.reserve(numMaxLevelFaces*20);
|
||||
int numPatchPointsExpected = numMaxLevelFaces * 20;
|
||||
// limits to 100M (=800M bytes) entries for the reserved size.
|
||||
int numStencilsExpected = std::min(numPatchPointsExpected * 16,
|
||||
100*1024*1024);
|
||||
_vertexStencils->reserve(numPatchPointsExpected, numStencilsExpected);
|
||||
// varying stencils use only 1 index with weight=1.0
|
||||
_varyingStencils->reserve(numPatchPointsExpected, numPatchPointsExpected);
|
||||
}
|
||||
|
||||
//
|
||||
@ -90,24 +99,24 @@ EndCapGregoryBasisPatchFactory::addPatchBasis(Index faceIndex,
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (verticesMask[i][0]) {
|
||||
_vertexStencils.push_back(basis.P[i]);
|
||||
_varyingStencils.push_back(basis.V[i]);
|
||||
GregoryBasis::AppendToStencilTable(basis.P[i], _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
||||
}
|
||||
if (verticesMask[i][1]) {
|
||||
_vertexStencils.push_back(basis.Ep[i]);
|
||||
_varyingStencils.push_back(basis.V[i]);
|
||||
GregoryBasis::AppendToStencilTable(basis.Ep[i], _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
||||
}
|
||||
if (verticesMask[i][2]) {
|
||||
_vertexStencils.push_back(basis.Em[i]);
|
||||
_varyingStencils.push_back(basis.V[i]);
|
||||
GregoryBasis::AppendToStencilTable(basis.Em[i], _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
||||
}
|
||||
if (verticesMask[i][3]) {
|
||||
_vertexStencils.push_back(basis.Fp[i]);
|
||||
_varyingStencils.push_back(basis.V[i]);
|
||||
GregoryBasis::AppendToStencilTable(basis.Fp[i], _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
||||
}
|
||||
if (verticesMask[i][4]) {
|
||||
_vertexStencils.push_back(basis.Fm[i]);
|
||||
_varyingStencils.push_back(basis.V[i]);
|
||||
GregoryBasis::AppendToStencilTable(basis.Fm[i], _vertexStencils);
|
||||
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "../far/patchTableFactory.h"
|
||||
#include "../far/gregoryBasis.h"
|
||||
#include "../far/stencilTable.h"
|
||||
#include "../vtr/level.h"
|
||||
|
||||
namespace OpenSubdiv {
|
||||
@ -74,11 +75,19 @@ public:
|
||||
///
|
||||
/// @param refiner TopologyRefiner from which to generate patches
|
||||
///
|
||||
/// @param vertexStencils Output stencil table for the patch points
|
||||
/// (vertex interpolation)
|
||||
///
|
||||
/// @param varyingStencils Output stencil table for the patch points
|
||||
/// (varying interpolation)
|
||||
///
|
||||
/// @param shareBoundaryVertices Use same boundary vertices for neighboring
|
||||
/// patches. It reduces the number of stencils
|
||||
/// to be used.
|
||||
///
|
||||
EndCapGregoryBasisPatchFactory(TopologyRefiner const & refiner,
|
||||
StencilTable *vertexStencils,
|
||||
StencilTable *varyingStencils,
|
||||
bool shareBoundaryVertices=true);
|
||||
|
||||
/// \brief Returns end patch point indices for \a faceIndex of \a level.
|
||||
@ -99,21 +108,6 @@ public:
|
||||
PatchTableFactory::PatchFaceTag const * levelPatchTags,
|
||||
int levelVertOffset);
|
||||
|
||||
/// \brief Create a StencilTable for end patch points, relative to the max
|
||||
/// subdivision level.
|
||||
///
|
||||
StencilTable* CreateVertexStencilTable() const {
|
||||
return GregoryBasis::CreateStencilTable(_vertexStencils);
|
||||
}
|
||||
|
||||
/// \brief Create a StencilTable for end patch varying primvar.
|
||||
/// This table is used as a convenient way to get varying primvars
|
||||
/// populated on end patch points along with positions.
|
||||
///
|
||||
StencilTable* CreateVaryingStencilTable() const {
|
||||
return GregoryBasis::CreateStencilTable(_varyingStencils);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Creates a basis for the vertices specified in mask on the face and
|
||||
@ -121,8 +115,8 @@ private:
|
||||
bool addPatchBasis(Index faceIndex, bool newVerticesMask[4][5],
|
||||
int levelVertOffset);
|
||||
|
||||
GregoryBasis::PointsVector _vertexStencils;
|
||||
GregoryBasis::PointsVector _varyingStencils;
|
||||
StencilTable *_vertexStencils;
|
||||
StencilTable *_varyingStencils;
|
||||
|
||||
TopologyRefiner const *_refiner;
|
||||
bool _shareBoundaryVertices;
|
||||
|
@ -126,7 +126,6 @@ GregoryBasis::ProtoBasis::ProtoBasis(
|
||||
P[i].Clear(stencilCapacity);
|
||||
e0[i].Clear(stencilCapacity);
|
||||
e1[i].Clear(stencilCapacity);
|
||||
V[i].Clear(1);
|
||||
}
|
||||
|
||||
Vtr::internal::StackBuffer<Index, 40> manifoldRings[4];
|
||||
@ -142,7 +141,7 @@ GregoryBasis::ProtoBasis::ProtoBasis(
|
||||
|
||||
for (int vid=0; vid<4; ++vid) {
|
||||
// save for varying stencils
|
||||
V[vid].AddWithWeight(facePoints[vid], 1.0f);
|
||||
varyingIndex[vid] = facePoints[vid] + levelVertOffset;
|
||||
|
||||
int ringSize =
|
||||
level.gatherQuadRegularRingAroundVertex(
|
||||
@ -445,41 +444,9 @@ GregoryBasis::ProtoBasis::ProtoBasis(
|
||||
Em[i].OffsetIndices(levelVertOffset);
|
||||
Fp[i].OffsetIndices(levelVertOffset);
|
||||
Fm[i].OffsetIndices(levelVertOffset);
|
||||
V[i].OffsetIndices(levelVertOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/
|
||||
StencilTable *
|
||||
GregoryBasis::CreateStencilTable(PointsVector const &stencils) {
|
||||
|
||||
int nStencils = (int)stencils.size();
|
||||
if (nStencils == 0) return NULL;
|
||||
|
||||
int nElements = 0;
|
||||
for (int i = 0; i < nStencils; ++i) {
|
||||
nElements += stencils[i].GetSize();
|
||||
}
|
||||
|
||||
// allocate destination
|
||||
StencilTable *stencilTable = new StencilTable();
|
||||
|
||||
// XXX: do we need numControlVertices in stencilTable?
|
||||
stencilTable->_numControlVertices = 0;
|
||||
stencilTable->resize(nStencils, nElements);
|
||||
|
||||
int * sizes = &stencilTable->_sizes[0];
|
||||
Index * indices = &stencilTable->_indices[0];
|
||||
float * weights = &stencilTable->_weights[0];
|
||||
|
||||
for (int i = 0; i < nStencils; ++i) {
|
||||
stencils[i].Copy(&sizes, &indices, &weights);
|
||||
}
|
||||
stencilTable->generateOffsets();
|
||||
|
||||
return stencilTable;
|
||||
}
|
||||
|
||||
} // end namespace Far
|
||||
|
||||
} // end namespace OPENSUBDIV_VERSION
|
||||
|
@ -161,8 +161,14 @@ public:
|
||||
++(*size);
|
||||
}
|
||||
|
||||
private:
|
||||
Vtr::Index GetStencilIndex(int index) const {
|
||||
return _stencils[index].index;
|
||||
}
|
||||
float GetStencilWeight(int index) const {
|
||||
return _stencils[index].weight;
|
||||
}
|
||||
|
||||
private:
|
||||
int _size;
|
||||
|
||||
struct Stencil {
|
||||
@ -219,12 +225,28 @@ public:
|
||||
Point P[4], Ep[4], Em[4], Fp[4], Fm[4];
|
||||
|
||||
// for varying interpolation
|
||||
Point V[4];
|
||||
Vtr::Index varyingIndex[4];
|
||||
};
|
||||
|
||||
typedef std::vector<GregoryBasis::Point> PointsVector;
|
||||
|
||||
static StencilTable *CreateStencilTable(PointsVector const &stencils);
|
||||
// for basis point stencil
|
||||
static void AppendToStencilTable(GregoryBasis::Point const &p,
|
||||
StencilTable *table) {
|
||||
int size = p.GetSize();
|
||||
table->_sizes.push_back(size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
table->_indices.push_back(p.GetStencilIndex(i));
|
||||
table->_weights.push_back(p.GetStencilWeight(i));
|
||||
}
|
||||
}
|
||||
|
||||
// for varying stencil (just copy)
|
||||
static void AppendToStencilTable(int index, StencilTable *table) {
|
||||
table->_sizes.push_back(1);
|
||||
table->_indices.push_back(index);
|
||||
table->_weights.push_back(1.f);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
@ -1122,14 +1122,26 @@ PatchTableFactory::populateAdaptivePatches(
|
||||
EndCapBSplineBasisPatchFactory *endCapBSpline = NULL;
|
||||
EndCapGregoryBasisPatchFactory *endCapGregoryBasis = NULL;
|
||||
EndCapLegacyGregoryPatchFactory *endCapLegacyGregory = NULL;
|
||||
StencilTable *localPointStencils = NULL;
|
||||
StencilTable *localPointVaryingStencils = NULL;
|
||||
|
||||
switch(context.options.GetEndCapType()) {
|
||||
case Options::ENDCAP_GREGORY_BASIS:
|
||||
localPointStencils = new StencilTable(0);
|
||||
localPointVaryingStencils = new StencilTable(0);
|
||||
endCapGregoryBasis = new EndCapGregoryBasisPatchFactory(
|
||||
refiner, context.options.shareEndCapPatchPoints);
|
||||
refiner,
|
||||
localPointStencils,
|
||||
localPointVaryingStencils,
|
||||
context.options.shareEndCapPatchPoints);
|
||||
break;
|
||||
case Options::ENDCAP_BSPLINE_BASIS:
|
||||
endCapBSpline = new EndCapBSplineBasisPatchFactory(refiner);
|
||||
localPointStencils = new StencilTable(0);
|
||||
localPointVaryingStencils = new StencilTable(0);
|
||||
endCapBSpline = new EndCapBSplineBasisPatchFactory(
|
||||
refiner,
|
||||
localPointStencils,
|
||||
localPointVaryingStencils);
|
||||
break;
|
||||
case Options::ENDCAP_LEGACY_GREGORY:
|
||||
endCapLegacyGregory = new EndCapLegacyGregoryPatchFactory(refiner);
|
||||
@ -1291,19 +1303,29 @@ PatchTableFactory::populateAdaptivePatches(
|
||||
}
|
||||
|
||||
// finalize end patches
|
||||
if (localPointStencils and localPointStencils->GetNumStencils() > 0) {
|
||||
localPointStencils->finalize();
|
||||
} else {
|
||||
delete localPointStencils;
|
||||
localPointStencils = NULL;
|
||||
}
|
||||
|
||||
if (localPointVaryingStencils and localPointVaryingStencils->GetNumStencils() > 0) {
|
||||
localPointVaryingStencils->finalize();
|
||||
} else {
|
||||
delete localPointVaryingStencils;
|
||||
localPointVaryingStencils = NULL;
|
||||
}
|
||||
|
||||
switch(context.options.GetEndCapType()) {
|
||||
case Options::ENDCAP_GREGORY_BASIS:
|
||||
table->_localPointStencils =
|
||||
endCapGregoryBasis->CreateVertexStencilTable();
|
||||
table->_localPointVaryingStencils =
|
||||
endCapGregoryBasis->CreateVaryingStencilTable();
|
||||
table->_localPointStencils = localPointStencils;
|
||||
table->_localPointVaryingStencils = localPointVaryingStencils;
|
||||
delete endCapGregoryBasis;
|
||||
break;
|
||||
case Options::ENDCAP_BSPLINE_BASIS:
|
||||
table->_localPointStencils =
|
||||
endCapBSpline->CreateVertexStencilTable();
|
||||
table->_localPointVaryingStencils =
|
||||
endCapBSpline->CreateVaryingStencilTable();
|
||||
table->_localPointStencils = localPointStencils;
|
||||
table->_localPointVaryingStencils = localPointVaryingStencils;
|
||||
delete endCapBSpline;
|
||||
break;
|
||||
case Options::ENDCAP_LEGACY_GREGORY:
|
||||
|
@ -205,6 +205,15 @@ protected:
|
||||
// Resize the table arrays (factory helper)
|
||||
void resize(int nstencils, int nelems);
|
||||
|
||||
// Reserves the table arrays (factory helper)
|
||||
void reserve(int nstencils, int nelems);
|
||||
|
||||
// Reallocates the table arrays to remove excess capacity (factory helper)
|
||||
void shrinkToFit();
|
||||
|
||||
// Performs any final operations on internal tables (factory helper)
|
||||
void finalize();
|
||||
|
||||
protected:
|
||||
StencilTable() : _numControlVertices(0) {}
|
||||
StencilTable(int numControlVerts)
|
||||
@ -212,8 +221,12 @@ protected:
|
||||
{ }
|
||||
|
||||
friend class StencilTableFactory;
|
||||
friend class PatchTableFactory;
|
||||
// XXX: temporarily, GregoryBasis class will go away.
|
||||
friend class GregoryBasis;
|
||||
// XXX: needed to call reserve().
|
||||
friend class EndCapBSplineBasisPatchFactory;
|
||||
friend class EndCapGregoryBasisPatchFactory;
|
||||
|
||||
int _numControlVertices; // number of control vertices
|
||||
|
||||
@ -297,6 +310,12 @@ class LimitStencilTable : public StencilTable {
|
||||
|
||||
public:
|
||||
|
||||
/// \brief Returns a LimitStencil at index i in the table
|
||||
LimitStencil GetLimitStencil(Index i) const;
|
||||
|
||||
/// \brief Returns the limit stencil at index i in the table
|
||||
LimitStencil operator[] (Index index) const;
|
||||
|
||||
/// \brief Returns the 'u' derivative stencil interpolation weights
|
||||
std::vector<float> const & GetDuWeights() const {
|
||||
return _duWeights;
|
||||
@ -399,6 +418,26 @@ StencilTable::resize(int nstencils, int nelems) {
|
||||
_weights.resize(nelems);
|
||||
}
|
||||
|
||||
inline void
|
||||
StencilTable::reserve(int nstencils, int nelems) {
|
||||
_sizes.reserve(nstencils);
|
||||
_indices.reserve(nelems);
|
||||
_weights.reserve(nelems);
|
||||
}
|
||||
|
||||
inline void
|
||||
StencilTable::shrinkToFit() {
|
||||
std::vector<int>(_sizes).swap(_sizes);
|
||||
std::vector<Index>(_indices).swap(_indices);
|
||||
std::vector<float>(_weights).swap(_weights);
|
||||
}
|
||||
|
||||
inline void
|
||||
StencilTable::finalize() {
|
||||
shrinkToFit();
|
||||
generateOffsets();
|
||||
}
|
||||
|
||||
// Returns a Stencil at index i in the table
|
||||
inline Stencil
|
||||
StencilTable::GetStencil(Index i) const {
|
||||
@ -423,6 +462,25 @@ LimitStencilTable::resize(int nstencils, int nelems) {
|
||||
_dvWeights.resize(nelems);
|
||||
}
|
||||
|
||||
// Returns a LimitStencil at index i in the table
|
||||
inline LimitStencil
|
||||
LimitStencilTable::GetLimitStencil(Index i) const {
|
||||
assert((not GetOffsets().empty()) and i<(int)GetOffsets().size());
|
||||
|
||||
Index ofs = GetOffsets()[i];
|
||||
|
||||
return LimitStencil( const_cast<int *>(&GetSizes()[i]),
|
||||
const_cast<Index *>(&GetControlIndices()[ofs]),
|
||||
const_cast<float *>(&GetWeights()[ofs]),
|
||||
const_cast<float *>(&GetDuWeights()[ofs]),
|
||||
const_cast<float *>(&GetDvWeights()[ofs]) );
|
||||
}
|
||||
|
||||
inline LimitStencil
|
||||
LimitStencilTable::operator[] (Index index) const {
|
||||
return GetLimitStencil(index);
|
||||
}
|
||||
|
||||
|
||||
} // end namespace Far
|
||||
|
||||
|
@ -212,7 +212,8 @@ StencilTableFactory::AppendLocalPointStencilTable(
|
||||
|
||||
// factorize and append.
|
||||
if (baseStencilTable == NULL or
|
||||
localPointStencilTable == NULL) return NULL;
|
||||
localPointStencilTable == NULL or
|
||||
localPointStencilTable->GetNumStencils() == 0) return NULL;
|
||||
|
||||
// baseStencilTable can be built with or without singular stencils
|
||||
// (single weight of 1.0f) as place-holders for coarse mesh vertices.
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
bool IsUniform() const { return _isUniform; }
|
||||
|
||||
/// \brief Returns the number of refinement levels
|
||||
int GetNumLevels() const { return (int)_levels.size(); }
|
||||
int GetNumLevels() const { return (int)_farLevels.size(); }
|
||||
|
||||
/// \brief Returns the highest level of refinement
|
||||
int GetMaxLevel() const { return _maxLevel; }
|
||||
|
@ -204,8 +204,11 @@ D3D11ComputeEvaluator::Compile(BufferDescriptor const &srcDesc,
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS
|
||||
| D3D10_SHADER_RESOURCES_MAY_ALIAS;
|
||||
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
#if defined(D3D10_SHADER_RESOURCES_MAY_ALIAS)
|
||||
dwShaderFlags |= D3D10_SHADER_RESOURCES_MAY_ALIAS;
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
dwShaderFlags |= D3DCOMPILE_DEBUG;
|
||||
#endif
|
||||
|
@ -75,7 +75,7 @@ D3D11VertexBuffer::UpdateData(const float *src, int startVertex, int numVertices
|
||||
return;
|
||||
}
|
||||
|
||||
int size = GetNumElements() * numVertices * sizeof(float);
|
||||
unsigned int size = GetNumElements() * numVertices * sizeof(float);
|
||||
|
||||
memcpy((float*)resource.pData + startVertex * _numElements, src, size);
|
||||
|
||||
|
@ -72,14 +72,16 @@
|
||||
// mix(input[c].var, input[d].var, UV.x), UV.y)
|
||||
#endif
|
||||
|
||||
// XXXdyu-patch-drawing support for fractional spacing
|
||||
#undef OSD_FRACTIONAL_ODD_SPACING
|
||||
// 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_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 +623,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 +801,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 +900,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 +909,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 +922,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 +947,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)
|
||||
@ -941,19 +1092,27 @@ OsdFlipMatrix(mat4 m)
|
||||
m[0][3], m[0][2], m[0][1], m[0][0]);
|
||||
}
|
||||
|
||||
// Regular BSpline to Bezier
|
||||
uniform mat4 Q = 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, 1.f/6.f, 4.f/6.f, 1.f/6.f
|
||||
);
|
||||
|
||||
// Infinitely Sharp (boundary)
|
||||
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
|
||||
);
|
||||
|
||||
// convert BSpline cv to Bezier cv
|
||||
void
|
||||
OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
|
||||
out OsdPerPatchVertexBezier result)
|
||||
{
|
||||
// Regular BSpline to Bezier
|
||||
mat4 Q = 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, 1.f/6.f, 4.f/6.f, 1.f/6.f
|
||||
);
|
||||
|
||||
result.patchParam = patchParam;
|
||||
|
||||
int i = ID%4;
|
||||
@ -961,15 +1120,10 @@ OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
|
||||
|
||||
#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
|
||||
|
||||
// Infinitely Sharp (boundary)
|
||||
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
|
||||
);
|
||||
vec3 P = vec3(0); // 0 to 1-2^(-Sf)
|
||||
vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
|
||||
vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
|
||||
|
||||
mat4 Mj, Ms;
|
||||
float sharpness = OsdGetPatchSharpness(patchParam);
|
||||
if (sharpness > 0) {
|
||||
float Sf = floor(sharpness);
|
||||
@ -977,59 +1131,74 @@ OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
|
||||
float Sr = fract(sharpness);
|
||||
mat4 Mf = OsdComputeMs(Sf);
|
||||
mat4 Mc = OsdComputeMs(Sc);
|
||||
Mj = (1-Sr) * Mf + Sr * Mi;
|
||||
Ms = (1-Sr) * Mf + Sr * Mc;
|
||||
mat4 Mj = (1-Sr) * Mf + Sr * Mi;
|
||||
mat4 Ms = (1-Sr) * Mf + Sr * Mc;
|
||||
float s0 = 1 - pow(2, -floor(sharpness));
|
||||
float s1 = 1 - pow(2, -ceil(sharpness));
|
||||
result.vSegments = vec2(s0, s1);
|
||||
} else {
|
||||
Mj = Ms = Mi;
|
||||
result.vSegments = vec2(0);
|
||||
}
|
||||
result.P = vec3(0); // 0 to 1-2^(-Sf)
|
||||
result.P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
|
||||
result.P2 = vec3(0); // 1-2^(-Sc) to 1
|
||||
|
||||
mat4 MUi, MUj, MUs;
|
||||
mat4 MVi, MVj, MVs;
|
||||
MUi = MUj = MUs = Q;
|
||||
MVi = MVj = MVs = Q;
|
||||
mat4 MUi = Q, MUj = Q, MUs = Q;
|
||||
mat4 MVi = Q, MVj = Q, MVs = Q;
|
||||
|
||||
int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
|
||||
if ((boundaryMask & 1) != 0) {
|
||||
MVi = OsdFlipMatrix(Mi);
|
||||
MVj = OsdFlipMatrix(Mj);
|
||||
MVs = OsdFlipMatrix(Ms);
|
||||
}
|
||||
if ((boundaryMask & 2) != 0) {
|
||||
MUi = Mi;
|
||||
MUj = Mj;
|
||||
MUs = Ms;
|
||||
}
|
||||
if ((boundaryMask & 4) != 0) {
|
||||
MVi = Mi;
|
||||
MVj = Mj;
|
||||
MVs = Ms;
|
||||
}
|
||||
if ((boundaryMask & 8) != 0) {
|
||||
MUi = OsdFlipMatrix(Mi);
|
||||
MUj = OsdFlipMatrix(Mj);
|
||||
MUs = OsdFlipMatrix(Ms);
|
||||
}
|
||||
|
||||
vec3 Hi[4], Hj[4], Hs[4];
|
||||
for (int l=0; l<4; ++l) {
|
||||
Hi[l] = Hj[l] = Hs[l] = vec3(0);
|
||||
for (int k=0; k<4; ++k) {
|
||||
Hi[l] += MUi[i][k] * cv[l*4 + k];
|
||||
Hj[l] += MUj[i][k] * cv[l*4 + k];
|
||||
Hs[l] += MUs[i][k] * cv[l*4 + k];
|
||||
int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
|
||||
if ((boundaryMask & 1) != 0) {
|
||||
MVi = OsdFlipMatrix(Mi);
|
||||
MVj = OsdFlipMatrix(Mj);
|
||||
MVs = OsdFlipMatrix(Ms);
|
||||
}
|
||||
}
|
||||
for (int k=0; k<4; ++k) {
|
||||
result.P += MVi[j][k]*Hi[k];
|
||||
result.P1 += MVj[j][k]*Hj[k];
|
||||
result.P2 += MVs[j][k]*Hs[k];
|
||||
if ((boundaryMask & 2) != 0) {
|
||||
MUi = Mi;
|
||||
MUj = Mj;
|
||||
MUs = Ms;
|
||||
}
|
||||
if ((boundaryMask & 4) != 0) {
|
||||
MVi = Mi;
|
||||
MVj = Mj;
|
||||
MVs = Ms;
|
||||
}
|
||||
if ((boundaryMask & 8) != 0) {
|
||||
MUi = OsdFlipMatrix(Mi);
|
||||
MUj = OsdFlipMatrix(Mj);
|
||||
MUs = OsdFlipMatrix(Ms);
|
||||
}
|
||||
|
||||
vec3 Hi[4], Hj[4], Hs[4];
|
||||
for (int l=0; l<4; ++l) {
|
||||
Hi[l] = Hj[l] = Hs[l] = vec3(0);
|
||||
for (int k=0; k<4; ++k) {
|
||||
Hi[l] += MUi[i][k] * cv[l*4 + k];
|
||||
Hj[l] += MUj[i][k] * cv[l*4 + k];
|
||||
Hs[l] += MUs[i][k] * cv[l*4 + k];
|
||||
}
|
||||
}
|
||||
for (int k=0; k<4; ++k) {
|
||||
P += MVi[j][k]*Hi[k];
|
||||
P1 += MVj[j][k]*Hj[k];
|
||||
P2 += MVs[j][k]*Hs[k];
|
||||
}
|
||||
|
||||
result.P = P;
|
||||
result.P1 = P1;
|
||||
result.P2 = P2;
|
||||
} else {
|
||||
result.vSegments = vec2(0);
|
||||
|
||||
OsdComputeBSplineBoundaryPoints(cv, patchParam);
|
||||
|
||||
vec3 Hi[4];
|
||||
for (int l=0; l<4; ++l) {
|
||||
Hi[l] = vec3(0);
|
||||
for (int k=0; k<4; ++k) {
|
||||
Hi[l] += Q[i][k] * cv[l*4 + k];
|
||||
}
|
||||
}
|
||||
for (int k=0; k<4; ++k) {
|
||||
P += Q[j][k]*Hi[k];
|
||||
}
|
||||
|
||||
result.P = P;
|
||||
result.P1 = P;
|
||||
result.P2 = P;
|
||||
}
|
||||
#else
|
||||
OsdComputeBSplineBoundaryPoints(cv, patchParam);
|
||||
|
@ -26,14 +26,16 @@
|
||||
// Patches.Common
|
||||
//----------------------------------------------------------
|
||||
|
||||
// XXXdyu-patch-drawing support for fractional spacing
|
||||
#undef OSD_FRACTIONAL_ODD_SPACING
|
||||
// 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_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 +496,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 +676,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 +774,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 +783,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 +796,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 +821,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)
|
||||
@ -815,19 +966,27 @@ OsdFlipMatrix(float4x4 m)
|
||||
m[0][3], m[0][2], m[0][1], m[0][0]);
|
||||
}
|
||||
|
||||
// Regular BSpline to Bezier
|
||||
static float4x4 Q = {
|
||||
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, 1.f/6.f, 4.f/6.f, 1.f/6.f
|
||||
};
|
||||
|
||||
// Infinitely Sharp (boundary)
|
||||
static float4x4 Mi = {
|
||||
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
|
||||
};
|
||||
|
||||
// convert BSpline cv to Bezier cv
|
||||
void
|
||||
OsdComputePerPatchVertexBSpline(int3 patchParam, int ID, float3 cv[16],
|
||||
out OsdPerPatchVertexBezier result)
|
||||
{
|
||||
// Regular BSpline to Bezier
|
||||
float4x4 Q = {
|
||||
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, 1.f/6.f, 4.f/6.f, 1.f/6.f
|
||||
};
|
||||
|
||||
result.patchParam = patchParam;
|
||||
|
||||
int i = ID%4;
|
||||
@ -835,15 +994,10 @@ OsdComputePerPatchVertexBSpline(int3 patchParam, int ID, float3 cv[16],
|
||||
|
||||
#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
|
||||
|
||||
// Infinitely Sharp (boundary)
|
||||
float4x4 Mi = {
|
||||
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
|
||||
};
|
||||
float3 P = float3(0,0,0); // 0 to 1-2^(-Sf)
|
||||
float3 P1 = float3(0,0,0); // 1-2^(-Sf) to 1-2^(-Sc)
|
||||
float3 P2 = float3(0,0,0); // 1-2^(-Sc) to 1
|
||||
|
||||
float4x4 Mj, Ms;
|
||||
float sharpness = OsdGetPatchSharpness(patchParam);
|
||||
if (sharpness > 0) {
|
||||
float Sf = floor(sharpness);
|
||||
@ -851,60 +1005,74 @@ OsdComputePerPatchVertexBSpline(int3 patchParam, int ID, float3 cv[16],
|
||||
float Sr = frac(sharpness);
|
||||
float4x4 Mf = OsdComputeMs(Sf);
|
||||
float4x4 Mc = OsdComputeMs(Sc);
|
||||
Mj = (1-Sr) * Mf + Sr * Mi;
|
||||
Ms = (1-Sr) * Mf + Sr * Mc;
|
||||
float4x4 Mj = (1-Sr) * Mf + Sr * Mi;
|
||||
float4x4 Ms = (1-Sr) * Mf + Sr * Mc;
|
||||
float s0 = 1 - pow(2, -floor(sharpness));
|
||||
float s1 = 1 - pow(2, -ceil(sharpness));
|
||||
result.vSegments = float2(s0, s1);
|
||||
|
||||
} else {
|
||||
Mj = Ms = Mi;
|
||||
result.vSegments = float2(0, 0);
|
||||
}
|
||||
result.P = float3(0,0,0); // 0 to 1-2^(-Sf)
|
||||
result.P1 = float3(0,0,0); // 1-2^(-Sf) to 1-2^(-Sc)
|
||||
result.P2 = float3(0,0,0); // 1-2^(-Sc) to 1
|
||||
float4x4 MUi = Q, MUj = Q, MUs = Q;
|
||||
float4x4 MVi = Q, MVj = Q, MVs = Q;
|
||||
|
||||
float4x4 MUi, MUj, MUs;
|
||||
float4x4 MVi, MVj, MVs;
|
||||
MUi = MUj = MUs = Q;
|
||||
MVi = MVj = MVs = Q;
|
||||
|
||||
int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
|
||||
if ((boundaryMask & 1) != 0) {
|
||||
MVi = OsdFlipMatrix(Mi);
|
||||
MVj = OsdFlipMatrix(Mj);
|
||||
MVs = OsdFlipMatrix(Ms);
|
||||
}
|
||||
if ((boundaryMask & 2) != 0) {
|
||||
MUi = Mi;
|
||||
MUj = Mj;
|
||||
MUs = Ms;
|
||||
}
|
||||
if ((boundaryMask & 4) != 0) {
|
||||
MVi = Mi;
|
||||
MVj = Mj;
|
||||
MVs = Ms;
|
||||
}
|
||||
if ((boundaryMask & 8) != 0) {
|
||||
MUi = OsdFlipMatrix(Mi);
|
||||
MUj = OsdFlipMatrix(Mj);
|
||||
MUs = OsdFlipMatrix(Ms);
|
||||
}
|
||||
|
||||
float3 Hi[4], Hj[4], Hs[4];
|
||||
for (int l=0; l<4; ++l) {
|
||||
Hi[l] = Hj[l] = Hs[l] = float3(0,0,0);
|
||||
for (int k=0; k<4; ++k) {
|
||||
Hi[l] += MUi[i][k] * cv[l*4 + k];
|
||||
Hj[l] += MUj[i][k] * cv[l*4 + k];
|
||||
Hs[l] += MUs[i][k] * cv[l*4 + k];
|
||||
int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
|
||||
if ((boundaryMask & 1) != 0) {
|
||||
MVi = OsdFlipMatrix(Mi);
|
||||
MVj = OsdFlipMatrix(Mj);
|
||||
MVs = OsdFlipMatrix(Ms);
|
||||
}
|
||||
}
|
||||
for (int k=0; k<4; ++k) {
|
||||
result.P += MVi[j][k]*Hi[k];
|
||||
result.P1 += MVj[j][k]*Hj[k];
|
||||
result.P2 += MVs[j][k]*Hs[k];
|
||||
if ((boundaryMask & 2) != 0) {
|
||||
MUi = Mi;
|
||||
MUj = Mj;
|
||||
MUs = Ms;
|
||||
}
|
||||
if ((boundaryMask & 4) != 0) {
|
||||
MVi = Mi;
|
||||
MVj = Mj;
|
||||
MVs = Ms;
|
||||
}
|
||||
if ((boundaryMask & 8) != 0) {
|
||||
MUi = OsdFlipMatrix(Mi);
|
||||
MUj = OsdFlipMatrix(Mj);
|
||||
MUs = OsdFlipMatrix(Ms);
|
||||
}
|
||||
|
||||
float3 Hi[4], Hj[4], Hs[4];
|
||||
for (int l=0; l<4; ++l) {
|
||||
Hi[l] = Hj[l] = Hs[l] = float3(0,0,0);
|
||||
for (int k=0; k<4; ++k) {
|
||||
Hi[l] += MUi[i][k] * cv[l*4 + k];
|
||||
Hj[l] += MUj[i][k] * cv[l*4 + k];
|
||||
Hs[l] += MUs[i][k] * cv[l*4 + k];
|
||||
}
|
||||
}
|
||||
for (int k=0; k<4; ++k) {
|
||||
P += MVi[j][k]*Hi[k];
|
||||
P1 += MVj[j][k]*Hj[k];
|
||||
P2 += MVs[j][k]*Hs[k];
|
||||
}
|
||||
|
||||
result.P = P;
|
||||
result.P1 = P1;
|
||||
result.P2 = P2;
|
||||
} else {
|
||||
result.vSegments = float2(0, 0);
|
||||
|
||||
OsdComputeBSplineBoundaryPoints(cv, patchParam);
|
||||
|
||||
float3 Hi[4];
|
||||
for (int l=0; l<4; ++l) {
|
||||
Hi[l] = float3(0,0,0);
|
||||
for (int k=0; k<4; ++k) {
|
||||
Hi[l] += Q[i][k] * cv[l*4 + k];
|
||||
}
|
||||
}
|
||||
for (int k=0; k<4; ++k) {
|
||||
P += Q[j][k]*Hi[k];
|
||||
}
|
||||
|
||||
result.P = P;
|
||||
result.P1 = P;
|
||||
result.P2 = P;
|
||||
}
|
||||
#else
|
||||
OsdComputeBSplineBoundaryPoints(cv, patchParam);
|
||||
|
@ -25,7 +25,7 @@
|
||||
#ifndef OPENSUBDIV3_VERSION_H
|
||||
#define OPENSUBDIV3_VERSION_H
|
||||
|
||||
#define OPENSUBDIV_VERSION v3_0_3
|
||||
#define OPENSUBDIV_VERSION v3_0_5
|
||||
|
||||
namespace OpenSubdiv {
|
||||
namespace OPENSUBDIV_VERSION {
|
||||
|
@ -163,10 +163,13 @@ int main(int, char **) {
|
||||
std::vector<Vertex> verts(nRefinerVertices + nLocalPoints);
|
||||
memcpy(&verts[0], g_verts, g_nverts*3*sizeof(float));
|
||||
|
||||
// Adaptive refinement may result in fewer levels than maxIsolation.
|
||||
int nRefinedLevels = refiner->GetNumLevels();
|
||||
|
||||
// Interpolate vertex primvar data : they are the control vertices
|
||||
// of the limit patches (see far_tutorial_0 for details)
|
||||
Vertex * src = &verts[0];
|
||||
for (int level = 1; level <= maxIsolation; ++level) {
|
||||
for (int level = 1; level < nRefinedLevels; ++level) {
|
||||
Vertex * dst = src + refiner->GetLevel(level-1).GetNumVertices();
|
||||
Far::PrimvarRefiner(*refiner).Interpolate(level, src, dst);
|
||||
src = dst;
|
||||
|
Loading…
Reference in New Issue
Block a user