From a1552cfe8234fb4f3558ebe264f864d8e02971cf Mon Sep 17 00:00:00 2001 From: manuelk Date: Fri, 3 Aug 2012 19:51:27 -0700 Subject: [PATCH] Siggrpah 2012 - rolling over all of prepro work into beta 1.1 --- CMakeLists.txt | 115 +- README.md | 19 +- cmake/FindGLEW.cmake | 16 +- cmake/FindGLUT.cmake | 18 +- cmake/FindIlmBase.cmake | 98 +- cmake/FindMaya.cmake | 121 +- cmake/FindOpenCL.cmake | 76 +- cmake/FindPTex.cmake | 129 ++ examples/CMakeLists.txt | 63 +- examples/{glutViewer => common}/cudaInit.h | 4 +- examples/common/export_obj.py | 69 + examples/common/stopwatch.h | 22 +- examples/glutViewer/CMakeLists.txt | 70 +- examples/glutViewer/shader.glsl | 245 +++ examples/glutViewer/viewer.cpp | 617 +++++-- examples/glutViewer/viewer_compat.cpp | 925 ++++++++++ .../AEopenSubdivPtexShaderTemplate.mel | 50 + .../CMakeLists.txt | 175 ++ .../OpenSubdivPtexShader.cpp | 1515 +++++++++++++++++ .../mayaPtexViewer_siggraph2012/cudaUtil.cpp | 12 + .../mayaPtexViewer_siggraph2012/hbrUtil.cpp | 197 +++ .../mayaPtexViewer_siggraph2012/hbrUtil.h | 74 + .../mayaPtexViewer_siggraph2012/shader.glsl | 294 ++++ examples/mayaViewer/CMakeLists.txt | 1 + .../mayaViewer/OpenSubdivDrawOverride.cpp | 12 +- examples/mayaViewer/hbrUtil.cpp | 18 +- examples/ptexViewer/CMakeLists.txt | 114 ++ examples/ptexViewer/shader.glsl | 336 ++++ examples/ptexViewer/viewer.cpp | 999 +++++++++++ opensubdiv/CMakeLists.txt | 2 +- opensubdiv/far/CMakeLists.txt | 4 +- opensubdiv/far/bilinearSubdivisionTables.h | 76 +- opensubdiv/far/catmarkSubdivisionTables.h | 122 +- opensubdiv/far/dispatcher.h | 71 +- opensubdiv/far/loopSubdivisionTables.h | 100 +- opensubdiv/far/mesh.h | 52 +- opensubdiv/far/meshFactory.h | 327 +++- opensubdiv/far/subdivisionTables.h | 134 +- opensubdiv/far/table.h | 127 ++ opensubdiv/far/vertexEditTables.h | 215 +++ opensubdiv/hbr/CMakeLists.txt | 4 +- opensubdiv/hbr/allocator.h | 82 +- opensubdiv/hbr/bilinear.h | 476 +++--- opensubdiv/hbr/catmark.h | 669 ++++---- opensubdiv/hbr/cornerEdit.h | 50 +- opensubdiv/hbr/creaseEdit.h | 42 +- opensubdiv/hbr/face.h | 370 ++-- opensubdiv/hbr/faceEdit.h | 54 +- opensubdiv/hbr/fvarData.h | 64 +- opensubdiv/hbr/fvarEdit.h | 30 +- opensubdiv/hbr/halfedge.h | 282 +-- opensubdiv/hbr/hierarchicalEdit.h | 58 +- opensubdiv/hbr/holeEdit.h | 22 +- opensubdiv/hbr/loop.h | 539 +++--- opensubdiv/hbr/mesh.h | 503 +++--- opensubdiv/hbr/subdivision.h | 89 +- opensubdiv/hbr/vertex.h | 812 ++++----- opensubdiv/hbr/vertexEdit.h | 196 +-- opensubdiv/osd/CMakeLists.txt | 76 +- opensubdiv/osd/clDispatcher.cpp | 114 +- opensubdiv/osd/clDispatcher.h | 67 +- opensubdiv/osd/clKernel.cl | 86 +- opensubdiv/osd/cpuDispatcher.cpp | 103 +- opensubdiv/osd/cpuDispatcher.h | 38 +- opensubdiv/osd/cpuKernel.cpp | 58 +- opensubdiv/osd/cpuKernel.h | 16 +- opensubdiv/osd/cudaDispatcher.cpp | 51 +- opensubdiv/osd/cudaDispatcher.h | 29 +- opensubdiv/osd/cudaKernel.cu | 126 +- opensubdiv/osd/glslDispatcher.cpp | 48 +- opensubdiv/osd/glslDispatcher.h | 47 +- opensubdiv/osd/glslKernel.glsl | 22 +- opensubdiv/osd/kernelDispatcher.h | 25 +- opensubdiv/osd/local.h | 4 +- opensubdiv/osd/mesh.cpp | 92 +- opensubdiv/osd/mesh.h | 33 +- opensubdiv/osd/pTexture.cpp | 1062 ++++++++++++ opensubdiv/osd/pTexture.h | 159 ++ opensubdiv/osd/vertex.h | 12 +- opensubdiv/osd/vertexBuffer.cpp | 100 +- opensubdiv/osd/vertexBuffer.h | 64 +- opensubdiv/tools/stringify/main.cpp | 4 +- opensubdiv/version.h | 2 +- regression/CMakeLists.txt | 26 +- regression/common/shape_utils.h | 319 +++- regression/far_regression/CMakeLists.txt | 14 +- regression/far_regression/main.cpp | 185 +- regression/osd_regression/CMakeLists.txt | 20 +- regression/osd_regression/main.cpp | 144 +- regression/shapes/catmark_square_hedit0.h | 92 + regression/shapes/catmark_square_hedit1.h | 91 + regression/shapes/catmark_square_hedit2.h | 91 + regression/shapes/catmark_square_hedit3.h | 92 + 93 files changed, 11953 insertions(+), 3535 deletions(-) create mode 100644 cmake/FindPTex.cmake rename examples/{glutViewer => common}/cudaInit.h (98%) create mode 100644 examples/common/export_obj.py create mode 100644 examples/glutViewer/shader.glsl create mode 100644 examples/glutViewer/viewer_compat.cpp create mode 100644 examples/mayaPtexViewer_siggraph2012/AEopenSubdivPtexShaderTemplate.mel create mode 100644 examples/mayaPtexViewer_siggraph2012/CMakeLists.txt create mode 100644 examples/mayaPtexViewer_siggraph2012/OpenSubdivPtexShader.cpp create mode 100644 examples/mayaPtexViewer_siggraph2012/cudaUtil.cpp create mode 100644 examples/mayaPtexViewer_siggraph2012/hbrUtil.cpp create mode 100644 examples/mayaPtexViewer_siggraph2012/hbrUtil.h create mode 100644 examples/mayaPtexViewer_siggraph2012/shader.glsl create mode 100644 examples/ptexViewer/CMakeLists.txt create mode 100644 examples/ptexViewer/shader.glsl create mode 100644 examples/ptexViewer/viewer.cpp create mode 100644 opensubdiv/far/table.h create mode 100644 opensubdiv/far/vertexEditTables.h create mode 100644 opensubdiv/osd/pTexture.cpp create mode 100644 opensubdiv/osd/pTexture.h create mode 100644 regression/shapes/catmark_square_hedit0.h create mode 100644 regression/shapes/catmark_square_hedit1.h create mode 100644 regression/shapes/catmark_square_hedit2.h create mode 100644 regression/shapes/catmark_square_hedit3.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4abd47e8..d2822562 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,19 +103,100 @@ find_package(OpenGL) find_package(OpenCL) find_package(CUDA) find_package(GLUT) +find_package(PTex) if (NOT APPLE) - find_package(GLEW) + find_package(GLEW REQUIRED) endif() find_package(Maya) +# Warn about missing dependencies that will cause parts of OpenSubdiv to be +# disabled. Also, add preprocessor defines that can be used in the source +# code to determine if a specific dependency is present or not. + +if(OPENMP_FOUND) + add_definitions( + -DOPENSUBDIV_HAS_OPENMP + ${OpenMP_CXX_FLAGS} + ) +else() + message(WARNING + "OpenMP was not found : support for OMP parallel compute kernels " + "will be diabled in Osd. If your compiler supports OpenMP " + "directives, please refer to the FindOpenMP.cmake shared module " + "in your cmake installation.") +endif() + +# note : (GLSL compute kernels require GL 4.2, which currently excludes APPLE) +if(OPENGL_FOUND AND GLEW_FOUND AND (NOT APPLE)) + add_definitions( + -DOPENSUBDIV_HAS_GLSL + ) +else() + message(WARNING + "OpenGL was not found : support for GLSL parallel compute kernels " + "will be disabled in Osd. If you have an OpenGL SDK installed " + "(version 4.2 or above), please refer to the FindOpenGL.cmake " + "shared module in your cmake installation.") +endif() + +if(OPENCL_FOUND) + add_definitions( + -DOPENSUBDIV_HAS_OPENCL + ) +else() + message(WARNING + "OpenCL was not found : support for OpenCL parallel compute kernels " + "will be disabled in Osd. If you have the OpenCL SDK installed, " + "please refer to the FindOpenCL.cmake in ${PROJECT_SOURCE_DIR}/cmake.") +endif() + +if(CUDA_FOUND) + add_definitions( + -DOPENSUBDIV_HAS_CUDA + ) +else() + message(WARNING + "CUDA was not found : support for CUDA parallel compute kernels " + "will be disabled in Osd. If you have the CUDA SDK installed, please " + "refer to the FindCUDA.cmake shared module in your cmake installation.") +endif() + +if(PTEX_FOUND) + add_definitions( + -DOPENSUBDIV_HAS_PTEX + ) +else() + message(WARNING + "Ptex was not found : the OpenSubdiv Ptex example will not be " + "available. If you do have Ptex installed and see this message, " + "please add your Ptex path to FindPTex.cmake in " + "${PROJECT_SOURCE_DIR}/cmake or set it through the PTEX_LOCATION " + "cmake command line argument or environment variable." + ) +endif() + +if(MAYA_FOUND) + add_definitions( + -DOPENSUBDIV_HAS_MAYA + ) +else() + message(WARNING + "Maya was not found : the OpenSubdiv mayaViwer plugin will not be " + "available. If you do have Maya installed and see this message, " + "please add your Maya path to FindMaya.cmake in " + "${PROJECT_SOURCE_DIR}/cmake or set it through the MAYA_LOCATION " + "cmake command line argument or environment variable." + ) +endif() + # Link examples & regressions dynamically against Osd set( OSD_LINK_TARGET osd_dynamic ) if (WIN32) add_definitions( - # GLEW gets built as a static library in Windows + # Link against the static version of GLEW. -DGLEW_STATIC ) # Link examples & regressions statically against Osd for @@ -178,9 +259,11 @@ if(MSVC) ) endif(MSVC) + + # Macro for adding a cuda executable if cuda is found and a regular # executable otherwise. -macro(_add_executable target) +macro(_add_possibly_cuda_executable target) if(CUDA_FOUND) cuda_add_executable(${target} ${ARGN}) else() @@ -188,9 +271,10 @@ macro(_add_executable target) endif() endmacro() + # Macro for adding a cuda library if cuda is found and a regular # library otherwise. -macro(_add_library target) +macro(_add_possibly_cuda_library target) if(CUDA_FOUND) cuda_add_library(${target} ${ARGN}) else() @@ -199,6 +283,29 @@ macro(_add_library target) endmacro() +# Macro for adding a (potentially cuda) executable. +macro(_add_glut_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 ${ILMBASE_LIBRARIES} ${GLUT_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} + $ + ) + endforeach() + endif() + +endmacro() + add_subdirectory(opensubdiv) diff --git a/README.md b/README.md index 098751b2..5a8b7156 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # OpenSubdiv # -OpenSubdiv is a set of open source libraries that implement high performance subdivision surface (subdiv) evaluation on massively parallel CPU and GPU architectures. This codepath is optimized for drawing deforming subdivs with static topology at interactive framerates. OpenSubdiv can amplify a 30,000 polygon base mesh into a smooth limit surface of 500,000 polygons in under 3 milliseconds on Kepler Nvidia hardware. The architecture features a precomputation step that uses Renderman's hbr library to compute fast run time data structures that are evaluated with backends in any of C++, CUDA, OpenCL, or GLSL. The resulting limit surface matches Pixar's Renderman to numerical precision. OpenSubdiv also includes support for semi-sharp creases and hierarchical edits on subdivs which both are powerful tools for shaping surfaces. +OpenSubdiv is a set of open source libraries that implement high performance subdivision surface (subdiv) evaluation on massively parallel CPU and GPU architectures. This codepath is optimized for drawing deforming subdivs with static topology at interactive framerates. OpenSubdiv can amplify a 30,000 polygon base mesh into a smooth limit surface of 500,000 polygons in under 3 milliseconds on Kepler Nvidia hardware. The architecture features a precomputation step that uses Renderman's hbr library to compute fast run time data structures that are evaluated with backends in any of C++, CUDA, OpenCL, or GLSL. The resulting limit surface matches Pixar's Renderman to numerical precision. OpenSubdiv also includes support for semi-sharp creases and hierarchical edits on subdivs which both are powerful tools for shaping surfaces. OpenSubdiv is covered by the Microsoft Public License (included below), and is free to use for commercial or non-commercial use. All Pixar patents covering algorithms used inside for semi-sharp crease evaluation and texture coordinate interpolation have also been released for public use. Our intent is to encourage high performance accurate subdiv drawing by giving away the "good stuff" that we use internally. We welcome any involvement in the development or extension of this code, we'd love it in fact. Please contact us if you're interested. @@ -39,7 +39,7 @@ Optional : make -### Useful cmake options ### +### Useful cmake options and environment variables ###

 -DCMAKE_BUILD_TYPE=[Debug|Release]
@@ -48,8 +48,17 @@ Optional :
 
 -DMAYA_LOCATION=[path to Maya]
 
+-DPTEX_LOCATION=[path to Ptex]
+
+-DGLUT_LOCATION=[path to GLUT]
+
+-DGLEW_LOCATION=[path to GLEW]
 
+The paths to Maya, Ptex, GLUT, and GLEW can also be specified through the +following environment variables: MAYA_LOCATION, PTEX_LOCATION, GLUT_LOCATION, +and GLEW_LOCATION. + ### Standalone viewer ###

@@ -70,12 +79,12 @@ Optional :
 
 Subdivision surfaces are commonly used for final rendering of character shapes for a smooth and controllable limit surfaces. However, subdivision surfaces in interactive apps are typically drawn as their polygonal control hulls because of performance.  The polygonal control hull is an approximation that is offset from the true limit surface,  Looking at an approximation in the interactive app makes it difficult to see exact contact, like fingers touching a potion bottle or hands touching a cheek.  It also makes it difficult to see poke throughs in cloth simulation if the skin and cloth are both approximations.  This problem is particularly bad when one character is much larger than another and unequal subdiv face sizes cause approximations errors to be magnified.
 
-Maya and Pixar's Presto animation system can take 100ms to subdivide a character of 30,000 polygons to the second level of subdivision (500,000 polygons).  By doing the same thing in 3ms OpenSubdiv allows the user to see the smooth, accurate limit surface at all times. 
+Maya and Pixar's Presto animation system can take 100ms to subdivide a character of 30,000 polygons to the second level of subdivision (500,000 polygons).  By doing the same thing in 3ms OpenSubdiv allows the user to see the smooth, accurate limit surface at all times.
 
 ## Components ##
 
 #### hbr (hierarchical boundary rep) ####
-This base library implements a half edge data structure to store edges, faces, and vertices of a subdivision surface. This code was authored by Julian Fong on the Renderman team. It is the lowest level subdivision libary in renderman. Separate objects are allocated for each vertex and edge (*2) with pointers to neighboring vertices and edges. Hbr is a generic templated API used by clients to create concrete instances by providing the implementation of the vertex class. 
+This base library implements a half edge data structure to store edges, faces, and vertices of a subdivision surface. This code was authored by Julian Fong on the Renderman team. It is the lowest level subdivision libary in renderman. Separate objects are allocated for each vertex and edge (*2) with pointers to neighboring vertices and edges. Hbr is a generic templated API used by clients to create concrete instances by providing the implementation of the vertex class.
 
 #### far (feature-adaptive rep) ####
 Far uses hbr to create and cache fast run time data structures for table driven subdivision of vertices and cubic patches for limit surface evaluation.  Feature-adaptive refinement logic is used to adaptively refine coarse topology near features like extrordinary vertices and creases in order to make the topology amenable to cubic patch evaluation. Far is also a generic templated algorthmic base API that clients in higher levels instantiate and use by providing an implementation of a vertex class. Subdivision schemes supported:
@@ -115,7 +124,7 @@ The second release of OpenSubdiv raises the performance bar to what we believe i
 
 This release will also complete hierarchical edit support and support for face varying coordinate interpolation.
 
-We are targeting release 2 for end of year 2012, hopefully earlier than that.  We have the patch code working in a very rough implementation but need to rewrite that in a development branch for release-ready code. Let us know if you're interested in contributing to that effort! 
+We are targeting release 2 for end of year 2012, hopefully earlier than that.  We have the patch code working in a very rough implementation but need to rewrite that in a development branch for release-ready code. Let us know if you're interested in contributing to that effort!
 
 ## Wish List ##
 
diff --git a/cmake/FindGLEW.cmake b/cmake/FindGLEW.cmake
index c23dc800..b862e3f7 100644
--- a/cmake/FindGLEW.cmake
+++ b/cmake/FindGLEW.cmake
@@ -62,25 +62,27 @@
 # GLEW_FOUND
 # GLEW_INCLUDE_DIR
 # GLEW_LIBRARY
-# 
+#
 
 include(FindPackageHandleStandardArgs)
 
 if (WIN32)
-    find_path( GLEW_INCLUDE_DIR 
-        NAMES 
+    find_path( GLEW_INCLUDE_DIR
+        NAMES
             GL/glew.h
         PATHS
             ${GLEW_LOCATION}/include
+            $ENV{GLEW_LOCATION}/include
             $ENV{PROGRAMFILES}/GLEW/include
             ${PROJECT_SOURCE_DIR}/extern/glew/include
             DOC "The directory where GL/glew.h resides" )
 
     find_library( GLEW_LIBRARY
-        NAMES 
+        NAMES
             glew GLEW glew32s glew32
         PATHS
             ${GLEW_LOCATION}/lib
+            $ENV{GLEW_LOCATION}/lib
             $ENV{PROGRAMFILES}/GLEW/lib
             ${PROJECT_SOURCE_DIR}/extern/glew/bin
             ${PROJECT_SOURCE_DIR}/extern/glew/lib
@@ -88,11 +90,12 @@ if (WIN32)
 endif ()
 
 if (${CMAKE_HOST_UNIX})
-    find_path( GLEW_INCLUDE_DIR 
+    find_path( GLEW_INCLUDE_DIR
         NAMES
             GL/glew.h
         PATHS
             ${GLEW_LOCATION}/include
+            $ENV{GLEW_LOCATION}/include
             /usr/include
             /usr/local/include
             /sw/include
@@ -101,10 +104,11 @@ if (${CMAKE_HOST_UNIX})
             DOC "The directory where GL/glew.h resides"
     )
     find_library( GLEW_LIBRARY
-        NAMES 
+        NAMES
             GLEW glew
         PATHS
             ${GLEW_LOCATION}/lib
+            $ENV{GLEW_LOCATION}/lib
             /usr/lib64
             /usr/lib
             /usr/local/lib64
diff --git a/cmake/FindGLUT.cmake b/cmake/FindGLUT.cmake
index 50bf3932..79523544 100644
--- a/cmake/FindGLUT.cmake
+++ b/cmake/FindGLUT.cmake
@@ -62,16 +62,19 @@
 # GLUT_FOUND
 # GLUT_INCLUDE_DIR
 # GLUT_LIBRARIES
-# 
+#
 
 if (WIN32)
     if(CYGWIN)
         find_path( GLUT_INCLUDE_DIR GL/glut.h
           ${GLUT_LOCATION}/include
+          $ENV{GLUT_LOCATION}/include
           /usr/include
         )
         find_library( GLUT_glut_LIBRARY glut32 freeglut
           ${GLUT_LOCATION}/lib
+          ${GLUT_LOCATION}/lib/x64
+          $ENV{GLUT_LOCATION}/lib
           ${OPENGL_LIBRARY_DIR}
           /usr/lib
           /usr/lib/w32api
@@ -81,6 +84,7 @@ if (WIN32)
     else()
         find_path( GLUT_INCLUDE_DIR GL/glut.h
             ${GLUT_LOCATION}/include
+            $ENV{GLUT_LOCATION}/include
             ${PROJECT_SOURCE_DIR}/extern/glut/include
             $ENV{PROGRAMFILES}/GLUT/include
             ${OPENGL_INCLUDE_DIR}
@@ -89,6 +93,8 @@ if (WIN32)
             NAMES glut32 glut32s glut freeglut
             PATHS
             ${GLUT_LOCATION}/lib
+            ${GLUT_LOCATION}/lib/x64
+            $ENV{GLUT_LOCATION}/lib
             ${PROJECT_SOURCE_DIR}/extern/glut/bin
             ${PROJECT_SOURCE_DIR}/extern/glut/lib
             $ENV{PROGRAMFILES}/GLUT/lib
@@ -103,11 +109,12 @@ else ()
             /System/Library/Frameworks/GLUT.framework/Versions/A/Headers
             ${OPENGL_LIBRARY_DIR}
         )
-        set(GLUT_glut_LIBRARY "-framework Glut" CACHE STRING "GLUT library for OSX") 
+        set(GLUT_glut_LIBRARY "-framework Glut" CACHE STRING "GLUT library for OSX")
         set(GLUT_cocoa_LIBRARY "-framework Cocoa" CACHE STRING "Cocoa framework for OSX")
     else ()
         find_path( GLUT_INCLUDE_DIR GL/glut.h
             ${GLUT_LOCATION}/include
+            $ENV{GLUT_LOCATION}/include
             /usr/include
             /usr/include/GL
             /usr/local/include
@@ -120,6 +127,7 @@ else ()
         )
         find_library( GLUT_glut_LIBRARY glut
             ${GLUT_LOCATION}/lib
+            $ENV{GLUT_LOCATION}/lib
             /usr/lib
             /usr/local/lib
             /usr/openwin/lib
@@ -127,6 +135,7 @@ else ()
         )
         find_library( GLUT_Xi_LIBRARY Xi
             ${GLUT_LOCATION}/lib
+            $ENV{GLUT_LOCATION}/lib
             /usr/lib
             /usr/local/lib
             /usr/openwin/lib
@@ -134,6 +143,7 @@ else ()
         )
         find_library( GLUT_Xmu_LIBRARY Xmu
             ${GLUT_LOCATION}/lib
+            $ENV{GLUT_LOCATION}/lib
             /usr/lib
             /usr/local/lib
             /usr/openwin/lib
@@ -151,7 +161,7 @@ if(GLUT_INCLUDE_DIR)
     set( GLUT_LIBRARIES
       ${GLUT_glut_LIBRARY}
       ${GLUT_Xmu_LIBRARY}
-      ${GLUT_Xi_LIBRARY} 
+      ${GLUT_Xi_LIBRARY}
       ${GLUT_cocoa_LIBRARY}
     )
     set( GLUT_FOUND "YES" )
@@ -175,4 +185,4 @@ mark_as_advanced(
   GLUT_Xmu_LIBRARY
   GLUT_Xi_LIBRARY
 )
-  
+
diff --git a/cmake/FindIlmBase.cmake b/cmake/FindIlmBase.cmake
index fe784b4e..795a3061 100644
--- a/cmake/FindIlmBase.cmake
+++ b/cmake/FindIlmBase.cmake
@@ -58,9 +58,9 @@
 # - Try to find the IlmBase library
 # Once done this will define
 #
-#  ILMBASE_FOUND - System has OPENCL
+#  ILMBASE_FOUND - System has IlmBase
 #  ILMBASE_INCLUDE_DIR - The include directory
-#  ILMBASE_LIBS_DIRECTORY - The libraries needed
+#  ILMBASE_LIBRARIES - The libraries needed
 
 IF(NOT DEFINED ILMBASE_LOCATION)
     IF ( ${CMAKE_HOST_UNIX} )
@@ -68,17 +68,17 @@ IF(NOT DEFINED ILMBASE_LOCATION)
           # TODO: set to default install path when shipping out
           SET( ILMBASE_LOCATION NOTFOUND )
         ELSE()
-          SET(ILMBASE_LOCATION "/usr/local/ilmbase-1.0.1/" )
+          SET(ILMBASE_LOCATION /usr/local/ilmbase-1.0.1 )
         ENDIF()
     ELSE()
         IF ( WIN32 )
-          SET( ILMBASE_LOCATION "C:/Program Files (x86)/ilmbase-1.0.1/" )
-        ELSEIF ( WIN64 )
-          SET( ILMBASE_LOCATION "C:/Program Files (x86)/ilmbase-1.0.1/" )
+          # Note: This assumes that the Deploy directory has been copied
+          #       back into the IlmBase root directory.
+          SET( ILMBASE_LOCATION $ENV{PROGRAMFILES}/ilmbase-1.0.1/Deploy )
         ENDIF()
     ENDIF()
 ENDIF()
-  
+
 IF ( ${CMAKE_HOST_UNIX} )
     SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -lpthread")
 ELSE()
@@ -86,6 +86,11 @@ ENDIF()
 
 SET(LIBRARY_PATHS
     ${ILMBASE_LOCATION}/lib
+    ${ILMBASE_LOCATION}/lib/Release
+    ${ILMBASE_LOCATION}/lib/x64/Release
+    $ENV{ILMBASE_LOCATION}/lib
+    $ENV{ILMBASE_LOCATION}/lib/Release
+    $ENV{ILMBASE_LOCATION}/lib/x64/Release
     ~/Library/Frameworks
     /Library/Frameworks
     /usr/local/lib
@@ -97,13 +102,11 @@ SET(LIBRARY_PATHS
     /usr/freeware/lib64
 )
 
-
-IF( DEFINED ILMBASE_LIBRARY_DIR )
-  SET( LIBRARY_PATHS ${ILMBASE_LIBRARY_DIR} ${LIBRARY_PATHS} )
-ENDIF()
-
 SET(INCLUDE_PATHS
     ${ILMBASE_LOCATION}/include/OpenEXR/
+    ${ILMBASE_LOCATION}/include
+    $ENV{ILMBASE_LOCATION}/include/OpenEXR/
+    $ENV{ILMBASE_LOCATION}/include
     ~/Library/Frameworks
     /Library/Frameworks
     /usr/local/include/OpenEXR/
@@ -128,61 +131,54 @@ FIND_PATH( ILMBASE_INCLUDE_DIR ImathMath.h
            NO_CMAKE_SYSTEM_PATH
            DOC "The directory where ImathMath.h resides" )
 
-IF( NOT DEFINED ILMBASE_IEX_LIB )
-  FIND_LIBRARY( ILMBASE_IEX_LIB Iex
-                PATHS
-                ${LIBRARY_PATHS}
-                NO_DEFAULT_PATH
-                NO_CMAKE_ENVIRONMENT_PATH
-                NO_CMAKE_PATH
-                NO_SYSTEM_ENVIRONMENT_PATH
-                NO_CMAKE_SYSTEM_PATH
-                DOC "The Iex library" )
-ENDIF()
+FIND_LIBRARY( ILMBASE_IEX_LIB Iex
+              PATHS
+              ${LIBRARY_PATHS}
+              NO_DEFAULT_PATH
+              NO_CMAKE_ENVIRONMENT_PATH
+              NO_CMAKE_PATH
+              NO_SYSTEM_ENVIRONMENT_PATH
+              NO_CMAKE_SYSTEM_PATH
+              DOC "The Iex library" )
 
-IF( NOT DEFINED ILMBASE_ILMTHREAD_LIB )
-  FIND_LIBRARY( ILMBASE_ILMTHREAD_LIB IlmThread
-                 PATHS
-                 ${LIBRARY_PATHS}
-                 NO_DEFAULT_PATH
-                 NO_CMAKE_ENVIRONMENT_PATH
-                 NO_CMAKE_PATH
-                 NO_SYSTEM_ENVIRONMENT_PATH
-                 NO_CMAKE_SYSTEM_PATH
-                 DOC "The IlmThread library" )
-ENDIF()
-
-IF( NOT DEFINED ILMBASE_IMATH_LIB )
-  FIND_LIBRARY( ILMBASE_IMATH_LIB Imath
-                PATHS
-                ${LIBRARY_PATHS}
-                NO_DEFAULT_PATH
-                NO_CMAKE_ENVIRONMENT_PATH
-                NO_CMAKE_PATH
-                NO_SYSTEM_ENVIRONMENT_PATH
-                NO_CMAKE_SYSTEM_PATH
-                DOC "The Imath library" )
-ENDIF()
+FIND_LIBRARY( ILMBASE_ILMTHREAD_LIB IlmThread
+              PATHS
+              ${LIBRARY_PATHS}
+              NO_DEFAULT_PATH
+              NO_CMAKE_ENVIRONMENT_PATH
+              NO_CMAKE_PATH
+              NO_SYSTEM_ENVIRONMENT_PATH
+              NO_CMAKE_SYSTEM_PATH
+              DOC "The IlmThread library" )
 
+FIND_LIBRARY( ILMBASE_IMATH_LIB Imath
+              PATHS
+              ${LIBRARY_PATHS}
+              NO_DEFAULT_PATH
+              NO_CMAKE_ENVIRONMENT_PATH
+              NO_CMAKE_PATH
+              NO_SYSTEM_ENVIRONMENT_PATH
+              NO_CMAKE_SYSTEM_PATH
+              DOC "The Imath library" )
 
 
 IF ( ${ILMBASE_IEX_LIB} STREQUAL "ILMBASE_IEX_LIB-NOTFOUND" )
-  MESSAGE( FATAL_ERROR "ilmbase libraries (Iex) not found, required" )
+  MESSAGE( FATAL_ERROR "ilmbase libraries (Iex) not found, required: ILMBASE_LOCATION: ${ILMBASE_LOCATION}" )
 ENDIF()
 
 IF ( ${ILMBASE_ILMTHREAD_LIB} STREQUAL "ILMBASE_ILMTHREAD_LIB-NOTFOUND" )
-  MESSAGE( FATAL_ERROR "ilmbase libraries (IlmThread) not found, required" )
+  MESSAGE( FATAL_ERROR "ilmbase libraries (IlmThread) not found, required: ILMBASE_LOCATION: ${ILMBASE_LOCATION}" )
 ENDIF()
 
 IF ( ${ILMBASE_IMATH_LIB} STREQUAL "ILMBASE_IMATH_LIB-NOTFOUND" )
-  MESSAGE( FATAL_ERROR "ilmbase libraries (Imath) not found, required" )
+  MESSAGE( FATAL_ERROR "ilmbase libraries (Imath) not found, required: ILMBASE_LOCATION: ${ILMBASE_LOCATION}" )
 ENDIF()
 
 IF ( ${ILMBASE_INCLUDE_DIR} STREQUAL "ILMBASE_INCLUDE_DIR-NOTFOUND" )
   MESSAGE( FATAL_ERROR "ilmbase header files not found, required: ILMBASE_LOCATION: ${ILMBASE_LOCATION}" )
 ENDIF()
 
-SET( ILMBASE_LIBS_DIRECTORY
+SET( ILMBASE_LIBRARIES
        ${ILMBASE_IMATH_LIB}
        ${ILMBASE_ILMTHREAD_LIB}
        ${ILMBASE_IEX_LIB}
@@ -192,7 +188,7 @@ INCLUDE(FindPackageHandleStandardArgs)
 FIND_PACKAGE_HANDLE_STANDARD_ARGS(IlmBase DEFAULT_MSG
     ILMBASE_LOCATION
     ILMBASE_INCLUDE_DIR
-    ILMBASE_LIBS_DIRECTORY
+    ILMBASE_LIBRARIES
 )
 
 SET( ILMBASE_FOUND TRUE )
diff --git a/cmake/FindMaya.cmake b/cmake/FindMaya.cmake
index 9bf08390..8696bc79 100644
--- a/cmake/FindMaya.cmake
+++ b/cmake/FindMaya.cmake
@@ -56,18 +56,18 @@
 #
 
 # - Maya finder module
-# This module searches for a valid Maya instalation. 
+# This module searches for a valid Maya instalation.
 # It searches for Maya's devkit, libraries, executables
 # and related paths (scripts)
 #
-# Variables that will be defined: 
+# Variables that will be defined:
 # MAYA_FOUND          Defined if a Maya installation has been detected
 # MAYA_EXECUTABLE     Path to Maya's executable
 # MAYA__FOUND    Defined if  has been found
 # MAYA__LIBRARY  Path to  library
 # MAYA_INCLUDE_DIRS   Path to the devkit's include directories
 #
-# IMPORTANT: Currently, there's only support for OSX platform and Maya version 2012. 
+# IMPORTANT: Currently, there's only support for OSX platform and Maya version 2012.
 
 #=============================================================================
 # Copyright 2011-2012 Francisco Requena 
@@ -88,83 +88,97 @@ SET(MAYA_VERSION_2012 TRUE)
 
 IF(APPLE)
   FIND_PATH(MAYA_BASE_DIR include/maya/MFn.h PATH
-  	ENV MAYA_LOCATION
-  	"/Applications/Autodesk/maya2012.17/Maya.app/Contents"
-  	"/Applications/Autodesk/maya2012/Maya.app/Contents"
-  	"/Applications/Autodesk/maya2011/Maya.app/Contents"
-  	"/Applications/Autodesk/maya2010/Maya.app/Contents"
-  	)
+        ${MAYA_LOCATION}
+        $ENV{MAYA_LOCATION}
+        "/Applications/Autodesk/maya2013/Maya.app/Contents"
+        "/Applications/Autodesk/maya2012.17/Maya.app/Contents"
+        "/Applications/Autodesk/maya2012/Maya.app/Contents"
+        "/Applications/Autodesk/maya2011/Maya.app/Contents"
+        "/Applications/Autodesk/maya2010/Maya.app/Contents"
+        )
   FIND_PATH(MAYA_LIBRARY_DIR libOpenMaya.dylib
     PATHS
-      ENV MAYA_LOCATION
+      ${MAYA_LOCATION}
+      $ENV{MAYA_LOCATION}
       ${MAYA_BASE_DIR}
     PATH_SUFFIXES
-	Maya.app/contents/MacOS/
+      Maya.app/contents/MacOS/
   DOC "Maya's libraries path"
 )
 ENDIF(APPLE)
 
 IF(UNIX)
   FIND_PATH(MAYA_BASE_DIR include/maya/MFn.h PATH
-  	ENV MAYA_LOCATION
-  	"/usr/autodesk/maya2012.17-x64"
-  	"/usr/autodesk/maya2012-x64"
-  	"/usr/autodesk/maya2011-x64"
-  	"/usr/autodesk/maya2010-x64"
-	)
+        ${MAYA_LOCATION}
+        $ENV{MAYA_LOCATION}
+        "/usr/autodesk/maya2013-x64"
+        "/usr/autodesk/maya2012.17-x64"
+        "/usr/autodesk/maya2012-x64"
+        "/usr/autodesk/maya2011-x64"
+        "/usr/autodesk/maya2010-x64"
+        )
   FIND_PATH(MAYA_LIBRARY_DIR libOpenMaya.so
     PATHS
-      ENV MAYA_LOCATION
+      ${MAYA_LOCATION}
+      $ENV{MAYA_LOCATION}
       ${MAYA_BASE_DIR}
     PATH_SUFFIXES
-	lib/
+      lib/
   DOC "Maya's libraries path"
 )
 ENDIF(UNIX)
- 
+
 IF(WIN32)
   FIND_PATH(MAYA_BASE_DIR include/maya/MFn.h PATH
-  	ENV MAYA_LOCATION
-	"C:/Program Files/Autodesk/Maya2012-x64"
-	"C:/Program Files/Autodesk/Maya2012"
-	"C:/Program Files (x86)/Autodesk/Maya2012"
-	"C:/Autodesk/maya-2012x64"
-	"C:/Program Files/Autodesk/Maya2011-x64"
-	"C:/Program Files/Autodesk/Maya2011"
-	"C:/Program Files (x86)/Autodesk/Maya2011"
-	"C:/Autodesk/maya-2011x64"
-	"C:/Program Files/Autodesk/Maya2010-x64"
-	"C:/Program Files/Autodesk/Maya2010"
-	"C:/Program Files (x86)/Autodesk/Maya2010"
-	"C:/Autodesk/maya-2010x64"
-	)
+        ${MAYA_LOCATION}
+        $ENV{MAYA_LOCATION}
+        "C:/Program Files/Autodesk/Maya2013-x64"
+        "C:/Program Files/Autodesk/Maya2013"
+        "C:/Program Files (x86)/Autodesk/Maya2013"
+        "C:/Autodesk/maya-2013x64"
+        "C:/Program Files/Autodesk/Maya2012-x64"
+        "C:/Program Files/Autodesk/Maya2012"
+        "C:/Program Files (x86)/Autodesk/Maya2012"
+        "C:/Autodesk/maya-2012x64"
+        "C:/Program Files/Autodesk/Maya2011-x64"
+        "C:/Program Files/Autodesk/Maya2011"
+        "C:/Program Files (x86)/Autodesk/Maya2011"
+        "C:/Autodesk/maya-2011x64"
+        "C:/Program Files/Autodesk/Maya2010-x64"
+        "C:/Program Files/Autodesk/Maya2010"
+        "C:/Program Files (x86)/Autodesk/Maya2010"
+        "C:/Autodesk/maya-2010x64"
+        )
   FIND_PATH(MAYA_LIBRARY_DIR OpenMaya.lib
     PATHS
-      ENV MAYA_LOCATION
+      ${MAYA_LOCATION}
+      $ENV{MAYA_LOCATION}
       ${MAYA_BASE_DIR}
     PATH_SUFFIXES
-	lib/
+      lib/
   DOC "Maya's libraries path"
 )
 ENDIF(WIN32)
 
 FIND_PATH(MAYA_INCLUDE_DIR maya/MFn.h
   PATHS
-    ENV MAYA_LOCATION
+    ${MAYA_LOCATION}
+    $ENV{MAYA_LOCATION}
     ${MAYA_BASE_DIR}
   PATH_SUFFIXES
-	../../devkit/include/
-	include/
+    ../../devkit/include/
+    include/
   DOC "Maya's devkit headers path"
 )
 
 FIND_PATH(MAYA_LIBRARY_DIR OpenMaya
   PATHS
-    ENV MAYA_LOCATION
+    ${MAYA_LOCATION}
+    $ENV{MAYA_LOCATION}
     ${MAYA_BASE_DIR}
   PATH_SUFFIXES
-	../../devkit/include/
-	include/
+    ../../devkit/include/
+    include/
   DOC "Maya's devkit headers path"
 )
 
@@ -172,10 +186,11 @@ LIST(APPEND MAYA_INCLUDE_DIRS ${MAYA_INCLUDE_DIR})
 
 FIND_PATH(MAYA_DEVKIT_INC_DIR GL/glext.h
   PATHS
-    ENV MAYA_LOCATION
+    ${MAYA_LOCATION}
+    $ENV{MAYA_LOCATION}
     ${MAYA_BASE_DIR}
   PATH_SUFFIXES
-	/devkit/plug-ins/
+    /devkit/plug-ins/
   DOC "Maya's devkit headers path"
 )
 LIST(APPEND MAYA_INCLUDE_DIRS ${MAYA_DEVKIT_INC_DIR})
@@ -185,7 +200,7 @@ FOREACH(MAYA_LIB
   OpenMayaAnim
   OpenMayaFX
   OpenMayaRender
-  OpenMayaUI  
+  OpenMayaUI
   Image
   Foundation
   IMFbase
@@ -195,24 +210,26 @@ FOREACH(MAYA_LIB
 )
   FIND_LIBRARY(MAYA_${MAYA_LIB}_LIBRARY ${MAYA_LIB}
     PATHS
-      ENV MAYA_LOCATION
+      ${MAYA_LOCATION}
+      $ENV{MAYA_LOCATION}
       ${MAYA_BASE_DIR}
     PATH_SUFFIXES
-      	MacOS/
-	lib/
+      MacOS/
+      lib/
     DOC "Maya's ${MAYA_LIB} library path"
   )
-  
+
   LIST(APPEND ${MAYA_LIBRARIES} MAYA_${MAYA_LIB}_LIBRARY)
 ENDFOREACH(MAYA_LIB)
 
 FIND_PROGRAM(MAYA_EXECUTABLE maya
   PATHS
-    ENV MAYA_LOCATION
+    ${MAYA_LOCATION}
+    $ENV{MAYA_LOCATION}
     ${MAYA_BASE_DIR}
   PATH_SUFFIXES
-    	MacOS/
-	bin/
+    MacOS/
+    bin/
   DOC "Maya's executable path"
 )
 
diff --git a/cmake/FindOpenCL.cmake b/cmake/FindOpenCL.cmake
index 761a58ec..305a1107 100644
--- a/cmake/FindOpenCL.cmake
+++ b/cmake/FindOpenCL.cmake
@@ -67,32 +67,32 @@ find_package(PackageHandleStandardArgs)
 
 if (APPLE)
 
-    find_library( OPENCL_LIBRARIES 
+    find_library( OPENCL_LIBRARIES
         NAMES
-            OpenCL 
+            OpenCL
         DOC "OpenCL lib for OSX"
     )
 
-    find_path( OPENCL_INCLUDE_DIRS 
+    find_path( OPENCL_INCLUDE_DIRS
         NAMES
-            OpenCL/cl.h 
+            OpenCL/cl.h
         DOC "Include for OpenCL on OSX"
     )
 
     find_path( _OPENCL_CPP_INCLUDE_DIRS
-        NAMES 
-            OpenCL/cl.hpp 
+        NAMES
+            OpenCL/cl.hpp
         DOC "Include for OpenCL CPP bindings on OSX"
     )
 
 elseif (WIN32)
 
-    find_path( OPENCL_INCLUDE_DIRS 
+    find_path( OPENCL_INCLUDE_DIRS
          NAMES
-             CL/cl.h 
+             CL/cl.h
     )
 
-    find_path( _OPENCL_CPP_INCLUDE_DIRS 
+    find_path( _OPENCL_CPP_INCLUDE_DIRS
         NAMES
             CL/cl.hpp
     )
@@ -103,39 +103,39 @@ elseif (WIN32)
         set(OPENCL_LIB_DIR "$ENV{ATISTREAMSDKROOT}/lib/x86")
     endif()
 
-    find_library( OPENCL_LIBRARIES 
+    find_library( OPENCL_LIBRARIES
         NAMES
-            OpenCL.lib 
-        PATHS 
-            ${OPENCL_LIB_DIR} 
+            OpenCL.lib
+        PATHS
+            ${OPENCL_LIB_DIR}
             ENV OpenCL_LIBPATH
     )
 
     get_filename_component( _OPENCL_INC_CAND ${OPENCL_LIB_DIR}/../../include ABSOLUTE )
 
-    find_path( OPENCL_INCLUDE_DIRS 
+    find_path( OPENCL_INCLUDE_DIRS
         NAMES
-            CL/cl.h 
-        PATHS 
-            "${_OPENCL_INC_CAND}" 
+            CL/cl.h
+        PATHS
+            "${_OPENCL_INC_CAND}"
             ENV OpenCL_INCPATH
     )
 
-    find_path( _OPENCL_CPP_INCLUDE_DIRS 
+    find_path( _OPENCL_CPP_INCLUDE_DIRS
         NAMES
-            CL/cl.hpp 
-        PATHS 
-            "${_OPENCL_INC_CAND}" 
+            CL/cl.hpp
+        PATHS
+            "${_OPENCL_INC_CAND}"
             ENV OpenCL_INCPATH
     )
 
 elseif (UNIX)
 
-    find_library( OPENCL_LIBRARIES 
+    find_library( OPENCL_LIBRARIES
         NAMES
             OpenCL
-        PATHS 
-            ENV LD_LIBRARY_PATH 
+        PATHS
+            ENV LD_LIBRARY_PATH
             ENV OpenCL_LIBPATH
     )
 
@@ -143,29 +143,29 @@ elseif (UNIX)
 
     get_filename_component( _OPENCL_INC_CAND ${OPENCL_LIB_DIR}/../../include ABSOLUTE )
 
-    find_path( OPENCL_INCLUDE_DIRS 
-        NAMES 
-            CL/cl.h 
-        PATHS 
-            ${_OPENCL_INC_CAND} 
-            "/usr/local/cuda/include" 
-            "/opt/AMDAPP/include" 
+    find_path( OPENCL_INCLUDE_DIRS
+        NAMES
+            CL/cl.h
+        PATHS
+            ${_OPENCL_INC_CAND}
+            "/usr/local/cuda/include"
+            "/opt/AMDAPP/include"
             ENV OpenCL_INCPATH
     )
 
-    find_path( _OPENCL_CPP_INCLUDE_DIRS 
+    find_path( _OPENCL_CPP_INCLUDE_DIRS
         NAMES
-            CL/cl.hpp 
-        PATHS 
-            ${_OPENCL_INC_CAND} 
-            "/usr/local/cuda/include" 
-            "/opt/AMDAPP/include" 
+            CL/cl.hpp
+        PATHS
+            ${_OPENCL_INC_CAND}
+            "/usr/local/cuda/include"
+            "/opt/AMDAPP/include"
             ENV OpenCL_INCPATH
     )
 
 else ()
 
-    message( "Could not determine OpenCL platform" ) 
+    message( "Could not determine OpenCL platform" )
 
 endif ()
 
diff --git a/cmake/FindPTex.cmake b/cmake/FindPTex.cmake
new file mode 100644
index 00000000..eb35361f
--- /dev/null
+++ b/cmake/FindPTex.cmake
@@ -0,0 +1,129 @@
+#
+#     Copyright (C) Pixar. All rights reserved.
+#
+#     This license governs use of the accompanying software. If you
+#     use the software, you accept this license. If you do not accept
+#     the license, do not use the software.
+#
+#     1. Definitions
+#     The terms "reproduce," "reproduction," "derivative works," and
+#     "distribution" have the same meaning here as under U.S.
+#     copyright law.  A "contribution" is the original software, or
+#     any additions or changes to the software.
+#     A "contributor" is any person or entity that distributes its
+#     contribution under this license.
+#     "Licensed patents" are a contributor's patent claims that read
+#     directly on its contribution.
+#
+#     2. Grant of Rights
+#     (A) Copyright Grant- Subject to the terms of this license,
+#     including the license conditions and limitations in section 3,
+#     each contributor grants you a non-exclusive, worldwide,
+#     royalty-free copyright license to reproduce its contribution,
+#     prepare derivative works of its contribution, and distribute
+#     its contribution or any derivative works that you create.
+#     (B) Patent Grant- Subject to the terms of this license,
+#     including the license conditions and limitations in section 3,
+#     each contributor grants you a non-exclusive, worldwide,
+#     royalty-free license under its licensed patents to make, have
+#     made, use, sell, offer for sale, import, and/or otherwise
+#     dispose of its contribution in the software or derivative works
+#     of the contribution in the software.
+#
+#     3. Conditions and Limitations
+#     (A) No Trademark License- This license does not grant you
+#     rights to use any contributor's name, logo, or trademarks.
+#     (B) If you bring a patent claim against any contributor over
+#     patents that you claim are infringed by the software, your
+#     patent license from such contributor to the software ends
+#     automatically.
+#     (C) If you distribute any portion of the software, you must
+#     retain all copyright, patent, trademark, and attribution
+#     notices that are present in the software.
+#     (D) If you distribute any portion of the software in source
+#     code form, you may do so only under this license by including a
+#     complete copy of this license with your distribution. If you
+#     distribute any portion of the software in compiled or object
+#     code form, you may only do so under a license that complies
+#     with this license.
+#     (E) The software is licensed "as-is." You bear the risk of
+#     using it. The contributors give no express warranties,
+#     guarantees or conditions. You may have additional consumer
+#     rights under your local laws which this license cannot change.
+#     To the extent permitted under your local laws, the contributors
+#     exclude the implied warranties of merchantability, fitness for
+#     a particular purpose and non-infringement.
+#
+#
+
+# Try to find PTex library and include path.
+# Once done this will define
+#
+# PTEX_FOUND
+# PTEX_INCLUDE_DIR
+# PTEX_LIBRARY
+#
+
+if (WIN32)
+    find_path( PTEX_INCLUDE_DIR
+        NAMES
+            Ptexture.h
+        PATHS
+            ${PTEX_LOCATION}/include
+            $ENV{PTEX_LOCATION}/include
+            $ENV{PROGRAMFILES}/Ptex/include
+            /usr/include
+            DOC "The directory where Ptexture.h resides")
+    find_library( PTEX_LIBRARY
+        NAMES
+            Ptex32 Ptex32s Ptex
+        PATHS
+            ${PTEX_LOCATION}/lib
+            $ENV{PTEX_LOCATION}/lib
+            $ENV{PROGRAMFILES}/Ptex/lib
+            /usr/lib
+            /usr/lib/w32api
+            /usr/local/lib
+            /usr/X11R6/lib
+            DOC "The Ptex library")
+else ()
+    find_path( PTEX_INCLUDE_DIR
+        NAMES
+            Ptexture.h
+        PATHS
+            ${PTEX_LOCATION}/include
+            ${PTEX_LOCATION}/include/wdas
+            $ENV{PTEX_LOCATION}/include
+            $ENV{PTEX_LOCATION}/include/wdas
+            /usr/include
+            /usr/local/include
+            /usr/openwin/share/include
+            /usr/openwin/include
+            /usr/X11R6/include
+            /usr/include/X11
+            DOC "The directory where Ptexture.h resides")
+    find_library( PTEX_LIBRARY
+        NAMES
+            Ptex wdasPtex
+        PATHS
+            ${PTEX_LOCATION}/lib
+            $ENV{PTEX_LOCATION}/lib
+            /usr/lib
+            /usr/local/lib
+            /usr/openwin/lib
+            /usr/X11R6/lib
+            DOC "The Ptex library")
+endif ()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(PTEX DEFAULT_MSG
+    PTEX_INCLUDE_DIR
+    PTEX_LIBRARY
+)
+
+mark_as_advanced(
+  PTEX_INCLUDE_DIR
+  PTEX_LIBRARY
+)
+
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 100107e5..740ad433 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -55,43 +55,38 @@
 #     a particular purpose and non-infringement.
 #
 
-if (APPLE)
-    message(STATUS "The OsX platform currently does not support all "
-    "the OpenGL features required by the glutViewer example : skipping.")
-else()
-    if( OPENGL_FOUND AND GLEW_FOUND AND GLUT_FOUND)
-        add_subdirectory(glutViewer)
-    else()
-        set(MISSING "")
-
-        if (NOT OPENGL_FOUND)
-            list(APPEND MISSING OpenGL)
-        endif()
-
-        if (NOT GLEW_FOUND)
-            list(APPEND MISSING glew)
-        endif()
-
-        if (NOT GLUT_FOUND)
-            list(APPEND MISSING glut)
-        endif()
-
-        message(STATUS
-            "The following libraries could not be found : ${MISSING}. The glutViewer "
-            "example will not be available. If you have these libraries installed, "
-            "please specify their path to cmake (by setting the GLEW_LOCATION, GLUT_LOCATION "
-            "entries for instance)"
-        )
+if( OPENGL_FOUND AND (GLEW_FOUND AND GLUT_FOUND) OR (APPLE AND GLUT_FOUND))
+    add_subdirectory(glutViewer)
+    if(PTEX_FOUND AND (NOT APPLE))
+        add_subdirectory(ptexViewer)
     endif()
+else()
+    set(MISSING "")
+
+    if (NOT OPENGL_FOUND)
+        list(APPEND MISSING OpenGL)
+    endif()
+
+    if (NOT GLEW_FOUND)
+        list(APPEND MISSING glew)
+    endif()
+
+    if (NOT GLUT_FOUND)
+        list(APPEND MISSING glut)
+    endif()
+
+    message(WARNING
+        "The following libraries could not be found : ${MISSING}.  "
+        "The glutViewer and ptexViewer examples will not be available.  "
+        "If you have these libraries installed, please specify their "
+        "path to cmake (through the GLEW_LOCATION and GLUT_LOCATION "
+        "command line arguments or environment variables)."
+    )
 endif()
 
 if(MAYA_FOUND)
     add_subdirectory(mayaViewer)
-else()
-    message(STATUS
-        "Maya could not be found, so the OpenSubdiv mayaViwer plugin will not "
-        "be available. If you do have Maya installed and see this message, "
-        "please add your Maya path to cmake/FindMaya.cmake or set it in "
-        "the MAYA_LOCATION environment variable."
-    )
+    if(PTEX_FOUND)
+        add_subdirectory(mayaPtexViewer_siggraph2012)
+    endif()
 endif()
diff --git a/examples/glutViewer/cudaInit.h b/examples/common/cudaInit.h
similarity index 98%
rename from examples/glutViewer/cudaInit.h
rename to examples/common/cudaInit.h
index 4053e24d..8672e148 100644
--- a/examples/glutViewer/cudaInit.h
+++ b/examples/common/cudaInit.h
@@ -68,7 +68,7 @@ inline int _ConvertSMVer2Cores_local(int major, int minor)
         int Cores;
     } sSMtoCores;
 
-    sSMtoCores nGpuArchCoresPerSM[] = 
+    sSMtoCores nGpuArchCoresPerSM[] =
     { { 0x10,  8 }, // Tesla Generation (SM 1.0) G80 class
       { 0x11,  8 }, // Tesla Generation (SM 1.1) G8x class
       { 0x12,  8 }, // Tesla Generation (SM 1.2) G9x class
@@ -123,7 +123,7 @@ inline int cutGetMaxGflopsDeviceId()
             // If we find GPU with SM major > 2, search only these
             if ( best_SM_arch > 2 ) {
                 // If our device==dest_SM_arch, choose this, or else pass
-                if (deviceProp.major == best_SM_arch) { 
+                if (deviceProp.major == best_SM_arch) {
                     max_compute_perf  = compute_perf;
                     max_perf_device   = current_device;
                 }
diff --git a/examples/common/export_obj.py b/examples/common/export_obj.py
new file mode 100644
index 00000000..64dd08f1
--- /dev/null
+++ b/examples/common/export_obj.py
@@ -0,0 +1,69 @@
+import maya.OpenMaya as OpenMaya
+
+selectionList = OpenMaya.MSelectionList()
+OpenMaya.MGlobal.getActiveSelectionList(selectionList)
+
+path = OpenMaya.MDagPath()
+selectionList.getDagPath(0, path)
+
+meshFn = OpenMaya.MFnMesh(path)
+
+points = OpenMaya.MPointArray()
+normals = OpenMaya.MFloatVectorArray()
+meshFn.getPoints(points)
+meshFn.getVertexNormals(True, normals);
+
+f = open('out.obj', 'w')
+
+for i in range(0,points.length()):
+    f.write('v %f %f %f\n' % (points[i].x, points[i].y, points[i].z))
+
+for i in range(0,points.length()):
+    f.write('vt 0 0 \n')
+    
+for i in range(0,normals.length()):
+    f.write('vn %f %f %f\n' % (normals[i].x, normals[i].y, normals[i].z))
+
+f.write('s off\n')
+
+vertexCount = OpenMaya.MIntArray()
+vertexList = OpenMaya.MIntArray()
+meshFn.getVertices(vertexCount, vertexList)
+edgeIds = OpenMaya.MUintArray()
+edgeCreaseData = OpenMaya.MDoubleArray()
+vtxIds = OpenMaya.MUintArray()
+vtxCreaseData = OpenMaya.MDoubleArray()
+meshFn.getCreaseEdges(edgeIds, edgeCreaseData)
+#meshFn.getCreaseVertices(vtxIds, vtxCreaseData)
+
+vindex = 0
+for i in range(0,vertexCount.length()):
+    f.write('f')
+    for j in range(0, vertexCount[i]):
+        v = vertexList[vindex] + 1
+        f.write(' %d/%d/%d' % (v, v, v))
+        vindex = vindex+1
+    f.write('\n')
+
+if vtxIds.length() > 0:
+    f.write('t corner %d/%d/0' % (vtxIds.length(), vtxIds.length()))
+    for i in range(0,vtxIds.length()):
+        f.write(' %d' % vtxIds[i])
+    for i in range(0,vtxCreaseData.length()):
+        f.write(' %f' % vtxCreaseData[i])
+    f.write('\n')
+    
+for i in range(0, edgeIds.length()):
+    edgeIt = OpenMaya.MItMeshEdge(path)
+    dummy = OpenMaya.MScriptUtil().asIntPtr()
+    edgeIt.setIndex(edgeIds[i], dummy)
+    faceList = OpenMaya.MIntArray()
+    edgeIt.getConnectedFaces(faceList)
+    vid0 = edgeIt.index(0)
+    vid1 = edgeIt.index(1)
+
+    f.write('t crease 2/1/0 %d %d %f\n' % (vid0, vid1, edgeCreaseData[i]))
+
+
+f.close()
+
diff --git a/examples/common/stopwatch.h b/examples/common/stopwatch.h
index 44f6c5ae..8529d8ca 100644
--- a/examples/common/stopwatch.h
+++ b/examples/common/stopwatch.h
@@ -71,39 +71,39 @@ class Stopwatch {
 public:
 
 #ifndef _WINDOWS
-    void Start() { 
+    void Start() {
         struct timeval l_rtime;
         gettimeofday(&l_rtime,0);
-	    _elapsed = l_rtime.tv_sec + l_rtime.tv_usec/1000000.0;
+        _elapsed = l_rtime.tv_sec + l_rtime.tv_usec/1000000.0;
     }
 
     void Stop() {
         struct timeval l_rtime;
 
         gettimeofday(&l_rtime,0);
-	    _elapsed = (l_rtime.tv_sec + l_rtime.tv_usec/1000000.0) - _elapsed;
+        _elapsed = (l_rtime.tv_sec + l_rtime.tv_usec/1000000.0) - _elapsed;
         _totalElapsed += _elapsed;
     }
 
     double GetElapsed() const {
         return _elapsed;
     }
-    
+
     double GetTotalElapsed() const {
         return _totalElapsed;
-    }   
+    }
 #else
     Stopwatch() {
         QueryPerformanceFrequency(&_frequency);
     }
-    
-    void Start() 
-    { 
+
+    void Start()
+    {
         QueryPerformanceCounter(&_time);
     }
 
-    void Stop() 
-    { 
+    void Stop()
+    {
         LARGE_INTEGER currentTime;
         QueryPerformanceCounter(¤tTime);
         _elapsed = currentTime.QuadPart - _time.QuadPart;
@@ -113,7 +113,7 @@ public:
     double GetElapsed() const {
         return (double) _elapsed / _frequency.QuadPart;
     }
-    
+
     double GetTotalElapsed() const {
         return (double) _totalElapsed / _frequency.QuadPart;
     }
diff --git a/examples/glutViewer/CMakeLists.txt b/examples/glutViewer/CMakeLists.txt
index 8f00173f..b58147d0 100644
--- a/examples/glutViewer/CMakeLists.txt
+++ b/examples/glutViewer/CMakeLists.txt
@@ -57,27 +57,18 @@
 
 # *** glutViewer ***
 
-set(PLATFORM_LIBRARIES 
+set(SHADER_FILES
+     shader.glsl
+)
+
+set(PLATFORM_LIBRARIES
     ${OSD_LINK_TARGET}
-    ${ILMBASE_LIBS_DIRECTORY}
+    ${ILMBASE_LIBRARIES}
     ${OPENGL_LIBRARY}
     ${GLEW_LIBRARY}
     ${GLUT_LIBRARIES}
 )
 
-if(OPENCL_FOUND)
-    add_definitions(
-        -DOPENSUBDIV_HAS_OPENCL
-    )
-endif()
-
-if(CUDA_FOUND)
-    add_definitions(
-        -DOPENSUBDIV_HAS_CUDA
-    )
-endif()
-
-
 include_directories(
     ${PROJECT_SOURCE_DIR}/opensubdiv
     ${PROJECT_SOURCE_DIR}/regression
@@ -87,33 +78,38 @@ include_directories(
 )
 
 #-------------------------------------------------------------------------------
-# Macro for adding a (potentially cuda) executable.
-macro(_add_glut_executable target)
+# Shader Stringification
+# We want to use preprocessor include directives to include GLSL and OpenCL
+# shader source files in cpp files, but since the sources contain newline
+# characters we would need raw string literals from C++11 to do this directly.
+# To avoid depending on C++11 we instead use a small tool called "line_quote"
+# to generate source files that are suitable for direct inclusion.
+foreach(shader_file ${SHADER_FILES})
 
-    _add_executable(${target} ${ARGN})
-    
-    if(WIN32)
-        # Windows needs some of its dependency dll's copied into the same
-        # directory as the executable
-        set( LIBRARIES ${ILMBASE_LIBS_DIRECTORY} ${GLUT_LIBRARIES})
-        foreach (LIB ${LIBRARIES} )
-            string(REPLACE ".lib" ".dll" LIB ${LIB})
-            string(REPLACE ".LIB" ".DLL" LIB ${LIB})
-			add_custom_command(
-				TARGET ${target} POST_BUILD
-				COMMAND ${CMAKE_COMMAND} -E copy_if_different
-					${LIB}
-					$
-			)
-        endforeach()
-    endif()
-    
-endmacro()
+    string(REGEX REPLACE ".*[.](.*)" "\\1" extension ${shader_file})
 
+    string(REGEX REPLACE "(.*)[.].*" "\\1.inc" inc_file ${shader_file})
+    list(APPEND INC_FILES ${inc_file})
 
+    add_custom_command(
+        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${inc_file}
+        COMMAND stringify ${CMAKE_CURRENT_SOURCE_DIR}/${shader_file}
+            ${CMAKE_CURRENT_SOURCE_DIR}/${inc_file}
+        DEPENDS stringify ${CMAKE_CURRENT_SOURCE_DIR}/${shader_file}
+    )
+endforeach()
+
+if(APPLE)
 _add_glut_executable(glutViewer
-   viewer.cpp
+    viewer_compat.cpp
 )
+else()
+_add_glut_executable(glutViewer
+    viewer.cpp
+    ${SHADER_FILES}
+    ${INC_FILES}
+)
+endif()
 
 target_link_libraries(glutViewer
     ${PLATFORM_LIBRARIES}
diff --git a/examples/glutViewer/shader.glsl b/examples/glutViewer/shader.glsl
new file mode 100644
index 00000000..55d20664
--- /dev/null
+++ b/examples/glutViewer/shader.glsl
@@ -0,0 +1,245 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+
+#version 400
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec3 position;
+layout (location=1) in vec3 normal;
+
+out vec3 vPosition;
+out vec3 vNormal;
+out vec4 vColor;
+
+void main()
+{
+    vPosition = position;
+    vNormal = normal;
+    vColor = vec4(1, 1, 1, 1);
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+layout(lines_adjacency) in;
+
+#ifdef GEOMETRY_OUT_FILL
+layout(triangle_strip, max_vertices = 4) out;
+#endif
+
+#ifdef GEOMETRY_OUT_LINE
+layout(line_strip, max_vertices = 5) out;
+#endif
+
+in vec3 vPosition[4];
+in vec3 vNormal[4];
+
+#else // PRIM_TRI
+
+layout(triangles) in;
+
+#ifdef GEOMETRY_OUT_FILL
+layout(triangle_strip, max_vertices = 3) out;
+#endif
+
+#ifdef GEOMETRY_OUT_LINE
+layout(line_strip, max_vertices = 4) out;
+#endif
+
+in vec3 vPosition[3];
+in vec3 vNormal[3];
+
+#endif // PRIM_TRI/QUAD
+
+
+uniform mat4 objectToClipMatrix;
+uniform mat4 objectToEyeMatrix;
+
+flat out vec3 gFacetNormal;
+out vec3 Peye;
+out vec3 Neye;
+out vec4 Cout;
+
+void emit(int index)
+{
+    Peye = vPosition[index];
+    gl_Position = objectToClipMatrix * vec4(vPosition[index], 1);
+    Neye = (objectToEyeMatrix * vec4(vNormal[index], 0)).xyz;
+    EmitVertex();
+}
+
+void main()
+{
+    gl_PrimitiveID = gl_PrimitiveIDIn;
+    
+#ifdef PRIM_QUAD
+#ifdef GEOMETRY_OUT_FILL
+    vec3 A = vPosition[0] - vPosition[1];
+    vec3 B = vPosition[3] - vPosition[1];
+    vec3 C = vPosition[2] - vPosition[1];
+
+    gFacetNormal = (objectToEyeMatrix*vec4(normalize(cross(B, A)), 0)).xyz;
+    emit(0);
+    emit(1);
+    emit(3);
+//    gFacetNormal = (objectToEyeMatrix*vec4(normalize(cross(C, B)), 0)).xyz;
+    emit(2);
+#else  // GEOMETRY_OUT_LINE
+    emit(0);
+    emit(1);
+    emit(2);
+    emit(3);
+    emit(0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+    vec3 A = vPosition[1] - vPosition[0];
+    vec3 B = vPosition[2] - vPosition[0];
+    gFacetNormal = (objectToEyeMatrix*vec4(normalize(cross(B, A)), 0)).xyz;
+
+    emit(0);
+    emit(1);
+    emit(2);
+#ifdef GEOMETRY_OUT_LINE
+    emit(0);
+#endif //GEOMETRY_OUT_LINE
+#endif // PRIM_TRI
+
+    EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+flat in vec3 gFacetNormal;
+in vec3 Neye;
+in vec3 Peye;
+in vec4 Cout;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+    vec4 position;
+    vec4 ambient;
+    vec4 diffuse;
+    vec4 specular;
+};
+
+uniform LightSource lightSource[NUM_LIGHTS];
+
+vec4
+lighting(vec3 Peye, vec3 Neye)
+{
+    vec4 color = vec4(0);
+    vec4 material = vec4(0.4, 0.4, 0.8, 1);
+
+    for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+        vec4 Plight = lightSource[i].position;
+
+        vec3 l = (Plight.w == 0.0)
+                    ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+        vec3 n = normalize(Neye);
+        vec3 h = normalize(l + vec3(0,0,1));    // directional viewer
+
+        float d = max(0.0, dot(n, l));
+        float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+        color += lightSource[i].ambient * material
+            + d * lightSource[i].diffuse * material
+            + s * lightSource[i].specular;
+    }
+
+    color.a = 1;
+    return color;
+}
+
+#ifdef GEOMETRY_OUT_LINE
+uniform vec4 fragColor;
+void
+main()
+{
+    gl_FragColor = fragColor;
+}
+
+#else
+
+void
+main()
+{
+    vec3 N = (gl_FrontFacing ? gFacetNormal : -gFacetNormal);
+    gl_FragColor = lighting(Peye, N);
+}
+#endif // GEOMETRY_OUT_LINE
+
+#endif
diff --git a/examples/glutViewer/viewer.cpp b/examples/glutViewer/viewer.cpp
index 4a99e4a9..7245a18b 100644
--- a/examples/glutViewer/viewer.cpp
+++ b/examples/glutViewer/viewer.cpp
@@ -58,6 +58,7 @@
 #if defined(__APPLE__)
     #include 
 #else
+    #include 
     #include 
     #include 
 #endif
@@ -81,17 +82,25 @@
     #include 
     #include 
 
-    #include "cudaInit.h"
+    #include "../common/cudaInit.h"
 #endif
 
+static const char *shaderSource =
+#include "shader.inc"
+;
+
+#include 
 #include 
+#include 
+#include 
 
 //------------------------------------------------------------------------------
 struct SimpleShape {
     std::string  name;
     Scheme       scheme;
     char const * data;
-    
+
+    SimpleShape() { }
     SimpleShape( char const * idata, char const * iname, Scheme ischeme )
         : name(iname), scheme(ischeme), data(idata) { }
 };
@@ -101,7 +110,7 @@ std::vector g_defaultShapes;
 int g_currentShape = 0;
 
 
-void 
+void
 initializeShapes( ) {
 
 #include 
@@ -161,6 +170,20 @@ initializeShapes( ) {
 #include 
     g_defaultShapes.push_back(SimpleShape(catmark_tent, "catmark_tent", kCatmark));
 
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_square_hedit0, "catmark_square_hedit0", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_square_hedit1, "catmark_square_hedit1", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_square_hedit2, "catmark_square_hedit2", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_square_hedit3, "catmark_square_hedit3", kCatmark));
+
+
+
 #include 
     g_defaultShapes.push_back(SimpleShape(loop_cube_creases0, "loop_cube_creases0", kLoop));
 
@@ -191,17 +214,22 @@ int   g_frame = 0,
       g_repeatCount = 0;
 
 // GLUT GUI variables
-int   g_wire = 0,
+int   g_freeze = 0,
+      g_wire = 0,
+      g_drawCoarseMesh = 1,
       g_drawNormals = 0,
-      g_mbutton;
+      g_drawHUD = 1,
+      g_mbutton[3] = {0, 0, 0};
 
-float g_rx = 0, 
-      g_ry = 0, 
-      g_prev_x = 0, 
+float g_rotate[2] = {0, 0},
+      g_prev_x = 0,
       g_prev_y = 0,
-      g_dolly = 5;
+      g_dolly = 5,
+      g_pan[2] = {0, 0},
+      g_center[3] = {0, 0, 0},
+      g_size = 0;
 
-int   g_width, 
+int   g_width,
       g_height;
 
 // performance
@@ -209,22 +237,33 @@ float g_cpuTime = 0;
 float g_gpuTime = 0;
 
 // geometry
-std::vector g_positions,
+std::vector g_orgPositions,
+                   g_positions,
                    g_normals;
-                   
-Scheme             g_scheme;                   
+
+Scheme             g_scheme;
 
 int g_numIndices = 0;
 int g_level = 2;
 int g_kernel = OpenSubdiv::OsdKernelDispatcher::kCPU;
+float g_moveScale = 1.0f;
 
 GLuint g_indexBuffer;
+GLuint g_quadFillProgram = 0,
+       g_quadLineProgram = 0,
+       g_triFillProgram = 0,
+       g_triLineProgram = 0;
+
+
+std::vector g_coarseEdges;
+std::vector g_coarseEdgeSharpness;
+std::vector g_coarseVertexSharpness;
 
 OpenSubdiv::OsdMesh * g_osdmesh = 0;
 OpenSubdiv::OsdVertexBuffer * g_vertexBuffer = 0;
 
-//------------------------------------------------------------------------------                                        
-inline void 
+//------------------------------------------------------------------------------
+inline void
 cross(float *n, const float *p0, const float *p1, const float *p2) {
 
     float a[3] = { p1[0]-p0[0], p1[1]-p0[1], p1[2]-p0[2] };
@@ -232,15 +271,15 @@ cross(float *n, const float *p0, const float *p1, const float *p2) {
     n[0] = a[1]*b[2]-a[2]*b[1];
     n[1] = a[2]*b[0]-a[0]*b[2];
     n[2] = a[0]*b[1]-a[1]*b[0];
-    
+
     float rn = 1.0f/sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
     n[0] *= rn;
     n[1] *= rn;
     n[2] *= rn;
 }
 
-//------------------------------------------------------------------------------                                        
-inline void 
+//------------------------------------------------------------------------------
+inline void
 normalize(float * p) {
 
     float dist = sqrtf( p[0]*p[0] + p[1]*p[1]  + p[2]*p[2] );
@@ -249,9 +288,25 @@ normalize(float * p) {
     p[2]/=dist;
 }
 
+//------------------------------------------------------------------------------
+inline void
+multMatrix(float *d, const float *a, const float *b) {
 
-//------------------------------------------------------------------------------                                        
-static void 
+    for (int i=0; i<4; ++i)
+    {
+        for (int j=0; j<4; ++j)
+        {
+            d[i*4 + j] =
+                a[i*4 + 0] * b[0*4 + j] +
+                a[i*4 + 1] * b[1*4 + j] +
+                a[i*4 + 2] * b[2*4 + j] +
+                a[i*4 + 3] * b[3*4 + j];
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+static void
 calcNormals(OpenSubdiv::OsdHbrMesh * mesh, std::vector const & pos, std::vector & result ) {
 
     // calc normal vectors
@@ -259,13 +314,13 @@ calcNormals(OpenSubdiv::OsdHbrMesh * mesh, std::vector const & pos, std::
 
     int nfaces = mesh->GetNumCoarseFaces();
     for (int i = 0; i < nfaces; ++i) {
-    
+
         OpenSubdiv::OsdHbrFace * f = mesh->GetFace(i);
-        
+
         float const * p0 = &pos[f->GetVertex(0)->GetID()*3],
                     * p1 = &pos[f->GetVertex(1)->GetID()*3],
                     * p2 = &pos[f->GetVertex(2)->GetID()*3];
-                
+
         float n[3];
         cross( n, p0, p1, p2 );
 
@@ -281,30 +336,43 @@ calcNormals(OpenSubdiv::OsdHbrMesh * mesh, std::vector const & pos, std::
 }
 
 //------------------------------------------------------------------------------
-void 
+void
 updateGeom() {
 
-    int nverts = (int)g_positions.size() / 3;
-    
+    int nverts = (int)g_orgPositions.size() / 3;
+
     std::vector vertex;
     vertex.reserve(nverts*6);
-    
-    const float *p = &g_positions[0];
+
+    const float *p = &g_orgPositions[0];
     const float *n = &g_normals[0];
-       
+
+    float r = sin(g_frame*0.001f) * g_moveScale;
     for (int i = 0; i < nverts; ++i) {
         float move = 0.05f*cosf(p[0]*20+g_frame*0.01f);
+        float ct = cos(p[2] * r);
+        float st = sin(p[2] * r);
+        g_positions[i*3+0] = p[0]*ct + p[1]*st;
+        g_positions[i*3+1] = -p[0]*st + p[1]*ct;
+        g_positions[i*3+2] = p[2];
+        
+        p += 3;
+    }
+
+    p = &g_positions[0];
+    for (int i = 0; i < nverts; ++i) {
         vertex.push_back(p[0]);
-        vertex.push_back(p[1]+move);
+        vertex.push_back(p[1]);
         vertex.push_back(p[2]);
         vertex.push_back(n[0]);
         vertex.push_back(n[1]);
         vertex.push_back(n[2]);
+        
         p += 3;
         n += 3;
     }
 
-    if (!g_vertexBuffer) 
+    if (!g_vertexBuffer)
         g_vertexBuffer = g_osdmesh->InitializeVertexBuffer(6);
     g_vertexBuffer->UpdateData(&vertex[0], nverts);
 
@@ -324,41 +392,16 @@ updateGeom() {
     glBindBuffer(GL_ARRAY_BUFFER, 0);
 }
 
+//-------------------------------------------------------------------------------
+void
+fitFrame() {
+
+    g_pan[0] = g_pan[1] = 0;
+    g_dolly = g_size;
+}
+
 //------------------------------------------------------------------------------
 void
-createOsdMesh( const char * shape, int level, int kernel, Scheme scheme=kCatmark ) { 
-
-    // generate Hbr representation from "obj" description
-    OpenSubdiv::OsdHbrMesh * hmesh = simpleHbr(shape, scheme, g_positions);
-
-    g_normals.resize(g_positions.size(),0.0f);
-    calcNormals( hmesh, g_positions, g_normals );
-
-    // generate Osd mesh from Hbr mesh
-    if (g_osdmesh) delete g_osdmesh;
-    g_osdmesh = new OpenSubdiv::OsdMesh();
-    g_osdmesh->Create(hmesh, level, kernel);
-    if (g_vertexBuffer) {
-        delete g_vertexBuffer;
-        g_vertexBuffer = NULL;
-    }
-    
-    // Hbr mesh can be deleted
-    delete hmesh;
-    
-    // update element array buffer
-    const std::vector &indices = g_osdmesh->GetFarMesh()->GetFaceVertices(level);
-
-    g_numIndices = indices.size();
-    g_scheme = scheme;
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indexBuffer);
-    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*g_numIndices, &(indices[0]), GL_STATIC_DRAW);
-       
-    updateGeom();
-}    
-
-//------------------------------------------------------------------------------
-void 
 reshape(int width, int height) {
 
     g_width = width;
@@ -369,29 +412,55 @@ reshape(int width, int height) {
     #define snprintf _snprintf
 #endif
 
-#define drawString(x, y, fmt, ...)               \
+#define drawString(x, y, ...)               \
     { char line[1024]; \
-      snprintf(line, 1024, fmt, __VA_ARGS__); \
+      snprintf(line, 1024, __VA_ARGS__); \
       char *p = line; \
-      glWindowPos2f(x, y); \
+      glWindowPos2i(x, y); \
       while(*p) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *p++); } }
 
 //------------------------------------------------------------------------------
 const char *getKernelName(int kernel) {
 
-         if (kernel == OpenSubdiv::OsdKernelDispatcher::kCPU) 
+         if (kernel == OpenSubdiv::OsdKernelDispatcher::kCPU)
         return "CPU";
-    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kOPENMP) 
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kOPENMP)
         return "OpenMP";
-    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kCUDA) 
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kCUDA)
         return "Cuda";
-    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kGLSL) 
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kGLSL)
         return "GLSL";
-    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kCL) 
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kCL)
         return "OpenCL";
     return "Unknown";
 }
 
+//------------------------------------------------------------------------------
+static GLuint compileShader(GLenum shaderType, const char *section, const char *define)
+{
+    const char *sources[3];
+    char sdefine[64];
+    sprintf(sdefine, "#define %s\n", section);
+
+    sources[0] = sdefine;
+    sources[1] = define;
+    sources[2] = shaderSource;
+
+    GLuint shader = glCreateShader(shaderType);
+    glShaderSource(shader, 3, sources, NULL);
+    glCompileShader(shader);
+
+    GLint status;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+    if( status == GL_FALSE ) {
+        GLchar emsg[1024];
+        glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg);
+        fprintf(stderr, "Error compiling GLSL shader (%s): %s\n", section, emsg );
+        exit(0);
+    }
+
+    return shader;
+}
 //------------------------------------------------------------------------------
 void
 drawNormals() {
@@ -404,103 +473,312 @@ drawNormals() {
     glBindBuffer(GL_ARRAY_BUFFER, g_vertexBuffer->GetGpuBuffer());
     glGetBufferSubData(GL_ARRAY_BUFFER,0,datasize*sizeof(float),data);
 
-    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);    
     glDisable(GL_LIGHTING);
     glColor3f(0.0f, 0.0f, 0.5f);
     glBegin(GL_LINES);
-    
+
     int start = g_osdmesh->GetFarMesh()->GetSubdivision()->GetFirstVertexOffset(g_level) *
                 g_vertexBuffer->GetNumElements();
-    
+
     for (int i=start; i(shape, scheme, g_orgPositions);
+
+    g_normals.resize(g_orgPositions.size(),0.0f);
+    g_positions.resize(g_orgPositions.size(),0.0f);
+    calcNormals( hmesh, g_orgPositions, g_normals );
+
+    // save coarse topology (used for coarse mesh drawing)
+    g_coarseEdges.clear();
+    g_coarseEdgeSharpness.clear();
+    g_coarseVertexSharpness.clear();
+    int nf = hmesh->GetNumFaces();
+    for(int i=0; iGetFace(i);
+        int nv = face->GetNumVertices();
+        for(int j=0; jGetVertex(j)->GetID());
+            g_coarseEdges.push_back(face->GetVertex((j+1)%nv)->GetID());
+            g_coarseEdgeSharpness.push_back(face->GetEdge(j)->GetSharpness());
+        }
+    }
+    int nv = hmesh->GetNumVertices();
+    for(int i=0; iGetVertex(i)->GetSharpness());
+    }
+
+    // generate Osd mesh from Hbr mesh
+    if (g_osdmesh) delete g_osdmesh;
+    g_osdmesh = new OpenSubdiv::OsdMesh();
+    g_osdmesh->Create(hmesh, level, kernel);
+    if (g_vertexBuffer) {
+        delete g_vertexBuffer;
+        g_vertexBuffer = NULL;
+    }
+
+    // Hbr mesh can be deleted
+    delete hmesh;
+
+    // update element array buffer
+    const std::vector &indices = g_osdmesh->GetFarMesh()->GetFaceVertices(level);
+
+    g_numIndices = (int)indices.size();
+    g_scheme = scheme;
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indexBuffer);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*g_numIndices, &(indices[0]), GL_STATIC_DRAW);
+
+    // compute model bounding
+    float min[3] = { FLT_MAX,  FLT_MAX,  FLT_MAX};
+    float max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
+    for (size_t i=0; i GetGpuBuffer();
-#ifdef VARYING_NORMAL
-    GLuint bVarying = g_varyingBuffer->GetGpuBuffer();
+    glEnableVertexAttribArray(0);
+    glEnableVertexAttribArray(1);
     glBindBuffer(GL_ARRAY_BUFFER, bVertex);
-    glVertexPointer(3, GL_FLOAT, 12, ((float*)(0)));
 
-    glBindBuffer(GL_ARRAY_BUFFER, bVarying);
-    glNormalPointer(GL_FLOAT, 12, ((float*)(0)));
-#else
-    glBindBuffer(GL_ARRAY_BUFFER, bVertex);
-    glVertexPointer(3, GL_FLOAT, 24, ((float*)(0)));
-    glNormalPointer(GL_FLOAT, 24, ((float*)(12)));
-#endif
-
-    glEnableClientState(GL_VERTEX_ARRAY);
-    glEnableClientState(GL_NORMAL_ARRAY);
+    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 6, 0);
+    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 6, (float*)12);
 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indexBuffer);
 
-    if (g_wire == 0) {
-        glColor3f(1.0f, 1.0f, 1.0f);
-        
-        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-        glDisable(GL_LIGHTING);
-        glDrawElements(g_scheme==kLoop ? GL_TRIANGLES : GL_QUADS, g_numIndices, GL_UNSIGNED_INT, NULL);
+    GLenum primType = GL_LINES_ADJACENCY;
+    if (g_scheme == kLoop) {
+        primType = GL_TRIANGLES;
+        bindProgram(g_triFillProgram);
     } else {
-        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-        glEnable(GL_LIGHTING);
-        glDrawElements(g_scheme==kLoop ? GL_TRIANGLES : GL_QUADS, g_numIndices, GL_UNSIGNED_INT, NULL);
+        bindProgram(g_quadFillProgram);
+    }
 
-        if(g_wire == 2){
-            glColor3f(0.0f, 0.0f, 0.5f);
-            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-            glDisable(GL_LIGHTING);
-            glDrawElements(g_scheme==kLoop ? GL_TRIANGLES : GL_QUADS, g_numIndices, GL_UNSIGNED_INT, NULL);
-        }
-        glColor3f(1.0f, 1.0f, 1.0f);
+    if (g_wire > 0) {
+        glDrawElements(primType, g_numIndices, GL_UNSIGNED_INT, NULL);
     }
     
+    if (g_wire == 0 || g_wire == 2) {
+        GLuint lineProgram = g_scheme == kLoop ? g_triLineProgram : g_quadLineProgram;
+
+        bindProgram(lineProgram);
+        GLuint fragColor = glGetUniformLocation(lineProgram, "fragColor");
+        if (g_wire == 2) {
+            glProgramUniform4f(lineProgram, fragColor, 0, 0, 0.5, 1);
+        } else {
+            glProgramUniform4f(lineProgram, fragColor, 1, 1, 1, 1);
+        }
+        glDrawElements(primType, g_numIndices, GL_UNSIGNED_INT, NULL);
+    }
+    glDisableVertexAttribArray(0);
+    glDisableVertexAttribArray(1);
+
+    glUseProgram(0);
+
     if (g_drawNormals)
         drawNormals();
+    
+    if (g_drawCoarseMesh)
+        drawCoarseMesh(g_drawCoarseMesh);
 
     glBindBuffer(GL_ARRAY_BUFFER, 0);
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
     glDisableClientState(GL_VERTEX_ARRAY);
-    glDisableClientState(GL_NORMAL_ARRAY);
-
-    glColor3f(1, 1, 1);
-    drawString(10, 10, "LEVEL = %d", g_level);
-    drawString(10, 30, "# of Vertices = %d", g_osdmesh->GetFarMesh()->GetNumVertices());
-    drawString(10, 50, "KERNEL = %s", getKernelName(g_kernel));
-    drawString(10, 70, "CPU TIME = %.3f ms", g_cpuTime);
-    drawString(10, 90, "GPU TIME = %.3f ms", g_gpuTime);
-    drawString(10, 110, "SUBDIVISION = %s", g_scheme==kBilinear ? "BILINEAR" : (g_scheme == kLoop ? "LOOP" : "CATMARK"));
 
+    if (g_drawHUD) {
+        glColor3f(1, 1, 1);
+        drawString(10, 10, "LEVEL = %d", g_level);
+        drawString(10, 30, "# of Vertices = %d", g_osdmesh->GetFarMesh()->GetNumVertices());
+        drawString(10, 50, "KERNEL = %s", getKernelName(g_kernel));
+        drawString(10, 70, "CPU TIME = %.3f ms", g_cpuTime);
+        drawString(10, 90, "GPU TIME = %.3f ms", g_gpuTime);
+        drawString(10, 110, "SUBDIVISION = %s", g_scheme==kBilinear ? "BILINEAR" : (g_scheme == kLoop ? "LOOP" : "CATMARK"));
+        
+        drawString(10, g_height-30, "w:   toggle wireframe");
+        drawString(10, g_height-50, "e:   display normal vector");
+        drawString(10, g_height-70, "m:   toggle vertex deforming");
+        drawString(10, g_height-90, "h:   display control cage");
+        drawString(10, g_height-110, "n/p: change model");
+        drawString(10, g_height-130, "1-7: subdivision level");
+        drawString(10, g_height-150, "space: freeze/unfreeze time");
+    }
+        
     glFinish();
     glutSwapBuffers();
 }
@@ -508,11 +786,17 @@ display() {
 //------------------------------------------------------------------------------
 void motion(int x, int y) {
 
-    if(g_mbutton == 0){
-        g_rx += x - g_prev_x;
-        g_ry += y - g_prev_y;
-    }else if(g_mbutton == 1){
-        g_dolly -= 0.01f*(x - g_prev_x);
+    if (g_mbutton[0] && !g_mbutton[1] && !g_mbutton[2]) {
+        // orbit
+        g_rotate[0] += x - g_prev_x;
+        g_rotate[1] += y - g_prev_y;
+    } else if (!g_mbutton[0] && g_mbutton[1] && !g_mbutton[2]) {
+        // pan
+        g_pan[0] -= g_dolly*(x - g_prev_x)/g_width;
+        g_pan[1] += g_dolly*(y - g_prev_y)/g_height;
+    } else if (g_mbutton[0] && g_mbutton[1] && !g_mbutton[2]) {
+        // dolly
+        g_dolly -= g_dolly*0.01f*(x - g_prev_x);
         if(g_dolly <= 0.01) g_dolly = 0.01f;
     }
 
@@ -525,13 +809,13 @@ void mouse(int button, int state, int x, int y) {
 
     g_prev_x = float(x);
     g_prev_y = float(y);
-    g_mbutton = button;
+    g_mbutton[button] = !state;
 }
 
 //------------------------------------------------------------------------------
 void quit() {
 
-    if(g_osdmesh) 
+    if(g_osdmesh)
         delete g_osdmesh;
 
     if (g_vertexBuffer)
@@ -551,24 +835,24 @@ void kernelMenu(int k) {
 }
 
 //------------------------------------------------------------------------------
-void 
+void
 modelMenu(int m) {
 
-    if (m < 0) 
+    if (m < 0)
         m = 0;
-        
-    if (m >= (int)g_defaultShapes.size()) 
-        m = g_defaultShapes.size() - 1;
+
+    if (m >= (int)g_defaultShapes.size())
+        m = (int)g_defaultShapes.size() - 1;
 
     g_currentShape = m;
-       
+
     glutSetWindowTitle( g_defaultShapes[m].name.c_str() );
 
     createOsdMesh( g_defaultShapes[m].data, g_level, g_kernel, g_defaultShapes[ g_currentShape ].scheme );
 }
 
 //------------------------------------------------------------------------------
-void 
+void
 levelMenu(int l) {
 
     g_level = l;
@@ -577,19 +861,23 @@ levelMenu(int l) {
 }
 
 //------------------------------------------------------------------------------
-void 
+void
 menu(int m) {
 
 }
 
 //------------------------------------------------------------------------------
-void 
+void
 keyboard(unsigned char key, int x, int y) {
 
     switch (key) {
         case 'q': quit();
+        case ' ': g_freeze = (g_freeze+1)%2; break;
         case 'w': g_wire = (g_wire+1)%3; break;
         case 'e': g_drawNormals = (g_drawNormals+1)%2; break;
+        case 'f': fitFrame(); break;
+        case 'm': g_moveScale = 1.0f - g_moveScale; break;
+        case 'h': g_drawCoarseMesh = (g_drawCoarseMesh+1)%3; break;
         case '1':
         case '2':
         case '3':
@@ -599,23 +887,26 @@ keyboard(unsigned char key, int x, int y) {
         case '7': levelMenu(key-'0'); break;
         case 'n': modelMenu(++g_currentShape); break;
         case 'p': modelMenu(--g_currentShape); break;
+        case 0x1b: g_drawHUD = (g_drawHUD+1)%2; break;
     }
 }
 
 //------------------------------------------------------------------------------
-void 
+void
 idle() {
 
-    g_frame++;
+    if (not g_freeze)
+        g_frame++;
+
     updateGeom();
     glutPostRedisplay();
 
-    if(g_repeatCount != 0 && g_frame >= g_repeatCount)
+    if (g_repeatCount != 0 and g_frame >= g_repeatCount)
         quit();
 }
 
 //------------------------------------------------------------------------------
-void 
+void
 initGL() {
 
     glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
@@ -626,7 +917,7 @@ initGL() {
 
     GLfloat color[4] = {1, 1, 1, 1};
     GLfloat position[4] = {5, 5, 10, 1};
-    GLfloat ambient[4] = {0.1f, 0.1f, 0.1f, 1.0f};
+    GLfloat ambient[4] = {0.0f, 0.0f, 0.0f, 1.0f};
     GLfloat diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f};
     GLfloat shininess = 25.0;
 
@@ -637,17 +928,37 @@ initGL() {
     glLightfv(GL_LIGHT0, GL_POSITION, position);
     glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
     glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
+
+    g_quadFillProgram = linkProgram("#define PRIM_QUAD\n#define GEOMETRY_OUT_FILL\n");
+    g_quadLineProgram = linkProgram("#define PRIM_QUAD\n#define GEOMETRY_OUT_LINE\n");
+    g_triFillProgram = linkProgram("#define PRIM_TRI\n#define GEOMETRY_OUT_FILL\n");
+    g_triLineProgram = linkProgram("#define PRIM_TRI\n#define GEOMETRY_OUT_LINE\n");
 }
 
 //------------------------------------------------------------------------------
 int main(int argc, char ** argv) {
 
     glutInit(&argc, argv);
-    
+
     glutInitDisplayMode(GLUT_RGBA |GLUT_DOUBLE | GLUT_DEPTH);
     glutInitWindowSize(1024, 1024);
     glutCreateWindow("OpenSubdiv test");
 
+    std::string str;
+    if (argc > 1) {
+        std::ifstream ifs(argv[1]);
+        if (ifs) {
+            std::stringstream ss;
+            ss << ifs.rdbuf();
+            ifs.close();
+            str = ss.str();
+
+            g_defaultShapes.push_back(SimpleShape(str.c_str(), argv[1], kCatmark));
+        }
+    }
+
+
+
     initializeShapes();
 
     int smenu = glutCreateMenu(modelMenu);
@@ -667,9 +978,9 @@ int main(int argc, char ** argv) {
     OpenSubdiv::OsdGlslKernelDispatcher::Register();
 
 #if OPENSUBDIV_HAS_OPENCL
-    OpenSubdiv::OsdClKernelDispatcher::Register();    
-#endif    
-    
+    OpenSubdiv::OsdClKernelDispatcher::Register();
+#endif
+
 #if OPENSUBDIV_HAS_CUDA
     OpenSubdiv::OsdCudaKernelDispatcher::Register();
 
@@ -691,7 +1002,7 @@ int main(int argc, char ** argv) {
     glutAddSubMenu("Model", smenu);
     glutAddSubMenu("Kernel", kmenu);
     glutAttachMenu(GLUT_RIGHT_BUTTON);
-    
+
     glutDisplayFunc(display);
     glutReshapeFunc(reshape);
     glutMouseFunc(mouse);
@@ -707,17 +1018,17 @@ int main(int argc, char ** argv) {
             g_level = atoi(argv[++i]);
         else if (!strcmp(argv[i], "-c"))
             g_repeatCount = atoi(argv[++i]);
-        else 
+        else
             filename = argv[i];
     }
-    
+
     glGenBuffers(1, &g_indexBuffer);
 
     modelMenu(0);
 
     glutIdleFunc(idle);
     glutMainLoop();
-    
+
     quit();
 }
 
diff --git a/examples/glutViewer/viewer_compat.cpp b/examples/glutViewer/viewer_compat.cpp
new file mode 100644
index 00000000..cd7edc83
--- /dev/null
+++ b/examples/glutViewer/viewer_compat.cpp
@@ -0,0 +1,925 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+
+#if defined(__APPLE__)
+    #include 
+    #include 
+#else
+    #include 
+    #include 
+    #include 
+#endif
+
+#include 
+#include 
+#include 
+
+#ifdef OPENSUBDIV_HAS_GLSL
+    #include 
+#endif
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+    #include 
+#endif
+
+#ifdef OPENSUBDIV_HAS_CUDA
+    #include 
+
+    #include 
+    #include 
+
+    #include "../common/cudaInit.h"
+#endif
+
+#include 
+
+#include "../common/stopwatch.h"
+
+#include 
+#include 
+#include 
+#include 
+
+//------------------------------------------------------------------------------
+struct SimpleShape {
+    std::string  name;
+    Scheme       scheme;
+    char const * data;
+
+    SimpleShape() { }
+    SimpleShape( char const * idata, char const * iname, Scheme ischeme )
+        : name(iname), scheme(ischeme), data(idata) { }
+};
+
+std::vector g_defaultShapes;
+
+int g_currentShape = 0;
+
+
+void
+initializeShapes( ) {
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(bilinear_cube, "bilinear_cube", kBilinear));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_cube_corner0, "catmark_cube_corner0", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_cube_corner1, "catmark_cube_corner1", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_cube_corner2, "catmark_cube_corner2", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_cube_corner3, "catmark_cube_corner3", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_cube_corner4, "catmark_cube_corner4", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_cube_creases0, "catmark_cube_creases0", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_cube_creases1, "catmark_cube_creases1", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_cube, "catmark_cube", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_dart_edgecorner, "catmark_dart_edgecorner", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_dart_edgeonly, "catmark_dart_edgeonly", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_edgecorner ,"catmark_edgecorner", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_edgeonly, "catmark_edgeonly", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_pyramid_creases0, "catmark_pyramid_creases0", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_pyramid_creases1, "catmark_pyramid_creases1", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_pyramid, "catmark_pyramid", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_tent_creases0, "catmark_tent_creases0", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_tent_creases1, "catmark_tent_creases1", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_tent, "catmark_tent", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_square_hedit0, "catmark_square_hedit0", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_square_hedit1, "catmark_square_hedit1", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_square_hedit2, "catmark_square_hedit2", kCatmark));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(catmark_square_hedit3, "catmark_square_hedit3", kCatmark));
+
+
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(loop_cube_creases0, "loop_cube_creases0", kLoop));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(loop_cube_creases1, "loop_cube_creases1", kLoop));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(loop_cube, "loop_cube", kLoop));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(loop_icosahedron, "loop_icosahedron", kLoop));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(loop_saddle_edgecorner, "loop_saddle_edgecorner", kLoop));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(loop_saddle_edgeonly, "loop_saddle_edgeonly", kLoop));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(loop_triangle_edgecorner, "loop_triangle_edgecorner", kLoop));
+
+#include 
+    g_defaultShapes.push_back(SimpleShape(loop_triangle_edgeonly, "loop_triangle_edgeonly", kLoop));
+}
+
+//------------------------------------------------------------------------------
+int   g_frame = 0,
+      g_repeatCount = 0;
+
+// GLUT GUI variables
+int   g_freeze = 0,
+      g_wire = 0,
+      g_drawCoarseMesh = 1,
+      g_drawNormals = 0,
+      g_drawHUD = 1,
+      g_mbutton[3] = {0, 0, 0};
+
+float g_rotate[2] = {0, 0},
+      g_prev_x = 0,
+      g_prev_y = 0,
+      g_dolly = 5,
+      g_pan[2] = {0, 0},
+      g_center[3] = {0, 0, 0},
+      g_size = 0;
+
+int   g_width,
+      g_height;
+
+// performance
+float g_cpuTime = 0;
+float g_gpuTime = 0;
+
+// geometry
+std::vector g_orgPositions,
+                   g_positions,
+                   g_normals;
+
+Scheme             g_scheme;
+
+int g_numIndices = 0;
+int g_level = 2;
+int g_kernel = OpenSubdiv::OsdKernelDispatcher::kCPU;
+float g_moveScale = 1.0f;
+
+GLuint g_indexBuffer;
+
+std::vector g_coarseEdges;
+std::vector g_coarseEdgeSharpness;
+std::vector g_coarseVertexSharpness;
+
+OpenSubdiv::OsdMesh * g_osdmesh = 0;
+OpenSubdiv::OsdVertexBuffer * g_vertexBuffer = 0;
+
+//------------------------------------------------------------------------------
+inline void
+cross(float *n, const float *p0, const float *p1, const float *p2) {
+
+    float a[3] = { p1[0]-p0[0], p1[1]-p0[1], p1[2]-p0[2] };
+    float b[3] = { p2[0]-p0[0], p2[1]-p0[1], p2[2]-p0[2] };
+    n[0] = a[1]*b[2]-a[2]*b[1];
+    n[1] = a[2]*b[0]-a[0]*b[2];
+    n[2] = a[0]*b[1]-a[1]*b[0];
+
+    float rn = 1.0f/sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
+    n[0] *= rn;
+    n[1] *= rn;
+    n[2] *= rn;
+}
+
+//------------------------------------------------------------------------------
+inline void
+normalize(float * p) {
+
+    float dist = sqrtf( p[0]*p[0] + p[1]*p[1]  + p[2]*p[2] );
+    p[0]/=dist;
+    p[1]/=dist;
+    p[2]/=dist;
+}
+
+//------------------------------------------------------------------------------
+inline void
+multMatrix(float *d, const float *a, const float *b) {
+
+    for (int i=0; i<4; ++i)
+    {
+        for (int j=0; j<4; ++j)
+        {
+            d[i*4 + j] =
+                a[i*4 + 0] * b[0*4 + j] +
+                a[i*4 + 1] * b[1*4 + j] +
+                a[i*4 + 2] * b[2*4 + j] +
+                a[i*4 + 3] * b[3*4 + j];
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+static void
+calcNormals(OpenSubdiv::OsdHbrMesh * mesh, std::vector const & pos, std::vector & result ) {
+
+    // calc normal vectors
+    int nverts = (int)pos.size()/3;
+
+    int nfaces = mesh->GetNumCoarseFaces();
+    for (int i = 0; i < nfaces; ++i) {
+
+        OpenSubdiv::OsdHbrFace * f = mesh->GetFace(i);
+
+        float const * p0 = &pos[f->GetVertex(0)->GetID()*3],
+                    * p1 = &pos[f->GetVertex(1)->GetID()*3],
+                    * p2 = &pos[f->GetVertex(2)->GetID()*3];
+
+        float n[3];
+        cross( n, p0, p1, p2 );
+
+        for (int j = 0; j < f->GetNumVertices(); j++) {
+            int idx = f->GetVertex(j)->GetID() * 3;
+            result[idx  ] += n[0];
+            result[idx+1] += n[1];
+            result[idx+2] += n[2];
+        }
+    }
+    for (int i = 0; i < nverts; ++i)
+        normalize( &result[i*3] );
+}
+
+//------------------------------------------------------------------------------
+void
+updateGeom() {
+
+    int nverts = (int)g_orgPositions.size() / 3;
+
+    std::vector vertex;
+    vertex.reserve(nverts*6);
+
+    const float *p = &g_orgPositions[0];
+    const float *n = &g_normals[0];
+
+    float r = sin(g_frame*0.001f) * g_moveScale;
+    for (int i = 0; i < nverts; ++i) {
+        float move = 0.05f*cosf(p[0]*20+g_frame*0.01f);
+        float ct = cos(p[2] * r);
+        float st = sin(p[2] * r);
+        g_positions[i*3+0] = p[0]*ct + p[1]*st;
+        g_positions[i*3+1] = -p[0]*st + p[1]*ct;
+        g_positions[i*3+2] = p[2];
+        
+        p += 3;
+    }
+
+    p = &g_positions[0];
+    for (int i = 0; i < nverts; ++i) {
+        vertex.push_back(p[0]);
+        vertex.push_back(p[1]);
+        vertex.push_back(p[2]);
+        vertex.push_back(n[0]);
+        vertex.push_back(n[1]);
+        vertex.push_back(n[2]);
+        
+        p += 3;
+        n += 3;
+    }
+
+    if (!g_vertexBuffer)
+        g_vertexBuffer = g_osdmesh->InitializeVertexBuffer(6);
+    g_vertexBuffer->UpdateData(&vertex[0], nverts);
+
+    Stopwatch s;
+    s.Start();
+
+    g_osdmesh->Subdivide(g_vertexBuffer, NULL);
+
+    s.Stop();
+    g_cpuTime = float(s.GetElapsed() * 1000.0f);
+    s.Start();
+    g_osdmesh->Synchronize();
+    s.Stop();
+    g_gpuTime = float(s.GetElapsed() * 1000.0f);
+
+    glBindBuffer(GL_ARRAY_BUFFER, g_vertexBuffer->GetGpuBuffer());
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+//-------------------------------------------------------------------------------
+void
+fitFrame() {
+
+    g_pan[0] = g_pan[1] = 0;
+    g_dolly = g_size;
+}
+
+//------------------------------------------------------------------------------
+void
+reshape(int width, int height) {
+
+    g_width = width;
+    g_height = height;
+}
+
+#if _MSC_VER
+    #define snprintf _snprintf
+#endif
+
+#define drawString(x, y, ...)               \
+    { char line[1024]; \
+      snprintf(line, 1024, __VA_ARGS__); \
+      char *p = line; \
+      glWindowPos2f(x, y); \
+      while(*p) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *p++); } }
+
+//------------------------------------------------------------------------------
+const char *getKernelName(int kernel) {
+
+         if (kernel == OpenSubdiv::OsdKernelDispatcher::kCPU)
+        return "CPU";
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kOPENMP)
+        return "OpenMP";
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kCUDA)
+        return "Cuda";
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kGLSL)
+        return "GLSL";
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kCL)
+        return "OpenCL";
+    return "Unknown";
+}
+
+//------------------------------------------------------------------------------
+void
+drawNormals() {
+
+    float * data=0;
+    int datasize = g_osdmesh->GetTotalVertices() * g_vertexBuffer->GetNumElements();
+
+    data = new float[datasize];
+
+    glBindBuffer(GL_ARRAY_BUFFER, g_vertexBuffer->GetGpuBuffer());
+    glGetBufferSubData(GL_ARRAY_BUFFER,0,datasize*sizeof(float),data);
+
+    glDisable(GL_LIGHTING);
+    glColor3f(0.0f, 0.0f, 0.5f);
+    glBegin(GL_LINES);
+
+    int start = g_osdmesh->GetFarMesh()->GetSubdivision()->GetFirstVertexOffset(g_level) *
+                g_vertexBuffer->GetNumElements();
+
+    for (int i=start; i(shape, scheme, g_orgPositions);
+
+    g_normals.resize(g_orgPositions.size(),0.0f);
+    g_positions.resize(g_orgPositions.size(),0.0f);
+    calcNormals( hmesh, g_orgPositions, g_normals );
+
+    // save coarse topology (used for coarse mesh drawing)
+    g_coarseEdges.clear();
+    g_coarseEdgeSharpness.clear();
+    g_coarseVertexSharpness.clear();
+    int nf = hmesh->GetNumFaces();
+    for(int i=0; iGetFace(i);
+        int nv = face->GetNumVertices();
+        for(int j=0; jGetVertex(j)->GetID());
+            g_coarseEdges.push_back(face->GetVertex((j+1)%nv)->GetID());
+            g_coarseEdgeSharpness.push_back(face->GetEdge(j)->GetSharpness());
+        }
+    }
+    int nv = hmesh->GetNumVertices();
+    for(int i=0; iGetVertex(i)->GetSharpness());
+    }
+
+    // generate Osd mesh from Hbr mesh
+    if (g_osdmesh) delete g_osdmesh;
+    g_osdmesh = new OpenSubdiv::OsdMesh();
+    g_osdmesh->Create(hmesh, level, kernel);
+    if (g_vertexBuffer) {
+        delete g_vertexBuffer;
+        g_vertexBuffer = NULL;
+    }
+
+    // Hbr mesh can be deleted
+    delete hmesh;
+
+    // update element array buffer
+    const std::vector &indices = g_osdmesh->GetFarMesh()->GetFaceVertices(level);
+
+    g_numIndices = indices.size();
+    g_scheme = scheme;
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indexBuffer);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*g_numIndices, &(indices[0]), GL_STATIC_DRAW);
+
+    // compute model bounding
+    float min[3] = { FLT_MAX,  FLT_MAX,  FLT_MAX};
+    float max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
+    for (size_t i=0; i GetGpuBuffer();
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glEnableClientState(GL_NORMAL_ARRAY);
+    glBindBuffer(GL_ARRAY_BUFFER, bVertex);
+
+    glVertexPointer(3, GL_FLOAT, sizeof (GLfloat) * 6, 0);
+    glNormalPointer(GL_FLOAT, sizeof (GLfloat) * 6, (float*)12);
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indexBuffer);
+
+    GLenum primType = g_scheme == kLoop ? GL_TRIANGLES : GL_QUADS;
+
+    glEnable(GL_LIGHTING);
+    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+    if (g_wire > 0) {
+        glDrawElements(primType, g_numIndices, GL_UNSIGNED_INT, NULL);
+    }
+    glDisable(GL_LIGHTING);
+    
+    if (g_wire == 0 || g_wire == 2) {
+        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+        if (g_wire == 2) {
+            glColor4f(0, 0, 0.5, 1);
+        } else {
+            glColor4f(1, 1, 1, 1);
+        }
+        glDrawElements(primType, g_numIndices, GL_UNSIGNED_INT, NULL);
+    }
+    glDisableClientState(GL_VERTEX_ARRAY);
+    glDisableClientState(GL_NORMAL_ARRAY);
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+    if (g_drawNormals)
+        drawNormals();
+    
+    if (g_drawCoarseMesh)
+        drawCoarseMesh(g_drawCoarseMesh);
+
+    if (g_drawHUD) {
+        glColor3f(1, 1, 1);
+        drawString(10, 10, "LEVEL = %d", g_level);
+        drawString(10, 30, "# of Vertices = %d", g_osdmesh->GetFarMesh()->GetNumVertices());
+        drawString(10, 50, "KERNEL = %s", getKernelName(g_kernel));
+        drawString(10, 70, "CPU TIME = %.3f ms", g_cpuTime);
+        drawString(10, 90, "GPU TIME = %.3f ms", g_gpuTime);
+        drawString(10, 110, "SUBDIVISION = %s", g_scheme==kBilinear ? "BILINEAR" : (g_scheme == kLoop ? "LOOP" : "CATMARK"));
+        
+        drawString(10, g_height-30, "w:   toggle wireframe");
+        drawString(10, g_height-50, "e:   display normal vector");
+        drawString(10, g_height-70, "m:   toggle vertex deforming");
+        drawString(10, g_height-90, "h:   display control cage");
+        drawString(10, g_height-110, "n/p: change model");
+        drawString(10, g_height-130, "1-7: subdivision level");
+        drawString(10, g_height-150, "space: freeze/unfreeze time");
+    }
+
+    glFinish();
+    glutSwapBuffers();
+}
+
+//------------------------------------------------------------------------------
+void motion(int x, int y) {
+
+    if (g_mbutton[0] && !g_mbutton[1] && !g_mbutton[2]) {
+        // orbit
+        g_rotate[0] += x - g_prev_x;
+        g_rotate[1] += y - g_prev_y;
+    } else if (!g_mbutton[0] && g_mbutton[1] && !g_mbutton[2]) {
+        // pan
+        g_pan[0] -= g_dolly*(x - g_prev_x)/g_width;
+        g_pan[1] += g_dolly*(y - g_prev_y)/g_height;
+    } else if (g_mbutton[0] && g_mbutton[1] && !g_mbutton[2]) {
+        // dolly
+        g_dolly -= g_dolly*0.01f*(x - g_prev_x);
+        if(g_dolly <= 0.01) g_dolly = 0.01f;
+    }
+
+    g_prev_x = float(x);
+    g_prev_y = float(y);
+}
+
+//------------------------------------------------------------------------------
+void mouse(int button, int state, int x, int y) {
+
+    g_prev_x = float(x);
+    g_prev_y = float(y);
+    g_mbutton[button] = !state;
+}
+
+//------------------------------------------------------------------------------
+void quit() {
+
+    if(g_osdmesh)
+        delete g_osdmesh;
+
+    if (g_vertexBuffer)
+        delete g_vertexBuffer;
+
+#ifdef OPENSUBDIV_HAS_CUDA
+    cudaDeviceReset();
+#endif
+    exit(0);
+}
+
+//------------------------------------------------------------------------------
+void kernelMenu(int k) {
+
+    g_kernel = k;
+    createOsdMesh( g_defaultShapes[ g_currentShape ].data, g_level, g_kernel, g_defaultShapes[ g_currentShape ].scheme );
+}
+
+//------------------------------------------------------------------------------
+void
+modelMenu(int m) {
+
+    if (m < 0)
+        m = 0;
+
+    if (m >= (int)g_defaultShapes.size())
+        m = g_defaultShapes.size() - 1;
+
+    g_currentShape = m;
+
+    glutSetWindowTitle( g_defaultShapes[m].name.c_str() );
+
+    createOsdMesh( g_defaultShapes[m].data, g_level, g_kernel, g_defaultShapes[ g_currentShape ].scheme );
+}
+
+//------------------------------------------------------------------------------
+void
+levelMenu(int l) {
+
+    g_level = l;
+
+    createOsdMesh( g_defaultShapes[g_currentShape].data, g_level, g_kernel, g_defaultShapes[ g_currentShape ].scheme );
+}
+
+//------------------------------------------------------------------------------
+void
+menu(int m) {
+
+}
+
+//------------------------------------------------------------------------------
+void
+keyboard(unsigned char key, int x, int y) {
+
+    switch (key) {
+        case 'q': quit();
+        case ' ': g_freeze = (g_freeze+1)%2; break;
+        case 'w': g_wire = (g_wire+1)%3; break;
+        case 'e': g_drawNormals = (g_drawNormals+1)%2; break;
+        case 'f': fitFrame(); break;
+        case 'm': g_moveScale = 1.0f - g_moveScale; break;
+        case 'h': g_drawCoarseMesh = (g_drawCoarseMesh+1)%3; break;
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7': levelMenu(key-'0'); break;
+        case 'n': modelMenu(++g_currentShape); break;
+        case 'p': modelMenu(--g_currentShape); break;
+        case 0x1b: g_drawHUD = (g_drawHUD+1)%2; break;
+    }
+}
+
+//------------------------------------------------------------------------------
+void
+idle() {
+
+    if (not g_freeze)
+        g_frame++;
+
+    updateGeom();
+    glutPostRedisplay();
+
+    if (g_repeatCount != 0 and g_frame >= g_repeatCount)
+        quit();
+}
+
+//------------------------------------------------------------------------------
+void
+initGL() {
+
+    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
+    glEnable(GL_LIGHT0);
+    glColor3f(1, 1, 1);
+    glEnable(GL_DEPTH_TEST);
+    glDepthFunc(GL_LEQUAL);
+
+    GLfloat color[4] = {1, 1, 1, 1};
+    GLfloat position[4] = {5, 5, 10, 1};
+    GLfloat ambient[4] = {0.1f, 0.1f, 0.1f, 1.0f};
+    GLfloat diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+    GLfloat shininess = 25.0;
+
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &shininess);
+    glLightfv(GL_LIGHT0, GL_POSITION, position);
+    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
+    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
+}
+
+//------------------------------------------------------------------------------
+int main(int argc, char ** argv) {
+
+    glutInit(&argc, argv);
+
+    glutInitDisplayMode(GLUT_RGBA |GLUT_DOUBLE | GLUT_DEPTH);
+    glutInitWindowSize(1024, 1024);
+    glutCreateWindow("OpenSubdiv test");
+
+    std::string str;
+    if (argc > 1) {
+        std::ifstream ifs(argv[1]);
+        if (ifs) {
+            std::stringstream ss;
+            ss << ifs.rdbuf();
+            ifs.close();
+            str = ss.str();
+
+            g_defaultShapes.push_back(SimpleShape(str.c_str(), argv[1], kCatmark));
+        }
+    }
+
+
+
+    initializeShapes();
+
+    int smenu = glutCreateMenu(modelMenu);
+    for(int i = 0; i < (int)g_defaultShapes.size(); ++i){
+        glutAddMenuEntry( g_defaultShapes[i].name.c_str(), i);
+    }
+
+    int lmenu = glutCreateMenu(levelMenu);
+    for(int i = 1; i < 8; ++i){
+        char level[16];
+        sprintf(level, "Level %d\n", i);
+        glutAddMenuEntry(level, i);
+    }
+
+    // Register Osd compute kernels
+    OpenSubdiv::OsdCpuKernelDispatcher::Register();
+
+#if OPENSUBDIV_HAS_GLSL
+    OpenSubdiv::OsdGlslKernelDispatcher::Register();
+#endif
+
+#if OPENSUBDIV_HAS_OPENCL
+    OpenSubdiv::OsdClKernelDispatcher::Register();
+#endif
+
+#if OPENSUBDIV_HAS_CUDA
+    OpenSubdiv::OsdCudaKernelDispatcher::Register();
+
+    // Note: This function randomly crashes with linux 5.0-dev driver.
+    // cudaGetDeviceProperties overrun stack..?
+    cudaGLSetGLDevice( cutGetMaxGflopsDeviceId() );
+#endif
+
+    int kmenu = glutCreateMenu(kernelMenu);
+    int nKernels = OpenSubdiv::OsdKernelDispatcher::kMAX;
+
+    for(int i = 0; i < nKernels; ++i)
+        if(OpenSubdiv::OsdKernelDispatcher::HasKernelType(
+               OpenSubdiv::OsdKernelDispatcher::KernelType(i)))
+            glutAddMenuEntry(getKernelName(i), i);
+
+    glutCreateMenu(menu);
+    glutAddSubMenu("Level", lmenu);
+    glutAddSubMenu("Model", smenu);
+    glutAddSubMenu("Kernel", kmenu);
+    glutAttachMenu(GLUT_RIGHT_BUTTON);
+
+    glutDisplayFunc(display);
+    glutReshapeFunc(reshape);
+    glutMouseFunc(mouse);
+    glutKeyboardFunc(keyboard);
+    glutMotionFunc(motion);
+#if not defined(__APPLE__)
+    glewInit();
+#endif
+    initGL();
+
+    const char *filename = NULL;
+
+    for (int i = 1; i < argc; ++i) {
+        if (!strcmp(argv[i], "-d"))
+            g_level = atoi(argv[++i]);
+        else if (!strcmp(argv[i], "-c"))
+            g_repeatCount = atoi(argv[++i]);
+        else
+            filename = argv[i];
+    }
+
+    glGenBuffers(1, &g_indexBuffer);
+
+    modelMenu(0);
+
+    glutIdleFunc(idle);
+    glutMainLoop();
+
+    quit();
+}
+
+//------------------------------------------------------------------------------
diff --git a/examples/mayaPtexViewer_siggraph2012/AEopenSubdivPtexShaderTemplate.mel b/examples/mayaPtexViewer_siggraph2012/AEopenSubdivPtexShaderTemplate.mel
new file mode 100644
index 00000000..e4bd44b4
--- /dev/null
+++ b/examples/mayaPtexViewer_siggraph2012/AEopenSubdivPtexShaderTemplate.mel
@@ -0,0 +1,50 @@
+global proc AEopenSubdivPtexShaderTemplate(string $node)
+{
+    editorTemplate -beginScrollLayout;
+
+    editorTemplate -beginLayout "Subdivision" -collapse false;
+    editorTemplate -addControl "scheme";
+    editorTemplate -addControl "level";
+    editorTemplate -endLayout;
+
+    editorTemplate -beginLayout "PTex" -collapse false;
+    editorTemplate -addControl "colorFile";
+    editorTemplate -addControl "displacementFile";
+    editorTemplate -addControl "occlusionFile";
+    editorTemplate -endLayout;
+
+    editorTemplate -beginLayout "Shader" -collapse false;
+    editorTemplate -addControl "shaderSource";
+    editorTemplate -addControl "wireframe";
+    editorTemplate -beginNoOptimize;
+    editorTemplate -addControl "enableColor";
+    editorTemplate -addControl "enableDisplacement";
+    editorTemplate -addControl "enableOcclusion";
+    editorTemplate -endNoOptimize;
+
+    editorTemplate -addSeparator;
+    editorTemplate -addControl "diffuse";
+    editorTemplate -addControl "ambient";
+    editorTemplate -addControl "specular";
+
+    editorTemplate -addSeparator;
+    editorTemplate -addControl "fresnelBias";
+    editorTemplate -addControl "fresnelScale";
+    editorTemplate -addControl "fresnelPower";
+    editorTemplate -addControl "light";
+    editorTemplate -endLayout;
+    
+    editorTemplate -beginLayout "Environment" -collapse false;
+    editorTemplate -addControl "diffuseMap";
+    editorTemplate -addControl "environmentMap";
+    editorTemplate -endLayout;
+
+
+    editorTemplate -addExtraControls;
+
+
+    editorTemplate -endScrollLayout;
+
+    editorTemplate -suppress "nodeState";
+}
+
diff --git a/examples/mayaPtexViewer_siggraph2012/CMakeLists.txt b/examples/mayaPtexViewer_siggraph2012/CMakeLists.txt
new file mode 100644
index 00000000..a576b697
--- /dev/null
+++ b/examples/mayaPtexViewer_siggraph2012/CMakeLists.txt
@@ -0,0 +1,175 @@
+#
+#     Copyright (C) Pixar. All rights reserved.
+#
+#     This license governs use of the accompanying software. If you
+#     use the software, you accept this license. If you do not accept
+#     the license, do not use the software.
+#
+#     1. Definitions
+#     The terms "reproduce," "reproduction," "derivative works," and
+#     "distribution" have the same meaning here as under U.S.
+#     copyright law.  A "contribution" is the original software, or
+#     any additions or changes to the software.
+#     A "contributor" is any person or entity that distributes its
+#     contribution under this license.
+#     "Licensed patents" are a contributor's patent claims that read
+#     directly on its contribution.
+#
+#     2. Grant of Rights
+#     (A) Copyright Grant- Subject to the terms of this license,
+#     including the license conditions and limitations in section 3,
+#     each contributor grants you a non-exclusive, worldwide,
+#     royalty-free copyright license to reproduce its contribution,
+#     prepare derivative works of its contribution, and distribute
+#     its contribution or any derivative works that you create.
+#     (B) Patent Grant- Subject to the terms of this license,
+#     including the license conditions and limitations in section 3,
+#     each contributor grants you a non-exclusive, worldwide,
+#     royalty-free license under its licensed patents to make, have
+#     made, use, sell, offer for sale, import, and/or otherwise
+#     dispose of its contribution in the software or derivative works
+#     of the contribution in the software.
+#
+#     3. Conditions and Limitations
+#     (A) No Trademark License- This license does not grant you
+#     rights to use any contributor's name, logo, or trademarks.
+#     (B) If you bring a patent claim against any contributor over
+#     patents that you claim are infringed by the software, your
+#     patent license from such contributor to the software ends
+#     automatically.
+#     (C) If you distribute any portion of the software, you must
+#     retain all copyright, patent, trademark, and attribution
+#     notices that are present in the software.
+#     (D) If you distribute any portion of the software in source
+#     code form, you may do so only under this license by including a
+#     complete copy of this license with your distribution. If you
+#     distribute any portion of the software in compiled or object
+#     code form, you may only do so under a license that complies
+#     with this license.
+#     (E) The software is licensed "as-is." You bear the risk of
+#     using it. The contributors give no express warranties,
+#     guarantees or conditions. You may have additional consumer
+#     rights under your local laws which this license cannot change.
+#     To the extent permitted under your local laws, the contributors
+#     exclude the implied warranties of merchantability, fitness for
+#     a particular purpose and non-infringement.
+#
+
+# *** mayaPtexViewer ***
+
+set(MAYA_FIND_QUIETLY TRUE)
+
+set(SHADER_FILES
+     shader.glsl
+)
+
+if(NOT MAYA_FOUND)
+    message(STATUS
+        "Maya could not be found, so the OpenSubdiv mayaViwer plugin will not "
+        "be available. If you do have Maya installed and see this message, "
+        "please add your Maya path to cmake/FindMaya.cmake or set it in "
+        "the MAYA_LOCATION environment variable."
+    )
+    return()
+endif()
+
+include_directories(
+    ${PROJECT_SOURCE_DIR}/opensubdiv
+    ${MAYA_INCLUDE_DIRS}
+    ${ILMBASE_INCLUDE_DIR}
+    ${GLEW_INCLUDE_DIR}
+    ${PTEX_INCLUDE_DIR}
+)
+
+set(SOURCE_FILES
+    OpenSubdivPtexShader.cpp
+    hbrUtil.cpp
+    cudaUtil.cpp
+)
+
+set(HEADER_FILES
+)
+
+if(UNIX)
+    set(PLATFORM_COMPILE_FLAGS
+        -D_BOOL
+        -DREQUIRE_IOSTREAM
+        -DLINUX
+    )
+    set(PLATFORM_LIBRARIES
+    )
+    set(PLATFORM_PLUGIN_EXTENSION
+        .so
+    )
+    set(PLATFORM_LINK_FLAGS
+    )
+endif(UNIX)
+
+if(WIN32)
+    set(PLATFORM_COMPILE_FLAGS
+        /D_AFXDLL
+        /DNT_PLUGIN
+        /DREQUIRE_IOSTREAM
+    )
+    set(PLATFORM_LIBRARIES
+    )
+    set(PLATFORM_PLUGIN_EXTENSION
+        .mll
+    )
+    set(PLATFORM_LINK_FLAGS
+        "/export:initializePlugin /export:uninitializePlugin"
+    )
+endif(WIN32)
+
+add_definitions(
+    ${PLATFORM_COMPILE_FLAGS}
+)
+
+#-------------------------------------------------------------------------------
+# Shader Stringification
+# We want to use preprocessor include directives to include GLSL and OpenCL
+# shader source files in cpp files, but since the sources contain newline
+# characters we would need raw string literals from C++11 to do this directly.
+# To avoid depending on C++11 we instead use a small tool called "line_quote"
+# to generate source files that are suitable for direct inclusion.
+foreach(shader_file ${SHADER_FILES})
+
+    string(REGEX REPLACE ".*[.](.*)" "\\1" extension ${shader_file})
+
+    string(REGEX REPLACE "(.*)[.].*" "\\1.inc" inc_file ${shader_file})
+    list(APPEND INC_FILES ${inc_file})
+
+    add_custom_command(
+        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${inc_file}
+        COMMAND stringify ${CMAKE_CURRENT_SOURCE_DIR}/${shader_file}
+            ${CMAKE_CURRENT_SOURCE_DIR}/${inc_file}
+        DEPENDS stringify ${CMAKE_CURRENT_SOURCE_DIR}/${shader_file}
+    )
+endforeach()
+#-------------------------------------------------------------------------------
+
+add_library(maya_ptex_plugin SHARED
+    ${SOURCE_FILES}
+    ${HEADER_FILES}
+    ${SHADER_FILES}
+    ${INC_FILES}
+)
+
+set_target_properties(maya_ptex_plugin
+    PROPERTIES
+    OUTPUT_NAME "mayaPtexViewer"
+    SUFFIX ${PLATFORM_PLUGIN_EXTENSION}
+    LINK_FLAGS "${PLATFORM_LINK_FLAGS}"
+)
+
+target_link_libraries(maya_ptex_plugin
+    ${OSD_LINK_TARGET}
+    ${MAYA_Foundation_LIBRARY}
+    ${MAYA_OpenMaya_LIBRARY}
+    ${MAYA_OpenMayaRender_LIBRARY}
+    ${MAYA_tbb_LIBRARY}
+    ${PLATFORM_LIBRARIES}
+    ${ILMBASE_LIBRARIES}
+    ${GLEW_LIBRARY}
+    ${PTEX_LIBRARY}
+)
diff --git a/examples/mayaPtexViewer_siggraph2012/OpenSubdivPtexShader.cpp b/examples/mayaPtexViewer_siggraph2012/OpenSubdivPtexShader.cpp
new file mode 100644
index 00000000..7ed7a424
--- /dev/null
+++ b/examples/mayaPtexViewer_siggraph2012/OpenSubdivPtexShader.cpp
@@ -0,0 +1,1515 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+
+#include 
+
+// Include this first to avoid winsock2.h problems on Windows:
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include 
+extern void cudaInit();
+#include "hbrUtil.h"
+
+static const char *defaultShaderSource = 
+#include "shader.inc"
+;
+
+#define CHECK_GL_ERROR(...)  \
+    if(GLuint err = glGetError()) {   \
+    printf("GL error %x :", err); \
+    printf(__VA_ARGS__); \
+    }
+
+#ifdef __linux__
+#define MAYA2013_PREVIEW
+#endif
+
+//---------------------------------------------------------------------------
+template static int
+FindAttribute( MFnDependencyNode &fnDN, const char *nm, T *val )
+{
+    MStatus s;
+    MPlug p;
+    T ss;
+    p = fnDN.findPlug(nm, &s);
+    if(s != MS::kSuccess) return -1;
+    s = p.getValue(ss);
+    if( s != MS::kSuccess ) return -1;
+    *val = ss;
+    return 0;
+}
+//---------------------------------------------------------------------------
+template static bool
+CompareArray(const T &a, const T &b)
+{
+    if(a.length() != b.length()) return false;
+    for(unsigned int i = 0; i < a.length(); ++i){
+        if(a[i] != b[i]) return false;
+    }
+    return true;
+}
+//---------------------------------------------------------------------------
+class OpenSubdivPtexShader : public MPxHwShaderNode
+{
+public:
+    OpenSubdivPtexShader();
+    virtual ~OpenSubdivPtexShader();
+
+    static void *creator();
+    static MStatus initialize();
+
+    virtual void postConstructor() 
+        {
+            setMPSafe(false);
+        }
+
+    virtual MStatus compute(const MPlug &plug, MDataBlock &data);
+    virtual MStatus bind(const MDrawRequest &request, M3dView &view);
+    virtual MStatus unbind(const MDrawRequest &request, M3dView &view);
+    virtual MStatus geometry( const MDrawRequest& request,
+                              M3dView& view,
+                              int prim,
+                              unsigned int writable,
+                              int indexCount,
+                              const unsigned int * indexArray,
+                              int vertexCount,
+                              const int * vertexIDs,
+                              const float * vertexArray,
+                              int normalCount,
+                              const float ** normalArrays,
+                              int colorCount,
+                              const float ** colorArrays,
+                              int texCoordCount,
+                              const float ** texCoordArrays);
+    virtual MStatus glBind(const MDagPath &);
+    virtual MStatus glUnBind(const MDagPath &);
+    virtual MStatus glGeometry(const MDagPath &path,
+                               int prim,
+                               unsigned int writable,
+                               int indexCount,
+                               const unsigned int * indexArray,
+                               int vertexCount,
+                               const int * vertexIDs,
+                               const float * vertexArray,
+                               int normalCount,
+                               const float ** normalArrays,
+                               int colorCount,
+                               const float ** colorArrays,
+                               int texCoordCount,
+                               const float ** texCoordArrays);
+    virtual MStatus renderSwatchImage( MImage & image )
+        {
+            unsigned int width, height;
+            image.getSize(width, height);
+            unsigned char *p = image.pixels();
+            for(int i=0; irelease();
+            return osdptex;
+        }
+        printf("Fail in load ptex\n");
+    }
+    return NULL;
+}
+
+void
+OpenSubdivPtexShader::bindPTexture(OpenSubdiv::OsdPTexture *osdPTex, GLuint data, GLuint packing, GLuint pages, int samplerUnit) const
+{
+    glProgramUniform1i(_program, data, samplerUnit + 0);
+    glActiveTexture(GL_TEXTURE0 + samplerUnit + 0);
+    glBindTexture(GL_TEXTURE_2D_ARRAY, osdPTex->GetTexelsTexture());
+
+    glProgramUniform1i(_program, packing, samplerUnit + 1);
+    glActiveTexture(GL_TEXTURE0 + samplerUnit + 1);
+    glBindTexture(GL_TEXTURE_BUFFER, osdPTex->GetLayoutTextureBuffer());
+
+    glProgramUniform1i(_program, pages, samplerUnit + 2);
+    glActiveTexture(GL_TEXTURE0 + samplerUnit + 2);
+    glBindTexture(GL_TEXTURE_BUFFER, osdPTex->GetPagesTextureBuffer());
+
+    glActiveTexture(GL_TEXTURE0);
+}
+
+void
+OpenSubdivPtexShader::bindTextures()
+{
+    if (_ptexColor)
+    {
+        // color ptex
+        GLint texData = glGetUniformLocation(_program, "textureImage_Data");
+        GLint texPacking = glGetUniformLocation(_program, "textureImage_Packing");
+        GLint texPages = glGetUniformLocation(_program, "textureImage_Pages");
+        bindPTexture(_ptexColor, texData, texPacking, texPages, 1);
+    }
+
+    // displacement ptex
+    if (_ptexDisplacement) {
+        GLint texData = glGetUniformLocation(_program, "textureDisplace_Data");
+        GLint texPacking = glGetUniformLocation(_program, "textureDisplace_Packing");
+        GLint texPages = glGetUniformLocation(_program, "textureDisplace_Pages");
+        bindPTexture(_ptexDisplacement, texData, texPacking, texPages, 4);
+    }
+    
+    // occlusion ptex
+    if (_ptexOcclusion) {
+        GLint texData = glGetUniformLocation(_program, "textureOcclusion_Data");
+        GLint texPacking = glGetUniformLocation(_program, "textureOcclusion_Packing");
+        GLint texPages = glGetUniformLocation(_program, "textureOcclusion_Pages");
+        bindPTexture(_ptexOcclusion, texData, texPacking, texPages, 7);
+    }
+
+    MHWRender::MRenderer *theRenderer = MHWRender::MRenderer::theRenderer();
+    MHWRender::MTextureManager *theTextureManager = theRenderer->getTextureManager();
+
+    if (_invalidateDiffuseMap)
+    {
+        MHWRender::MTexture *texture = theTextureManager->acquireTexture(_diffuseMap);
+        if (texture) {
+            GLint difmap = glGetUniformLocation(_program, "diffuseMap");
+            glProgramUniform1i(_program, difmap, 10);
+            glActiveTexture(GL_TEXTURE0+10);
+            glBindTexture(GL_TEXTURE_2D, *(GLuint*)texture->resourceHandle());
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            _invalidateDiffuseMap = false;
+        } else {
+            printf("Can't read %s\n", _diffuseMap.asChar());
+        }
+    }
+    if (_invalidateEnvMap)
+    {
+        MHWRender::MTexture *texture = theTextureManager->acquireTexture(_environmentMap);
+        if (texture) {
+            GLint envmap = glGetUniformLocation(_program, "environmentMap");
+            glProgramUniform1i(_program, envmap, 11);
+            glActiveTexture(GL_TEXTURE0+11);
+            glBindTexture(GL_TEXTURE_2D, *(GLuint*)texture->resourceHandle());
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            _invalidateEnvMap = false;
+        } else {
+            printf("Can't read %s\n", _environmentMap.asChar());
+        }
+    }
+}
+
+static MColor getColor(MObject object, MObject attr)
+{
+    MPlug plug(object, attr);
+    
+    MObject data;
+    plug.getValue(data);
+    MFnNumericData numFn(data);
+    float color[3];
+    numFn.getData(color[0], color[1], color[2]);
+    return MColor(color[0], color[1], color[2]);
+}
+
+static MFloatVector getVector(MObject object, MObject attr)
+{
+    MPlug plug(object, attr);
+    
+    MObject data;
+    plug.getValue(data);
+    MFnNumericData numFn(data);
+    float color[3];
+    numFn.getData(color[0], color[1], color[2]);
+    return MVector(color[0], color[1], color[2]);
+}
+
+void
+OpenSubdivPtexShader::updateUniforms()
+{
+    MObject object = thisMObject();
+    MColor diffuse = getColor(object, aDiffuse);
+    MColor ambient = getColor(object, aAmbient);
+    MColor specular = getColor(object, aSpecular);
+    MFnDependencyNode depFn(object);
+    float fresnelBias, fresnelScale, fresnelPower;
+    FindAttribute(depFn, "fresnelBias", &fresnelBias);
+    FindAttribute(depFn, "fresnelScale", &fresnelScale);
+    FindAttribute(depFn, "fresnelPower", &fresnelPower);
+    MFloatVector light = getVector(object, aLight);
+    FindAttribute(depFn, "wireframe", &_wireframe);
+
+    glProgramUniform3f(_program, glGetUniformLocation(_program, "diffuse"),
+                       diffuse.r, diffuse.g, diffuse.b);
+    glProgramUniform3f(_program, glGetUniformLocation(_program, "ambient"),
+                       ambient.r, ambient.g, ambient.b);
+    glProgramUniform3f(_program, glGetUniformLocation(_program, "specular"),
+                       specular.r, specular.g, specular.b);
+    glProgramUniform3f(_program, glGetUniformLocation(_program, "light"),
+                       light.x, light.y, light.z);
+
+    glProgramUniform1f(_program, glGetUniformLocation(_program, "fresnelBias"), fresnelBias);
+    glProgramUniform1f(_program, glGetUniformLocation(_program, "fresnelScale"), fresnelScale);
+    glProgramUniform1f(_program, glGetUniformLocation(_program, "fresnelPower"), fresnelPower);
+}
+
+//------------------------------------------------------------------------------
+bool
+OpenSubdivPtexShader::setInternalValueInContext(const MPlug &plug, const MDataHandle &handle, MDGContext &)
+{
+    if(plug == level)
+    {
+        _level = handle.asLong();
+    }
+    else if(plug == scheme)
+    {
+        _scheme = handle.asShort();
+    }
+    else if(plug == environmentMapFile)
+    {
+        _invalidateEnvMap = true;
+        _environmentMap = handle.asString();
+    }
+    else if(plug == diffuseMapFile)
+    {
+        _invalidateDiffuseMap = true;
+        _diffuseMap = handle.asString();
+    }
+    else if(plug == colorFile)
+    {
+        MString filename = handle.asString();
+        if (_ptexColor) delete _ptexColor;
+        _ptexColor = loadPtex(filename);
+    }
+    else if(plug == displacementFile) 
+    {
+        MString filename = handle.asString();
+        if (_ptexDisplacement) delete _ptexDisplacement;
+        _ptexDisplacement = loadPtex(filename);
+    }
+    else if(plug == occlusionFile)
+    {
+        MString filename = handle.asString();
+        if (_ptexOcclusion) delete _ptexOcclusion;
+        _ptexOcclusion = loadPtex(filename);
+    }
+    else if(plug == shaderSource)
+    {
+        MString filename = handle.asString();
+        std::ifstream ifs;
+        ifs.open(filename.asChar());
+        if (ifs.fail()) {
+            printf("link default shader\n");
+            _shaderSource = defaultShaderSource;
+        } else {
+            printf("link %s shader\n", filename.asChar());
+            std::stringstream buffer;
+            buffer << ifs.rdbuf();
+            _shaderSource = buffer.str();
+        }
+        ifs.close();
+        linkProgram();
+    }
+    else if(plug == enableColor)
+    {
+        _enableColor = handle.asBool();
+        linkProgram();
+    }
+    else if(plug == enableDisplacement)
+    {
+        _enableDisplacement = handle.asBool();
+        linkProgram();
+    }
+    else if(plug == enableOcclusion)
+    {
+        _enableOcclusion = handle.asBool();
+        linkProgram();
+    }
+    return false;
+}
+
+MStatus
+OpenSubdivPtexShader::bind(const MDrawRequest& request, M3dView &view)
+{
+    return MS::kSuccess;
+}
+
+MStatus
+OpenSubdivPtexShader::unbind(const MDrawRequest& request, M3dView &view)
+{
+    return MS::kSuccess;
+}
+
+MStatus
+OpenSubdivPtexShader::geometry( const MDrawRequest& request,
+                                M3dView& view,
+                                int prim,
+                                unsigned int writable,
+                                int indexCount,
+                                const unsigned int * indexArray,
+                                int vertexCount,
+                                const int * vertexIDs,
+                                const float * vertexArray,
+                                int normalCount,
+                                const float ** normalArrays,
+                                int colorCount,
+                                const float ** colorArrays,
+                                int texCoordCount,
+                                const float ** texCoordArrays)
+{
+    return MS::kSuccess;
+}
+
+MStatus OpenSubdivPtexShader::glBind(const MDagPath &)
+{
+    return MS::kSuccess;
+}
+MStatus OpenSubdivPtexShader::glUnBind(const MDagPath &)
+{
+    return MS::kSuccess;
+}
+
+MStatus OpenSubdivPtexShader::glGeometry(const MDagPath &path,
+                               int prim,
+                               unsigned int writable,
+                               int indexCount,
+                               const unsigned int * indexArray,
+                               int vertexCount,
+                               const int * vertexIDs,
+                               const float * vertexArray,
+                               int normalCount,
+                               const float ** normalArrays,
+                               int colorCount,
+                               const float ** colorArrays,
+                               int texCoordCount,
+                               const float ** texCoordArrays)
+{
+    return MS::kSuccess;
+}
+
+
+GLuint
+OpenSubdivPtexShader::compileShader(GLenum shaderType, const char *section, const char *shaderSource)
+{
+    int color = _enableColor ? 1 : 0;
+    int displacement = _enableDisplacement ? 1 : 0;
+    int occlusion = _enableOcclusion ? 1 : 0;
+
+    const char *sources[2];
+    char define[1024];
+    sprintf(define,
+            "#define %s\n"
+            "#define USE_PTEX_COLOR %d\n"
+            "#define USE_PTEX_OCCLUSION %d\n"
+            "#define USE_PTEX_DISPLACEMENT %d\n",
+            section, color, occlusion, displacement);
+
+    sources[0] = define;
+    sources[1] = shaderSource;
+
+    GLuint shader = glCreateShader(shaderType);
+    glShaderSource(shader, 2, sources, NULL);
+    glCompileShader(shader);
+
+    GLint status;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+    if( status == GL_FALSE ) {
+        GLchar emsg[1024];
+        glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg);
+        fprintf(stderr, "Error compiling GLSL shader (%s): %s\n", section, emsg ); 
+        return 0;
+    }
+
+    return shader;
+}
+
+
+void
+OpenSubdivPtexShader::linkProgram()
+{
+    printf( "Link Program\n");
+    CHECK_GL_ERROR("link program 1\n");
+    if (_program)
+        glDeleteProgram(_program);
+
+    _invalidateEnvMap = true;
+    _invalidateDiffuseMap = true;
+
+    _program = glCreateProgram();
+
+    CHECK_GL_ERROR("link program 2\n");
+
+    const char *src = _shaderSource.c_str();
+
+    GLuint vertexShader         = compileShader(GL_VERTEX_SHADER,
+                                                "VERTEX_SHADER", src);
+    GLuint geometryShader       = compileShader(GL_GEOMETRY_SHADER,
+                                                "GEOMETRY_SHADER", src);
+    GLuint fragmentShader       = compileShader(GL_FRAGMENT_SHADER,
+                                                "FRAGMENT_SHADER", src);
+
+    glAttachShader(_program, vertexShader);
+    glAttachShader(_program, geometryShader);
+    glAttachShader(_program, fragmentShader);
+
+    glLinkProgram(_program);
+
+    glDeleteShader(vertexShader);
+    glDeleteShader(geometryShader);
+    glDeleteShader(fragmentShader);
+
+    CHECK_GL_ERROR("link program done\n");
+    GLint status;  
+    glGetProgramiv(_program, GL_LINK_STATUS, &status );      
+    if( status == GL_FALSE ) {
+        GLchar emsg[1024];
+        glGetProgramInfoLog(_program, sizeof(emsg), 0, emsg);
+        fprintf(stderr, "Error linking GLSL program : %s\n", emsg ); 
+        _program = 0;
+        return;
+    }
+}
+
+//---------------------------------------------------------------------------
+// Viewport 2.0 override implementation
+//---------------------------------------------------------------------------
+class OsdPtexMeshData : public MUserData
+{
+public:
+    OsdPtexMeshData(MObject mesh);
+    virtual ~OsdPtexMeshData();
+
+    void populateIfNeeded(int level, int scheme);
+    void updateGeometry(const MHWRender::MVertexBuffer *point, const MHWRender::MVertexBuffer *normal);
+    void prepare();
+    void draw(GLuint program) const;
+    void update();
+
+private:
+    void initializeOsd();
+    void initializeIndexBuffer();
+    void bindPtexCoordinates(GLuint program) const;
+
+    MObject _mesh;
+
+    OpenSubdiv::OsdHbrMesh *_hbrmesh;
+    OpenSubdiv::OsdMesh *_osdmesh;
+//    OpenSubdiv::OsdVertexBuffer *_vertexBuffer;
+    OpenSubdiv::OsdVertexBuffer *_positionBuffer, *_normalBuffer;
+
+    MFloatPointArray _pointArray;
+    MFloatVectorArray  _normalArray;
+
+    // topology cache
+    MIntArray _vertexList;
+    MUintArray _edgeIds, _vtxIds;
+    MDoubleArray _edgeCreaseData, _vtxCreaseData;
+    int _level;
+    int _maxlevel;
+    int _interpBoundary;
+    int _scheme;
+
+    int _numIndices;
+    GLuint _indexBuffer;
+
+    bool _needsUpdate;
+    bool _needsInitializeOsd;
+    bool _needsInitializeIndexBuffer;
+};
+
+OsdPtexMeshData::OsdPtexMeshData(MObject mesh) :
+    MUserData(false), _mesh(mesh),
+    _hbrmesh(NULL), _osdmesh(NULL),
+//    _vertexBuffer(NULL),
+    _positionBuffer(NULL), _normalBuffer(NULL),
+    _level(0),
+    _interpBoundary(0),
+    _scheme(0),
+    _numIndices(0),
+    _indexBuffer(0),
+    _needsUpdate(false),
+    _needsInitializeOsd(false),
+    _needsInitializeIndexBuffer(false)
+{
+}
+
+OsdPtexMeshData::~OsdPtexMeshData() 
+{
+    if (_hbrmesh) delete _hbrmesh;
+    if (_osdmesh) delete _osdmesh;
+//    if (_vertexBuffer) delete _vertexBuffer;
+    if (_positionBuffer) delete _positionBuffer;
+    if (_normalBuffer) delete _normalBuffer;
+    if (_indexBuffer)
+        glDeleteBuffers(1, &_indexBuffer);
+
+}
+
+void
+OsdPtexMeshData::populateIfNeeded(int lv, int scheme)
+{
+    MStatus s;
+
+    if (scheme == 1) scheme = 0; // avoid loop for now
+
+    MFnMesh meshFn(_mesh, &s);
+    if (s != MS::kSuccess) return;
+
+    if (lv < 1) lv =1;
+
+    MIntArray vertexCount, vertexList;
+    meshFn.getVertices(vertexCount, vertexList);
+    MUintArray edgeIds;
+    MDoubleArray edgeCreaseData;
+    meshFn.getCreaseEdges(edgeIds, edgeCreaseData);
+    MUintArray vtxIds;
+    MDoubleArray vtxCreaseData;
+    meshFn.getCreaseVertices(vtxIds, vtxCreaseData );
+
+    if (vertexCount.length() == 0) return;
+
+    short interpBoundary = 0;
+    FindAttribute(meshFn, "boundaryRule", &interpBoundary);
+
+    if(CompareArray(_vertexList, vertexList) &&
+       CompareArray(_edgeIds, edgeIds) &&
+       CompareArray(_edgeCreaseData, edgeCreaseData) &&
+       CompareArray(_vtxIds, vtxIds) &&
+       CompareArray(_vtxCreaseData, vtxCreaseData) &&
+       _interpBoundary == interpBoundary &&
+       _scheme == scheme &&
+       _maxlevel >= lv)
+    {
+        if(_level != lv) {
+            _level = lv;
+            _needsInitializeIndexBuffer = true;
+        }
+        return;
+    }
+
+    printf("Populate %s, level = %d < %d\n", meshFn.name().asChar(), lv, _level);
+
+    // update topology
+    _vertexList = vertexList;
+    _edgeIds = edgeIds;
+    _edgeCreaseData = edgeCreaseData;
+    _vtxIds = vtxIds;
+    _vtxCreaseData = vtxCreaseData;
+    _maxlevel = std::max(lv, _maxlevel);
+    _level = lv;
+    _interpBoundary = interpBoundary;
+    _scheme = scheme;
+
+    std::vector numIndices, faceIndices, edgeCreaseIndices, vtxCreaseIndices;
+    std::vector edgeCreases, vtxCreases;
+    numIndices.resize(vertexCount.length());
+    faceIndices.resize(vertexList.length());
+    for(int i = 0; i < (int)vertexCount.length(); ++i) numIndices[i] = vertexCount[i];
+    for(int i = 0; i < (int)vertexList.length(); ++i) faceIndices[i] = vertexList[i];
+    vtxCreaseIndices.resize(vtxIds.length());
+    for(int i = 0; i < (int)vtxIds.length(); ++i) vtxCreaseIndices[i] = vtxIds[i];
+    vtxCreases.resize(vtxCreaseData.length());
+    for(int i = 0; i < (int)vtxCreaseData.length(); ++i)
+        vtxCreases[i] = (float)vtxCreaseData[i];
+    edgeCreases.resize(edgeCreaseData.length());
+    for(int i = 0; i < (int)edgeCreaseData.length(); ++i)
+        edgeCreases[i] = (float)edgeCreaseData[i];
+
+    // edge crease index is stored as pair of   ...
+    int nEdgeIds = edgeIds.length();
+    edgeCreaseIndices.resize(nEdgeIds*2);
+    for(int i = 0; i < nEdgeIds; ++i){
+        int2 vertices;
+        if (meshFn.getEdgeVertices(edgeIds[i], vertices) != MS::kSuccess) {
+            s.perror("ERROR can't get creased edge vertices");
+            continue;
+        }
+        edgeCreaseIndices[i*2] = vertices[0];
+        edgeCreaseIndices[i*2+1] = vertices[1];
+    }
+
+    _hbrmesh = ConvertToHBR(meshFn.numVertices(), numIndices, faceIndices,
+                            vtxCreaseIndices, vtxCreases,
+                            std::vector(), std::vector(),
+                            edgeCreaseIndices, edgeCreases,
+                            interpBoundary, scheme);
+
+    // GL function can't be used in prepareForDraw API.
+    _needsInitializeOsd = true;
+}
+
+void
+OsdPtexMeshData::initializeOsd()
+{
+    if (!_hbrmesh)
+        return;
+
+    // create Osd mesh
+    int kernel = OpenSubdiv::OsdKernelDispatcher::kCUDA;
+//    int kernel = OpenSubdiv::OsdKernelDispatcher::kCL;
+//    int kernel = OpenSubdiv::OsdKernelDispatcher::kCPU;
+//    if (OpenSubdiv::OsdKernelDispatcher::HasKernelType(OpenSubdiv::OsdKernelDispatcher::kOPENMP)) {
+//        kernel = OpenSubdiv::OsdKernelDispatcher::kOPENMP;
+//    }
+    if (_osdmesh)
+        delete _osdmesh;
+
+    _osdmesh = new OpenSubdiv::OsdMesh();
+    _osdmesh->Create(_hbrmesh, _level, kernel);
+    delete _hbrmesh;
+    _hbrmesh = NULL;
+
+    // create vertex buffer
+//    if (_vertexBuffer) delete _vertexBuffer;
+//    _vertexBuffer = _osdmesh->InitializeVertexBuffer(6 /* position + normal */);
+    if (_positionBuffer) delete _positionBuffer;
+    if (_normalBuffer) delete _normalBuffer;
+    _positionBuffer = _osdmesh->InitializeVertexBuffer(3);
+    _normalBuffer = _osdmesh->InitializeVertexBuffer(3);
+
+    _needsInitializeOsd = false;
+    _needsInitializeIndexBuffer = true;
+
+    // get geometry from maya mesh
+    MFnMesh meshFn(_mesh);
+    meshFn.getPoints(_pointArray);
+    meshFn.getVertexNormals(true, _normalArray);
+
+    _needsUpdate = true;
+}
+
+void
+OsdPtexMeshData::updateGeometry(const MHWRender::MVertexBuffer *points, const MHWRender::MVertexBuffer *normals)
+{
+    // Update vertex
+//    if (!_vertexBuffer) return;
+    if (!_positionBuffer) return;
+    if (!_normalBuffer) return;
+
+    int nCoarsePoints = _pointArray.length();
+
+    glBindBuffer(GL_COPY_READ_BUFFER, *(GLuint*)points->resourceHandle());
+    glBindBuffer(GL_COPY_WRITE_BUFFER, _positionBuffer->GetGpuBuffer());
+    glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, nCoarsePoints*3*sizeof(float));
+
+    glBindBuffer(GL_COPY_READ_BUFFER, *(GLuint*)normals->resourceHandle());
+    glBindBuffer(GL_COPY_WRITE_BUFFER, _normalBuffer->GetGpuBuffer());
+    glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, nCoarsePoints*3*sizeof(float));
+    
+    glBindBuffer(GL_COPY_READ_BUFFER, 0);
+    glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
+
+    _osdmesh->Subdivide(_positionBuffer, NULL);
+    _osdmesh->Subdivide(_normalBuffer, NULL);
+
+#if 0
+    float *pbuffer = new float[nCoarsePoints*3];
+    float *nbuffer = new float[nCoarsePoints*3];
+    glBindBuffer(GL_ARRAY_BUFFER, *(GLuint*)points->resourceHandle());
+    glGetBufferSubData(GL_ARRAY_BUFFER, 0, nCoarsePoints*3*sizeof(float), pbuffer);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+    glBindBuffer(GL_ARRAY_BUFFER, *(GLuint*)normals->resourceHandle());
+    glGetBufferSubData(GL_ARRAY_BUFFER, 0, nCoarsePoints*3*sizeof(float), nbuffer);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+    const float *p = pbuffer, *n = nbuffer;;
+    for(int i = 0; i vertex;
+    vertex.resize(nPoints*6);
+
+    for(int i = 0; i < nPoints; ++i){
+        vertex[i*6+0] = _pointArray[i].x;
+        vertex[i*6+1] = _pointArray[i].y;
+        vertex[i*6+2] = _pointArray[i].z;
+        vertex[i*6+3] = _normalArray[i].x;
+        vertex[i*6+4] = _normalArray[i].y;
+        vertex[i*6+5] = _normalArray[i].z;
+    }
+
+    _vertexBuffer->UpdateData(&vertex.at(0), nPoints);
+    _osdmesh->Subdivide(_vertexBuffer, NULL);
+#endif
+
+    _needsUpdate = false;
+}
+
+void
+OsdPtexMeshData::initializeIndexBuffer()
+{
+    // update element array buffer
+    const std::vector indices = _osdmesh->GetFarMesh()->GetFaceVertices(_level);
+    _numIndices = indices.size();
+
+    if (_indexBuffer == 0)
+        glGenBuffers(1, &_indexBuffer);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*_numIndices,
+                 &(indices[0]), GL_STATIC_DRAW);
+
+    _needsInitializeIndexBuffer = false;
+}
+
+void
+OsdPtexMeshData::bindPtexCoordinates(GLuint program) const
+{
+    if (!_osdmesh) return;
+
+    // bind ptexture
+    GLint texIndices = glGetUniformLocation(program, "ptexIndices");
+    GLint ptexLevel = glGetUniformLocation(program, "ptexLevel");
+        
+    glProgramUniform1i(program, ptexLevel, 1<<_level);
+    glProgramUniform1i(program, texIndices, 0);
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_BUFFER, _osdmesh->GetPtexCoordinatesTextureBuffer(_level));
+}
+
+void
+OsdPtexMeshData::prepare()
+{
+    if (_needsInitializeOsd) {
+        const_cast(this)->initializeOsd();
+    }
+    if (_needsInitializeIndexBuffer) {
+        const_cast(this)->initializeIndexBuffer();
+    }
+}
+
+void
+OsdPtexMeshData::draw(GLuint program) const
+{
+    MStatus status;
+
+    bindPtexCoordinates(program);
+
+    CHECK_GL_ERROR("draw begin\n");
+
+    glEnableVertexAttribArray(0);
+    glEnableVertexAttribArray(1);
+#if 0
+    GLuint bVertex = _vertexBuffer->GetGpuBuffer();
+    glBindBuffer(GL_ARRAY_BUFFER, bVertex);
+
+    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 6, 0);
+    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 6, (float*)12);
+#else
+    GLuint bPosition = _positionBuffer->GetGpuBuffer();
+    GLuint bNormal = _normalBuffer->GetGpuBuffer();
+    glBindBuffer(GL_ARRAY_BUFFER, bPosition);
+    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 3, 0);
+
+    glBindBuffer(GL_ARRAY_BUFFER, bNormal);
+    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 3, 0);
+#endif
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
+
+    CHECK_GL_ERROR("before draw elements\n");
+    glDrawElements(GL_LINES_ADJACENCY, _numIndices, GL_UNSIGNED_INT, 0);
+    
+    glDisableVertexAttribArray(0);
+    glDisableVertexAttribArray(1);
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+    CHECK_GL_ERROR("draw done\n");
+}
+
+// ------------------------------------------------------------------------------
+class OpenSubdivPtexShaderOverride : public MHWRender::MPxShaderOverride
+{
+public:
+    static MHWRender::MPxShaderOverride* creator(const MObject &obj)
+        {
+            return new OpenSubdivPtexShaderOverride(obj);
+        }
+    virtual ~OpenSubdivPtexShaderOverride();
+    
+    virtual MString initialize(const MInitContext &initContext, MInitFeedback &initFeedback)
+        {
+            MString empty;
+
+            {
+                MHWRender::MVertexBufferDescriptor positionDesc(
+                    empty,
+                    MHWRender::MGeometry::kPosition,
+                    MHWRender::MGeometry::kFloat,
+                    3);
+                addGeometryRequirement(positionDesc);
+            }
+
+            MHWRender::MVertexBufferDescriptor positionDesc(
+                "osdPosition",
+                MHWRender::MGeometry::kTangent,
+                MHWRender::MGeometry::kFloat,
+                3);
+            positionDesc.setSemanticName("osdPosition");
+            addGeometryRequirement(positionDesc);
+
+            MHWRender::MVertexBufferDescriptor normalDesc(
+                "osdNormal",
+                MHWRender::MGeometry::kBitangent,
+                MHWRender::MGeometry::kFloat,
+                3);
+            normalDesc.setSemanticName("osdNormal");
+            addGeometryRequirement(normalDesc);
+
+            if (initFeedback.customData == NULL) {
+                OsdPtexMeshData *data = new OsdPtexMeshData(initContext.dagPath.node());
+                initFeedback.customData = data;
+            }
+
+            return MString("OpenSubdivPtexShaderOverride");
+        }
+
+    virtual void updateDG(MObject object)
+        {
+            if (object == MObject::kNullObj) 
+                return;
+
+            _shader = (OpenSubdivPtexShader*)MPxHwShaderNode::getHwShaderNodePtr(object);
+            if (_shader) {
+//                MFnDependencyNode depFn(object);
+//                FindAttribute(depFn, "level", &_level);
+
+                _shader->updateUniforms();
+            }
+        }
+    virtual void updateDevice()
+        {
+            // only place to access GPU device safely
+        }
+    virtual void endUpdate()
+        {
+        }
+
+/*
+    virtual void activateKey(MHWRender::MDrawContext &context, const MString &key)
+        {
+        }
+
+    virtual void terminateKey(MHWRender::MDrawContext &context, const MString &key)
+        {
+        }
+*/
+
+    virtual bool draw(MHWRender::MDrawContext &context, const MHWRender::MRenderItemList &renderItemList) const;
+
+    virtual bool rebuildAlways()
+        {
+            return false;
+        }
+
+    virtual MHWRender::DrawAPI supportedDrawAPIs() const 
+        {
+            return MHWRender::kOpenGL;
+        }
+    virtual bool isTransparent()
+        {
+            return true;
+        }
+    
+
+private:
+    OpenSubdivPtexShaderOverride(const MObject &obj);
+
+    OpenSubdivPtexShader *_shader;
+    int _level;
+
+};
+
+
+OpenSubdivPtexShaderOverride::OpenSubdivPtexShaderOverride(const MObject &obj)
+    : MHWRender::MPxShaderOverride(obj),
+      _shader(NULL)
+{
+}
+
+OpenSubdivPtexShaderOverride::~OpenSubdivPtexShaderOverride()
+{
+}
+
+
+bool
+OpenSubdivPtexShaderOverride::draw(MHWRender::MDrawContext &context, const MHWRender::MRenderItemList &renderItemList) const
+{
+	using namespace MHWRender;
+    {
+        MHWRender::MStateManager *stateMgr = context.getStateManager();
+        static const MDepthStencilState * depthState = NULL;
+        if (!depthState) {
+            MDepthStencilStateDesc desc;
+            depthState = stateMgr->acquireDepthStencilState(desc);
+        }
+        static const MBlendState *blendState = NULL;
+        if (!blendState) {
+            MBlendStateDesc desc;
+
+            for(int i = 0; i < (desc.independentBlendEnable ? MHWRender::MBlendState::kMaxTargets : 1); ++i) 
+            {    
+                desc.targetBlends[i].blendEnable = false;
+            }
+            blendState = stateMgr->acquireBlendState(desc);
+        }
+
+        stateMgr->setDepthStencilState(depthState);
+        stateMgr->setBlendState(blendState);
+    }
+
+    GLuint program = _shader->getProgram();
+
+    for(int i=0; i< renderItemList.length(); i++){
+        const MHWRender::MRenderItem *renderItem = renderItemList.itemAt(i);
+//        printf("draw: %d %s\n", i, renderItem->sourceDagPath().fullPathName().asChar());
+//        printf("# of indexBuffer = %d\n", renderItem->geometry()->indexBufferCount());
+
+//        const OsdPtexMeshData *data = (dynamic_cast(renderItem->customData());
+        OsdPtexMeshData *data = (OsdPtexMeshData*)(renderItem->customData());
+        if (data == NULL) {
+            return false;
+        }
+        
+        data->populateIfNeeded(_shader->getLevel(), _shader->getScheme());
+
+        const MHWRender::MVertexBuffer *position = NULL, *normal = NULL;
+        {
+            const MHWRender::MGeometry *geometry = renderItem->geometry();
+//            printf("Vertex buffer count = %d\n", geometry->vertexBufferCount());
+            for(int i = 0; i < geometry->vertexBufferCount(); i++){
+                const MHWRender::MVertexBuffer *vb = geometry->vertexBuffer(i);
+                const MHWRender::MVertexBufferDescriptor &vdesc = vb->descriptor();
+                if (vdesc.name() == "osdPosition")
+                    position = vb;
+                if (vdesc.name() == "osdNormal")
+                    normal = vb;
+//                printf("%d: %s, offset=%d, stride=%d\n", i, vdesc.name().asChar(), vdesc.offset(), vdesc.stride());
+            }
+        }
+
+        glUseProgram(program);
+        {
+            // shader uniform setting
+            GLint position = glGetUniformLocation(program, "lightSource[0].position");
+            GLint ambient = glGetUniformLocation(program, "lightSource[0].ambient");
+            GLint diffuse = glGetUniformLocation(program, "lightSource[0].diffuse");
+            GLint specular = glGetUniformLocation(program, "lightSource[0].specular");
+
+            glProgramUniform4f(program, position, 0, 0.2f, 1, 0);
+            glProgramUniform4f(program, ambient, 0.4f, 0.4f, 0.4f, 1.0f);
+            glProgramUniform4f(program, diffuse, 0.3f, 0.3f, 0.3f, 1.0f);
+            glProgramUniform4f(program, specular, 0.2f, 0.2f, 0.2f, 1.0f);
+        
+            GLint otcMatrix = glGetUniformLocation(program, "objectToClipMatrix");
+            GLint oteMatrix = glGetUniformLocation(program, "objectToEyeMatrix");
+            GLint etoMatrix = glGetUniformLocation(program, "eyeToObjectMatrix");
+
+            float modelview[4][4], mvp[4][4], modelviewI[4][4];
+            context.getMatrix(MHWRender::MDrawContext::kWorldViewMtx).get(modelview);
+            context.getMatrix(MHWRender::MDrawContext::kWorldViewProjMtx).get(mvp);
+            context.getMatrix(MHWRender::MDrawContext::kWorldViewInverseMtx).get(modelviewI);
+
+            glProgramUniformMatrix4fv(program, otcMatrix, 1, false, (float*)mvp);
+            glProgramUniformMatrix4fv(program, oteMatrix, 1, false, (float*)modelview);
+            glProgramUniformMatrix4fv(program, etoMatrix, 1, false, (float*)modelviewI);
+
+            GLint eye = glGetUniformLocation(program, "eyePositionInWorld");
+            MPoint e = MPoint(0,0,0) * context.getMatrix(MHWRender::MDrawContext::kWorldViewInverseMtx);
+            glProgramUniform3f(program, eye, e.x, e.y, e.z);
+        }
+
+        if (_shader->isWireframe()) {
+            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+        } else {
+            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+        }
+
+        _shader->bindTextures();
+
+//        CHECK_GL_ERROR("pre draw");
+        // draw meshdata
+        data->prepare();
+
+        data->updateGeometry(position, normal);
+
+        data->draw(program);
+
+//        CHECK_GL_ERROR("post draw");
+
+        glUseProgram(0);
+    }
+    return true;
+}
+// ------------------------------------------------------------------------------
+
+using namespace MHWRender;
+
+class OsdBufferGenerator : public MHWRender::MPxVertexBufferGenerator
+{
+public:
+    OsdBufferGenerator(bool normal) : _normal(normal) {}
+    virtual ~OsdBufferGenerator() {}
+
+    virtual bool getSourceIndexing(const MDagPath &dagPath, MHWRender::MComponentDataIndexing &sourceIndexing) const
+        {
+            MStatus status;
+            MFnMesh mesh(dagPath.node());
+            if (!status) return false;
+
+            MIntArray vertexCount, vertexList;
+            mesh.getVertices(vertexCount, vertexList);
+
+            MUintArray &vertices = sourceIndexing.indices();
+            for(unsigned int i = 0; i < vertexList.length(); ++i)
+                vertices.append((unsigned int)vertexList[i]);
+
+            sourceIndexing.setComponentType(MComponentDataIndexing::kFaceVertex); // .... although not facevertex
+
+            return true;
+        }
+    virtual bool getSourceStreams(const MDagPath &dagPath, MStringArray &) const
+        {
+            return false;
+        }
+#ifdef MAYA2013_PREVIEW
+    virtual void createVertexStream(const MDagPath &dagPath, MVertexBuffer &vertexBuffer,
+                                    const MComponentDataIndexing &targetIndexing, const MComponentDataIndexing &,
+                                    const MVertexBufferArray &) const 
+#else
+    virtual void createVertexStream(const MDagPath &dagPath, MVertexBuffer &vertexBuffer,
+                                    const MComponentDataIndexing &targetIndexing) const 
+#endif
+        {
+            const MVertexBufferDescriptor &desc = vertexBuffer.descriptor();
+
+            MFnMesh meshFn(dagPath);
+            int nVertices = meshFn.numVertices();
+            if (!_normal) {
+                MFloatPointArray points;
+                meshFn.getPoints(points);
+                
+#ifdef MAYA2013_PREVIEW
+                float *buffer = (float*)vertexBuffer.acquire(nVertices, true);
+#else
+                float *buffer = (float*)vertexBuffer.acquire(nVertices);
+#endif
+                float *dst = buffer;
+                for(int i=0; i < nVertices; ++i){
+                    *dst++ = points[i].x;
+                    *dst++ = points[i].y;
+                    *dst++ = points[i].z;
+                }
+                vertexBuffer.commit(buffer);
+            } else {
+                MFloatVectorArray normals;
+                meshFn.getVertexNormals(true, normals);
+
+#ifdef MAYA2013_PREVIEW
+                float *buffer = (float*)vertexBuffer.acquire(nVertices, true);
+#else
+                float *buffer = (float*)vertexBuffer.acquire(nVertices);
+#endif
+                float *dst = buffer;
+                for(int i=0; i < nVertices; ++i){
+                    *dst++ = normals[i].x;
+                    *dst++ = normals[i].y;
+                    *dst++ = normals[i].z;
+                }
+                vertexBuffer.commit(buffer);
+            }
+        }
+
+    static MPxVertexBufferGenerator *positionCreator()
+        {
+            return new OsdBufferGenerator(false);
+        }
+    static MPxVertexBufferGenerator *normalCreator()
+        {
+            return new OsdBufferGenerator(true);
+        }
+private:
+    bool _normal;
+};
+
+//---------------------------------------------------------------------------
+// Plugin Registration
+//---------------------------------------------------------------------------
+MStatus
+initializePlugin( MObject obj )
+{
+    MStatus status;
+    MFnPlugin plugin( obj, "Pixar", "3.0", "Any" );// vender,version,apiversion
+    
+    MString swatchName = MHWShaderSwatchGenerator::initialize();
+    MString userClassify("shader/surface/utility/:drawdb/shader/surface/OpenSubdivPtexShader:swatch/"+swatchName);
+
+    // shader node
+    status = plugin.registerNode( "openSubdivPtexShader",
+                                  OpenSubdivPtexShader::id, 
+                                  &OpenSubdivPtexShader::creator, 
+                                  &OpenSubdivPtexShader::initialize,
+                                  MPxNode::kHwShaderNode,
+                                  &userClassify);
+
+    MHWRender::MDrawRegistry::registerVertexBufferGenerator("osdPosition",
+                                                            OsdBufferGenerator::positionCreator);
+    MHWRender::MDrawRegistry::registerVertexBufferGenerator("osdNormal",
+                                                            OsdBufferGenerator::normalCreator);
+
+    // shaderoverride
+    status = MHWRender::MDrawRegistry::registerShaderOverrideCreator(
+        "drawdb/shader/surface/OpenSubdivPtexShader",
+        OpenSubdivPtexShader::drawRegistrantId,
+        OpenSubdivPtexShaderOverride::creator);
+
+    glewInit();
+    OpenSubdiv::OsdCpuKernelDispatcher::Register();
+    OpenSubdiv::OsdCudaKernelDispatcher::Register();
+    cudaInit();
+
+    OpenSubdiv::OsdPTexture::SetGutterWidth(1);
+    OpenSubdiv::OsdPTexture::SetPageMargin(8);
+    OpenSubdiv::OsdPTexture::SetGutterDebug(0);
+
+    return status;
+}
+
+MStatus
+uninitializePlugin( MObject obj )
+{
+    MFnPlugin plugin( obj );
+  
+    MStatus status;
+    status = plugin.deregisterNode(OpenSubdivPtexShader::id);
+
+    MHWRender::MDrawRegistry::deregisterVertexBufferGenerator("osdPosition");
+    MHWRender::MDrawRegistry::deregisterVertexBufferGenerator("osdNormal");
+
+    status = MHWRender::MDrawRegistry::deregisterShaderOverrideCreator(
+        "drawdb/shader/surface/OpenSubdivPtexShader",
+        OpenSubdivPtexShader::drawRegistrantId);
+
+    return status;
+}
diff --git a/examples/mayaPtexViewer_siggraph2012/cudaUtil.cpp b/examples/mayaPtexViewer_siggraph2012/cudaUtil.cpp
new file mode 100644
index 00000000..3bcc3b7d
--- /dev/null
+++ b/examples/mayaPtexViewer_siggraph2012/cudaUtil.cpp
@@ -0,0 +1,12 @@
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include "../common/cudaInit.h"
+
+void cudaInit()
+{
+    cudaGLSetGLDevice( cutGetMaxGflopsDeviceId() );
+}
diff --git a/examples/mayaPtexViewer_siggraph2012/hbrUtil.cpp b/examples/mayaPtexViewer_siggraph2012/hbrUtil.cpp
new file mode 100644
index 00000000..81a5bb2f
--- /dev/null
+++ b/examples/mayaPtexViewer_siggraph2012/hbrUtil.cpp
@@ -0,0 +1,197 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+#include "hbrUtil.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define OSD_ERROR printf  // XXXX
+
+OpenSubdiv::OsdHbrMesh * ConvertToHBR(int nVertices,
+                                      std::vector   const & numIndices,
+                                      std::vector   const & faceIndices,
+                                      std::vector   const & vtxCreaseIndices,
+                                      std::vector const & vtxCreases,
+                                      std::vector   const & edgeCrease1Indices, // face index, local edge index
+                                      std::vector const & edgeCreases1,
+                                      std::vector   const & edgeCrease2Indices, // 2 vertex indices (Maya friendly)
+                                      std::vector const & edgeCreases2,
+                                      int interpBoundary, int scheme)
+{
+    static OpenSubdiv::HbrBilinearSubdivision _bilinear;
+    static OpenSubdiv::HbrLoopSubdivision _loop;
+    static OpenSubdiv::HbrCatmarkSubdivision _catmark;
+    
+    OpenSubdiv::OsdHbrMesh *hbrMesh;
+    if (scheme == 0)
+        hbrMesh = new OpenSubdiv::OsdHbrMesh(&_catmark);
+    else if (scheme == 1)
+        hbrMesh = new OpenSubdiv::OsdHbrMesh(&_loop);
+    else 
+        hbrMesh = new OpenSubdiv::OsdHbrMesh(&_bilinear);
+
+    OpenSubdiv::OsdVertex v;
+    for(int i = 0; i < nVertices; ++i){
+        // create empty vertex : actual vertices will be initialized in UpdatePoints();
+        hbrMesh->NewVertex(i, v);
+    }
+
+    // get face indices
+    std::vector vIndex;
+    int nFaces = (int)numIndices.size(), offset = 0, ptxidx = 0;
+    for (int i = 0; i < nFaces; ++i) {
+        int numVertex = numIndices[i];
+        vIndex.resize(numVertex);
+
+        bool valid=true;
+        for (int j=0; jGetVertex( vIndex[j] );
+            OpenSubdiv::OsdHbrVertex * destination = hbrMesh->GetVertex( vNextIndex );
+            if (!origin || !destination) {
+                OSD_ERROR("ERROR : An edge was specified that connected a nonexistent vertex");
+                valid=false;
+            }
+
+            if (origin == destination) {
+                OSD_ERROR("ERROR : An edge was specified that connected a vertex to itself");
+                valid=false;
+            }
+
+            OpenSubdiv::OsdHbrHalfedge * opposite = destination->GetEdge(origin);
+            if (opposite && opposite->GetOpposite()) {
+                OSD_ERROR("ERROR : A non-manifold edge incident to more than 2 faces was found");
+                valid=false;
+            }
+
+            if (origin->GetEdge(destination)) {
+                OSD_ERROR("ERROR : An edge connecting two vertices was specified more than once. "
+                          "It's likely that an incident face was flipped");
+                valid=false;
+            }
+        }
+
+        if ( valid ) {
+            OpenSubdiv::OsdHbrFace *face = hbrMesh->NewFace(numVertex, &(vIndex[0]), 0);
+            face->SetPtexIndex(ptxidx);
+            ptxidx += (numVertex == 4) ? 1 : numVertex;
+        }
+        else 
+            OSD_ERROR("Face %d will be ignored\n", i);
+
+        offset += numVertex;
+    }
+
+    // XXX: use hbr enum or redefine same enum in gsd
+    hbrMesh->SetInterpolateBoundaryMethod((OpenSubdiv::OsdHbrMesh::InterpolateBoundaryMethod)interpBoundary);
+
+    // set edge crease in two different indexing way
+    int nEdgeCreases = (int)edgeCreases1.size();
+    for (int i = 0; i < nEdgeCreases; ++i) {
+        if( edgeCreases1[i] <= 0. ) 
+            continue;
+
+        OpenSubdiv::OsdHbrHalfedge * e = hbrMesh->GetFace(edgeCrease1Indices[i*2])->GetEdge(edgeCrease1Indices[i*2+1]);
+        if (!e) {
+            OSD_ERROR("Can't find edge (face %d edge %d)\n", edgeCrease1Indices[i*2], edgeCrease1Indices[i*2+1]);
+            continue;
+        }
+        e->SetSharpness( (float)edgeCreases1[i] );
+    }
+    nEdgeCreases = (int)edgeCreases2.size();
+    for (int i = 0; i < nEdgeCreases; ++i) {
+        if( edgeCreases1[i] <= 0. ) 
+            continue;
+
+        OpenSubdiv::OsdHbrVertex * v0 = hbrMesh->GetVertex(edgeCrease2Indices[i*2]);
+        OpenSubdiv::OsdHbrVertex * v1 = hbrMesh->GetVertex(edgeCrease2Indices[i*2+1]);
+        OpenSubdiv::OsdHbrHalfedge * e = NULL;
+
+        if ( v0 && v1 ) 
+            if ( ! (e = v0->GetEdge(v1)) )
+                e = v1->GetEdge(v0);
+        if (!e) {
+            OSD_ERROR("ERROR can't find edge");
+            continue;
+        }
+        e->SetSharpness( (float)edgeCreases2[i] );
+    }
+        
+    // set corner
+    {
+        int nVertexCreases = (int)vtxCreases.size();
+        for ( int i = 0; i< nVertexCreases; ++i ) {
+            if( vtxCreases[i] <= 0. ) 
+                continue;
+            OpenSubdiv::OsdHbrVertex * v = hbrMesh->GetVertex(vtxCreaseIndices[i]); 
+            if (!v) {
+                OSD_ERROR("Can't find vertex %d\n", vtxCreaseIndices[i]);
+                continue;
+            }
+            v->SetSharpness( (float)vtxCreases[i] );
+        }
+    }
+
+    hbrMesh->Finish();
+    return hbrMesh;
+}
+
diff --git a/examples/mayaPtexViewer_siggraph2012/hbrUtil.h b/examples/mayaPtexViewer_siggraph2012/hbrUtil.h
new file mode 100644
index 00000000..ffe75ff6
--- /dev/null
+++ b/examples/mayaPtexViewer_siggraph2012/hbrUtil.h
@@ -0,0 +1,74 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+#ifndef OSD_HBR_UTIL_H
+#define OSD_HBR_UTIL_H
+
+#include 
+#include 
+
+extern "C" OpenSubdiv::OsdHbrMesh * ConvertToHBR(int nVertices,
+                                                 std::vector   const & numIndices,
+                                                 std::vector   const & faceIndices,
+                                                 std::vector   const & vtxCreaseIndices,
+                                                 std::vector const & vtxCreases,
+                                                 std::vector   const & edgeCrease1Indices,
+                                                 std::vector const & edgeCreases1,
+                                                 std::vector   const & edgeCrease2Indices,
+                                                 std::vector const & edgeCreases2,
+                                                 int interpBoundary,
+                                                 int scheme);
+#endif
diff --git a/examples/mayaPtexViewer_siggraph2012/shader.glsl b/examples/mayaPtexViewer_siggraph2012/shader.glsl
new file mode 100644
index 00000000..af1d1afb
--- /dev/null
+++ b/examples/mayaPtexViewer_siggraph2012/shader.glsl
@@ -0,0 +1,294 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+
+#version 400
+
+//--------------------------------------------------------------
+// Common
+//--------------------------------------------------------------
+uniform int ptexLevel;
+uniform isamplerBuffer ptexIndices;
+
+vec4 PTexLookup(vec2 faceUV,
+                sampler2DArray data,
+                samplerBuffer packings,
+                isamplerBuffer pages)
+{
+    ivec2 ptexIndex = texelFetch(ptexIndices, gl_PrimitiveID).xy;
+    int faceID = abs(ptexIndex.x);
+    int u = ptexIndex.y >> 16;
+    int v = (ptexIndex.y & 0xffff);
+    int lv = ptexLevel;
+    if (ptexIndex.x < 0) lv >>= 1; // non-quad root face
+
+    int page = texelFetch(pages, faceID).x;
+    vec4 packing = texelFetch(packings, faceID);
+
+    vec2 uv = (faceUV * vec2(1.0)/lv) + vec2(u, v)/lv;
+
+    vec3 coords = vec3( packing.x + uv.x * packing.z,
+                        packing.y + uv.y * packing.w,
+                        page);
+
+    return texture(data, coords);
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec3 position;
+layout (location=1) in vec3 normal;
+
+out vec3 vPosition;
+out vec3 vNormal;
+
+void main()
+{
+    vPosition = position;
+    vNormal = normal;
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+layout(lines_adjacency) in;
+layout(triangle_strip, max_vertices = 4) out;
+
+#if USE_PTEX_DISPLACEMENT
+uniform sampler2DArray textureDisplace_Data;
+uniform samplerBuffer textureDisplace_Packing;
+uniform isamplerBuffer textureDisplace_Pages;
+#endif
+
+uniform mat4 objectToClipMatrix;
+uniform mat4 objectToEyeMatrix;
+
+in vec3 vPosition[4];
+in vec3 vNormal[4];
+
+flat out vec3 gFacetNormal;
+out vec3 Pobj;
+out vec3 Nobj;
+out vec2 gFaceUV;
+
+void main()
+{
+    gl_PrimitiveID = gl_PrimitiveIDIn;
+
+  vec3 pos[4];
+  vec2 teTextureCoord[4];
+  teTextureCoord[0] = vec2(0, 0);
+  teTextureCoord[1] = vec2(1, 0);
+  teTextureCoord[2] = vec2(1, 1);
+  teTextureCoord[3] = vec2(0, 1);
+  pos[0] = vPosition[0];
+  pos[1] = vPosition[1];
+  pos[2] = vPosition[2];
+  pos[3] = vPosition[3];
+
+
+#if USE_PTEX_DISPLACEMENT
+  for(int i=0; i< 4; i++){
+     vec4 displace = PTexLookup(teTextureCoord[i],
+                               textureDisplace_Data,
+                               textureDisplace_Packing,
+    textureDisplace_Pages);
+    
+     pos[i] += displace.x * vNormal[i];
+}
+#endif
+
+    vec3 A = pos[0] - pos[1];
+    vec3 B = pos[3] - pos[1];
+    vec3 C = pos[2] - pos[1];
+    gFacetNormal = normalize(cross(B, A));
+
+    Pobj = pos[0];
+    gl_Position = objectToClipMatrix * vec4(pos[0], 1);
+    Nobj = vNormal[0];
+    gFaceUV = teTextureCoord[0];
+    EmitVertex();
+
+    Pobj = pos[1];
+    gl_Position = objectToClipMatrix * vec4(pos[1], 1);
+    Nobj = vNormal[1];
+    gFaceUV = teTextureCoord[1];
+    EmitVertex();
+
+    Pobj = pos[3];
+    gl_Position = objectToClipMatrix * vec4(pos[3], 1);
+    Nobj = vNormal[3];
+    gFaceUV = teTextureCoord[3];
+    EmitVertex();
+
+    gFacetNormal = normalize(cross(C, B));
+
+    Pobj = pos[2];
+    gl_Position = objectToClipMatrix * vec4(pos[2], 1);
+    Nobj = vNormal[2];
+    gFaceUV = teTextureCoord[2];
+    EmitVertex();
+
+    EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+uniform int ptexFaceOffset;
+
+#if USE_PTEX_COLOR
+uniform sampler2DArray textureImage_Data;
+uniform samplerBuffer textureImage_Packing;
+uniform isamplerBuffer textureImage_Pages;
+#endif
+
+#if USE_PTEX_OCCLUSION
+uniform sampler2DArray textureOcclusion_Data;
+uniform samplerBuffer textureOcclusion_Packing;
+uniform isamplerBuffer textureOcclusion_Pages;
+#endif
+
+uniform sampler2D diffuseMap;
+uniform sampler2D environmentMap;
+
+flat in vec3 gFacetNormal;
+in vec3 Pobj;
+in vec3 Nobj;
+in vec2 gFaceUV;
+
+#define NUM_LIGHTS 1
+
+struct LightSource {
+    vec4 position;
+    vec4 ambient;
+    vec4 diffuse;
+    vec4 specular;
+};
+
+uniform LightSource lightSource[NUM_LIGHTS];
+
+uniform vec3 diffuse;
+uniform vec3 ambient;
+uniform vec3 specular; 
+uniform vec3 eyePositionInWorld;
+
+uniform float fresnelBias;
+uniform float fresnelScale;
+uniform float fresnelPower;
+
+vec4 getEnvironment(sampler2D sampler, vec3 dir)
+{
+    return texture(sampler, vec2((atan(dir.x,dir.z)/3.1415926+1)*0.5, (1-dir.y)*0.5));
+}
+
+void
+main()
+{
+#if USE_PTEX_COLOR
+    vec4 texColor = PTexLookup(gFaceUV,
+                               textureImage_Data,
+                               textureImage_Packing,
+                               textureImage_Pages);
+#else
+    vec4 texColor = vec4(1);
+#endif
+
+#if USE_PTEX_DISPLACEMENT
+    vec3 objN = (gl_FrontFacing ? gFacetNormal : -gFacetNormal);
+#else
+    vec3 objN = (gl_FrontFacing ? Nobj : -Nobj);
+#endif
+
+#if USE_PTEX_OCCLUSION
+    float occ = PTexLookup(gFaceUV,
+                           textureOcclusion_Data,
+                           textureOcclusion_Packing,
+                           textureOcclusion_Pages).x;
+#else
+    float occ = 0.0;
+#endif
+
+    vec4 a = vec4(ambient, 1);
+    vec4 d = getEnvironment(diffuseMap, objN) * 1.4;
+
+    vec3 eye = normalize(Pobj - eyePositionInWorld);
+    vec3 reflect = reflect(eye, objN);
+    vec4 s = getEnvironment(environmentMap, reflect);
+    float fresnel = fresnelBias + fresnelScale * pow(1.0+dot(objN,eye), fresnelPower);
+
+    a *= (1.0-occ);
+    d *= (1.0-occ)*vec4(diffuse, 1);
+    s *= (1.0-pow(occ, 0.2))*vec4(specular, 1) * fresnel;
+    
+    gl_FragColor = (a + d) * texColor + s;
+    gl_FragColor = pow(gl_FragColor, vec4(0.4545));
+}
+
+
+#endif
+
diff --git a/examples/mayaViewer/CMakeLists.txt b/examples/mayaViewer/CMakeLists.txt
index 1d4dea61..0de45963 100644
--- a/examples/mayaViewer/CMakeLists.txt
+++ b/examples/mayaViewer/CMakeLists.txt
@@ -137,5 +137,6 @@ target_link_libraries(maya_plugin
     ${MAYA_OpenMayaRender_LIBRARY}
     ${MAYA_tbb_LIBRARY}
     ${PLATFORM_LIBRARIES}
+    ${ILMBASE_LIBRARIES}
     ${GLEW_LIBRARY}
 )
diff --git a/examples/mayaViewer/OpenSubdivDrawOverride.cpp b/examples/mayaViewer/OpenSubdivDrawOverride.cpp
index cfa8affb..25870769 100644
--- a/examples/mayaViewer/OpenSubdivDrawOverride.cpp
+++ b/examples/mayaViewer/OpenSubdivDrawOverride.cpp
@@ -160,7 +160,7 @@ class OpenSubdivDrawOverride : public MHWRender::MPxDrawOverride
 public:
     static MHWRender::MPxDrawOverride* Creator(const MObject& obj) {
             return new OpenSubdivDrawOverride(obj);
-	}
+        }
 
     virtual ~OpenSubdivDrawOverride();
 
@@ -202,7 +202,7 @@ SubdivUserData::~SubdivUserData()
     glDeleteBuffers(1, &_index);
 }
 
-void 
+void
 SubdivUserData::Populate(MObject mesh)
 {
     MStatus s;
@@ -254,7 +254,7 @@ SubdivUserData::Populate(MObject mesh)
     }
 
     // XXX redundant copy... replace _vertexList with numIndices, etc
-    
+
     // create Osd mesh
     std::vector numIndices, faceIndices, edgeCreaseIndices, vtxCreaseIndices;
     std::vector edgeCreases, vtxCreases;
@@ -307,7 +307,7 @@ SubdivUserData::Populate(MObject mesh)
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _index);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*_numIndices,
                  &(indices[0]), GL_STATIC_DRAW);
-                                                          
+
     _cachedTotal = -1;
     UpdatePoints(mesh);
 }
@@ -546,8 +546,8 @@ OpenSubdivCommand::doIt(const MArgList &args)
 // Plugin Registration
 //---------------------------------------------------------------------------
 
-MString	drawDbClassification("drawdb/geometry/mesh");
-MString	drawRegistrantId("OpenSubdivDrawOverridePlugin");
+MString drawDbClassification("drawdb/geometry/mesh");
+MString drawRegistrantId("OpenSubdivDrawOverridePlugin");
 
 MStatus initializePlugin( MObject obj )
 {
diff --git a/examples/mayaViewer/hbrUtil.cpp b/examples/mayaViewer/hbrUtil.cpp
index 918646ac..c156e5a9 100644
--- a/examples/mayaViewer/hbrUtil.cpp
+++ b/examples/mayaViewer/hbrUtil.cpp
@@ -77,7 +77,7 @@ OpenSubdiv::OsdHbrMesh * ConvertToHBR(int nVertices,
     static OpenSubdiv::HbrBilinearSubdivision _bilinear;
     static OpenSubdiv::HbrLoopSubdivision _loop;
     static OpenSubdiv::HbrCatmarkSubdivision _catmark;
-    
+
     OpenSubdiv::OsdHbrMesh *hbrMesh;
     if (loop)
         hbrMesh = new OpenSubdiv::OsdHbrMesh(&_loop);
@@ -128,9 +128,9 @@ OpenSubdiv::OsdHbrMesh * ConvertToHBR(int nVertices,
             }
         }
 
-        if ( valid ) 
+        if ( valid )
             hbrMesh->NewFace(numVertex, &(vIndex[0]), 0);
-        else 
+        else
             OSD_ERROR("Face %d will be ignored\n", i);
 
         offset += numVertex;
@@ -142,7 +142,7 @@ OpenSubdiv::OsdHbrMesh * ConvertToHBR(int nVertices,
     // set edge crease in two different indexing way
     int nEdgeCreases = (int)edgeCreases1.size();
     for (int i = 0; i < nEdgeCreases; ++i) {
-        if( edgeCreases1[i] <= 0. ) 
+        if( edgeCreases1[i] <= 0. )
             continue;
 
         OpenSubdiv::OsdHbrHalfedge * e = hbrMesh->GetFace(edgeCrease1Indices[i*2])->GetEdge(edgeCrease1Indices[i*2+1]);
@@ -154,14 +154,14 @@ OpenSubdiv::OsdHbrMesh * ConvertToHBR(int nVertices,
     }
     nEdgeCreases = (int)edgeCreases2.size();
     for (int i = 0; i < nEdgeCreases; ++i) {
-        if( edgeCreases1[i] <= 0. ) 
+        if( edgeCreases1[i] <= 0. )
             continue;
 
         OpenSubdiv::OsdHbrVertex * v0 = hbrMesh->GetVertex(edgeCrease2Indices[i*2]);
         OpenSubdiv::OsdHbrVertex * v1 = hbrMesh->GetVertex(edgeCrease2Indices[i*2+1]);
         OpenSubdiv::OsdHbrHalfedge * e = NULL;
 
-        if ( v0 && v1 ) 
+        if ( v0 && v1 )
             if ( ! (e = v0->GetEdge(v1)) )
                 e = v1->GetEdge(v0);
         if (!e) {
@@ -170,14 +170,14 @@ OpenSubdiv::OsdHbrMesh * ConvertToHBR(int nVertices,
         }
         e->SetSharpness( (float)edgeCreases2[i] );
     }
-        
+
     // set corner
     {
         int nVertexCreases = (int)vtxCreases.size();
         for ( int i = 0; i< nVertexCreases; ++i ) {
-            if( vtxCreases[i] <= 0. ) 
+            if( vtxCreases[i] <= 0. )
                 continue;
-            OpenSubdiv::OsdHbrVertex * v = hbrMesh->GetVertex(vtxCreaseIndices[i]); 
+            OpenSubdiv::OsdHbrVertex * v = hbrMesh->GetVertex(vtxCreaseIndices[i]);
             if (!v) {
                 OSD_ERROR("Can't find vertex %d\n", vtxCreaseIndices[i]);
                 continue;
diff --git a/examples/ptexViewer/CMakeLists.txt b/examples/ptexViewer/CMakeLists.txt
new file mode 100644
index 00000000..b76e6116
--- /dev/null
+++ b/examples/ptexViewer/CMakeLists.txt
@@ -0,0 +1,114 @@
+#
+#     Copyright (C) Pixar. All rights reserved.
+#
+#     This license governs use of the accompanying software. If you
+#     use the software, you accept this license. If you do not accept
+#     the license, do not use the software.
+#
+#     1. Definitions
+#     The terms "reproduce," "reproduction," "derivative works," and
+#     "distribution" have the same meaning here as under U.S.
+#     copyright law.  A "contribution" is the original software, or
+#     any additions or changes to the software.
+#     A "contributor" is any person or entity that distributes its
+#     contribution under this license.
+#     "Licensed patents" are a contributor's patent claims that read
+#     directly on its contribution.
+#
+#     2. Grant of Rights
+#     (A) Copyright Grant- Subject to the terms of this license,
+#     including the license conditions and limitations in section 3,
+#     each contributor grants you a non-exclusive, worldwide,
+#     royalty-free copyright license to reproduce its contribution,
+#     prepare derivative works of its contribution, and distribute
+#     its contribution or any derivative works that you create.
+#     (B) Patent Grant- Subject to the terms of this license,
+#     including the license conditions and limitations in section 3,
+#     each contributor grants you a non-exclusive, worldwide,
+#     royalty-free license under its licensed patents to make, have
+#     made, use, sell, offer for sale, import, and/or otherwise
+#     dispose of its contribution in the software or derivative works
+#     of the contribution in the software.
+#
+#     3. Conditions and Limitations
+#     (A) No Trademark License- This license does not grant you
+#     rights to use any contributor's name, logo, or trademarks.
+#     (B) If you bring a patent claim against any contributor over
+#     patents that you claim are infringed by the software, your
+#     patent license from such contributor to the software ends
+#     automatically.
+#     (C) If you distribute any portion of the software, you must
+#     retain all copyright, patent, trademark, and attribution
+#     notices that are present in the software.
+#     (D) If you distribute any portion of the software in source
+#     code form, you may do so only under this license by including a
+#     complete copy of this license with your distribution. If you
+#     distribute any portion of the software in compiled or object
+#     code form, you may only do so under a license that complies
+#     with this license.
+#     (E) The software is licensed "as-is." You bear the risk of
+#     using it. The contributors give no express warranties,
+#     guarantees or conditions. You may have additional consumer
+#     rights under your local laws which this license cannot change.
+#     To the extent permitted under your local laws, the contributors
+#     exclude the implied warranties of merchantability, fitness for
+#     a particular purpose and non-infringement.
+#
+
+# *** ptexViewer ***
+
+set(SHADER_FILES
+     shader.glsl
+)
+
+set(PLATFORM_LIBRARIES
+    ${OSD_LINK_TARGET}
+    ${ILMBASE_LIBRARIES}
+    ${OPENGL_LIBRARY}
+    ${GLEW_LIBRARY}
+    ${GLUT_LIBRARIES}
+    ${PTEX_LIBRARY}
+)
+
+include_directories(
+    ${PROJECT_SOURCE_DIR}/opensubdiv
+    ${PROJECT_SOURCE_DIR}/regression
+    ${ILMBASE_INCLUDE_DIR}
+    ${GLEW_INCLUDE_DIR}
+    ${GLUT_INCLUDE_DIR}
+    ${PTEX_INCLUDE_DIR}
+)
+
+#-------------------------------------------------------------------------------
+# Shader Stringification
+# We want to use preprocessor include directives to include GLSL and OpenCL
+# shader source files in cpp files, but since the sources contain newline
+# characters we would need raw string literals from C++11 to do this directly.
+# To avoid depending on C++11 we instead use a small tool called "line_quote"
+# to generate source files that are suitable for direct inclusion.
+foreach(shader_file ${SHADER_FILES})
+
+    string(REGEX REPLACE ".*[.](.*)" "\\1" extension ${shader_file})
+
+    string(REGEX REPLACE "(.*)[.].*" "\\1.inc" inc_file ${shader_file})
+    list(APPEND INC_FILES ${inc_file})
+
+    add_custom_command(
+        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${inc_file}
+        COMMAND stringify ${CMAKE_CURRENT_SOURCE_DIR}/${shader_file}
+            ${CMAKE_CURRENT_SOURCE_DIR}/${inc_file}
+        DEPENDS stringify ${CMAKE_CURRENT_SOURCE_DIR}/${shader_file}
+    )
+endforeach()
+
+#-------------------------------------------------------------------------------
+_add_glut_executable(ptexViewer
+   viewer.cpp
+   ${SHADER_FILES}
+   ${INC_FILES}
+)
+
+target_link_libraries(ptexViewer
+    ${PLATFORM_LIBRARIES}
+)
+
diff --git a/examples/ptexViewer/shader.glsl b/examples/ptexViewer/shader.glsl
new file mode 100644
index 00000000..ff77ee70
--- /dev/null
+++ b/examples/ptexViewer/shader.glsl
@@ -0,0 +1,336 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+
+#version 400
+
+//--------------------------------------------------------------
+// Common
+//--------------------------------------------------------------
+uniform int ptexLevel;
+uniform isamplerBuffer ptexIndices;
+
+vec4 PTexLookup(vec2 faceUV,
+                sampler2DArray data,
+                samplerBuffer packings,
+                isamplerBuffer pages)
+{
+    ivec2 ptexIndex = texelFetch(ptexIndices, gl_PrimitiveID).xy;
+    int faceID = abs(ptexIndex.x);
+    int u = ptexIndex.y >> 16;
+    int v = (ptexIndex.y & 0xffff);
+    int lv = ptexLevel;
+    if (ptexIndex.x < 0) lv >>= 1; // non-quad root face
+
+    int page = texelFetch(pages, faceID).x;
+    vec4 packing = texelFetch(packings, faceID);
+
+    vec2 uv = (faceUV * vec2(1.0)/lv) + vec2(u, v)/lv;
+
+    vec3 coords = vec3( packing.x + uv.x * packing.z,
+                        packing.y + uv.y * packing.w,
+                        page);
+
+    return texture(data, coords);
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec3 position;
+layout (location=1) in vec3 normal;
+
+out vec3 vPosition;
+out vec3 vNormal;
+out vec4 vColor;
+
+void main()
+{
+    vPosition = position;
+    vNormal = normal;
+    vColor = vec4(1, 1, 1, 1);
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+layout(lines_adjacency) in;
+layout(triangle_strip, max_vertices = 4) out;
+
+#if USE_PTEX_DISPLACEMENT
+uniform sampler2DArray textureDisplace_Data;
+uniform samplerBuffer textureDisplace_Packing;
+uniform isamplerBuffer textureDisplace_Pages;
+#endif
+
+uniform mat4 objectToClipMatrix;
+uniform mat4 objectToEyeMatrix;
+
+in vec3 vPosition[4];
+in vec3 vNormal[4];
+
+flat out vec3 gFacetNormal;
+out vec3 Peye;
+out vec3 Neye;
+out vec4 Cout;
+out vec2 gFaceUV;
+
+void main()
+{
+  gl_PrimitiveID = gl_PrimitiveIDIn;
+
+  vec3 pos[4];
+  vec2 teTextureCoord[4];
+  teTextureCoord[0] = vec2(0, 0);
+  teTextureCoord[1] = vec2(1, 0);
+  teTextureCoord[2] = vec2(1, 1);
+  teTextureCoord[3] = vec2(0, 1);
+  pos[0] = vPosition[0];
+  pos[1] = vPosition[1];
+  pos[2] = vPosition[2];
+  pos[3] = vPosition[3];
+
+
+#if USE_PTEX_DISPLACEMENT
+  for(int i=0; i< 4; i++){
+     vec4 displace = PTexLookup(teTextureCoord[i],
+                               textureDisplace_Data,
+                               textureDisplace_Packing,
+    textureDisplace_Pages);
+    
+     pos[i] += displace.x * vNormal[i];
+}
+#endif
+
+    vec3 A = pos[0] - pos[1];
+    vec3 B = pos[3] - pos[1];
+    vec3 C = pos[2] - pos[1];
+    gFacetNormal = (objectToEyeMatrix*vec4(normalize(cross(B, A)), 0)).xyz;
+
+    Peye = pos[0];
+    gl_Position = objectToClipMatrix * vec4(pos[0], 1);
+    Neye = (objectToEyeMatrix * vec4(vNormal[0], 0)).xyz;
+    gFaceUV = teTextureCoord[0];
+    EmitVertex();
+
+    Peye = pos[1];
+    gl_Position = objectToClipMatrix * vec4(pos[1], 1);
+    Neye = (objectToEyeMatrix * vec4(vNormal[1], 0)).xyz;
+    gFaceUV = teTextureCoord[1];
+    EmitVertex();
+
+    Peye = pos[3];
+    gl_Position = objectToClipMatrix * vec4(pos[3], 1);
+    Neye = (objectToEyeMatrix * vec4(vNormal[3], 0)).xyz;
+    gFaceUV = teTextureCoord[3];
+    EmitVertex();
+
+    gFacetNormal = (objectToEyeMatrix*vec4(normalize(cross(C, B)), 0)).xyz;
+
+    Peye = pos[2];
+    gl_Position = objectToClipMatrix * vec4(pos[2], 1);
+    Neye = (objectToEyeMatrix * vec4(vNormal[2], 0)).xyz;
+    gFaceUV = teTextureCoord[2];
+    EmitVertex();
+
+    EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+uniform int ptexFaceOffset;
+
+#if USE_PTEX_COLOR
+uniform sampler2DArray textureImage_Data;
+uniform samplerBuffer textureImage_Packing;
+uniform isamplerBuffer textureImage_Pages;
+#endif
+
+#if USE_PTEX_OCCLUSION
+uniform sampler2DArray textureOcclusion_Data;
+uniform samplerBuffer textureOcclusion_Packing;
+uniform isamplerBuffer textureOcclusion_Pages;
+#endif
+
+flat in vec3 gFacetNormal;
+in vec3 Neye;
+in vec3 Peye;
+in vec4 Cout;
+in vec2 gFaceUV;
+
+#define NUM_LIGHTS 1
+
+struct LightSource {
+    vec4 position;
+    vec4 ambient;
+    vec4 diffuse;
+    vec4 specular;
+};
+
+uniform LightSource lightSource[NUM_LIGHTS];
+uniform bool useLighting = true;
+
+vec4
+lighting(vec3 Peye, vec3 Neye)
+{
+    vec4 color = vec4(0);
+
+    for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+        vec4 Plight = lightSource[i].position;
+
+        vec3 l = (Plight.w == 0.0)
+                    ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+        vec3 n = normalize(Neye);
+        vec3 h = normalize(l + vec3(0,0,1));    // directional viewer
+
+        float d = max(0.0, dot(n, l));
+        float s = pow(max(0.0, dot(n, h)), 8.0f);
+
+#if USE_PTEX_OCCLUSION
+        float occ = PTexLookup(gFaceUV,
+                               textureOcclusion_Data,
+                               textureOcclusion_Packing,
+                               textureOcclusion_Pages).x;
+#else
+        float occ = 0.0;
+#endif
+        color += (1.0-occ) * ((lightSource[i].ambient +
+                               d * lightSource[i].diffuse +
+                               s * lightSource[i].specular));
+    }
+
+    color.a = 1;
+
+    return color;
+}
+
+void
+main()
+{
+#if USE_PTEX_COLOR
+    vec4 texColor = PTexLookup(gFaceUV,
+                               textureImage_Data,
+                               textureImage_Packing,
+                               textureImage_Pages);
+#else
+    vec4 texColor = vec4(1);
+#endif
+    texColor = pow(texColor, vec4(0.4545));
+
+
+    if (useLighting) {
+#if USE_PTEX_DISPLACEMENT
+        vec3 N = (gl_FrontFacing ? gFacetNormal : -gFacetNormal);
+#else
+        vec3 N = (gl_FrontFacing ? Neye : -Neye);
+#endif
+
+        //gl_FragColor = lighting(Peye, N) * texColor * Cout;
+        gl_FragColor = lighting(Peye, N) * texColor;
+    } else {
+        gl_FragColor = texColor*Cout;
+    }
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Ptex debug vertex shader
+//--------------------------------------------------------------
+#ifdef PTEX_DEBUG_VERTEX_SHADER
+
+in vec3 position;
+out vec2 texCoord;
+
+void
+main()
+{
+    gl_Position = vec4(position, 1);
+    texCoord = position.xy;
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Ptex debug fragment shader
+//--------------------------------------------------------------
+#ifdef PTEX_DEBUG_FRAGMENT_SHADER
+
+uniform int ptexDebugPage;
+uniform sampler2DArray ptexDebugData;
+
+in vec2 texCoord;
+
+void
+main()
+{
+    gl_FragColor = texture(ptexDebugData, vec3(texCoord.x, texCoord.y, ptexDebugPage));
+}
+
+#endif
diff --git a/examples/ptexViewer/viewer.cpp b/examples/ptexViewer/viewer.cpp
new file mode 100644
index 00000000..5e84af34
--- /dev/null
+++ b/examples/ptexViewer/viewer.cpp
@@ -0,0 +1,999 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+
+#if defined(__APPLE__)
+    #include 
+#else
+    #include 
+    #include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "../common/stopwatch.h"
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+    #include 
+#endif
+
+#ifdef OPENSUBDIV_HAS_CUDA
+    #include 
+
+    #include 
+    #include 
+
+    #include "../common/cudaInit.h"
+#endif
+
+#include "Ptexture.h"
+#include "PtexUtils.h"
+
+static const char *shaderSource =
+#include "shader.inc"
+;
+
+#include 
+
+//------------------------------------------------------------------------------
+int   g_frame = 0,
+      g_repeatCount = 0;
+
+// GLUT GUI variables
+int   g_wire = 0,
+      g_drawNormals = 0,
+      g_mbutton[3] = {0, 0, 0},
+      g_level = 2,
+      g_kernel = OpenSubdiv::OsdKernelDispatcher::kCPU,
+      g_scheme = 0,
+      g_gutterWidth = 1,
+      g_ptexDebug = 0,
+      g_gutterDebug = 0;
+float g_moveScale = 1.0f;
+
+// ptex switch
+int   g_color = 1,
+      g_occlusion = 0,
+      g_displacement = 0;
+
+// camera
+float g_rotate[2] = {0, 0},
+      g_prev_x = 0,
+      g_prev_y = 0,
+      g_dolly = 5,
+      g_pan[2] = {0, 0},
+      g_center[3] = {0, 0, 0},
+      g_size = 0;
+
+// viewport
+int   g_width,
+      g_height;
+
+// performance
+float g_cpuTime = 0;
+float g_gpuTime = 0;
+Stopwatch g_fpsTimer;
+
+// geometry
+std::vector g_positions,
+                   g_normals;
+int g_numIndices = 0;
+
+GLuint g_indexBuffer;
+GLuint g_program = 0;
+GLuint g_debugProgram = 0;
+
+OpenSubdiv::OsdMesh * g_osdmesh = 0;
+OpenSubdiv::OsdVertexBuffer * g_vertexBuffer = 0;
+OpenSubdiv::OsdPTexture * g_osdPTexImage = 0;
+OpenSubdiv::OsdPTexture * g_osdPTexDisplacement = 0;
+OpenSubdiv::OsdPTexture * g_osdPTexOcclusion = 0;
+const char * g_ptexColorFile = 0;
+const char * g_ptexDisplacementFile = 0;
+const char * g_ptexOcclusionFile = 0;
+
+//------------------------------------------------------------------------------
+inline void
+cross(float *n, const float *p0, const float *p1, const float *p2) {
+
+    float a[3] = { p1[0]-p0[0], p1[1]-p0[1], p1[2]-p0[2] };
+    float b[3] = { p2[0]-p0[0], p2[1]-p0[1], p2[2]-p0[2] };
+    n[0] = a[1]*b[2]-a[2]*b[1];
+    n[1] = a[2]*b[0]-a[0]*b[2];
+    n[2] = a[0]*b[1]-a[1]*b[0];
+
+    float rn = 1.0f/sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
+    n[0] *= rn;
+    n[1] *= rn;
+    n[2] *= rn;
+}
+
+//------------------------------------------------------------------------------
+inline void
+normalize(float * p) {
+
+    float dist = sqrtf( p[0]*p[0] + p[1]*p[1]  + p[2]*p[2] );
+    p[0]/=dist;
+    p[1]/=dist;
+    p[2]/=dist;
+}
+
+//------------------------------------------------------------------------------
+inline void
+multMatrix(float *d, const float *a, const float *b) {
+
+    for (int i=0; i<4; ++i)
+    {
+        for (int j=0; j<4; ++j)
+        {
+            d[i*4 + j] =
+                a[i*4 + 0] * b[0*4 + j] +
+                a[i*4 + 1] * b[1*4 + j] +
+                a[i*4 + 2] * b[2*4 + j] +
+                a[i*4 + 3] * b[3*4 + j];
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+static void
+calcNormals(OpenSubdiv::OsdHbrMesh * mesh, std::vector const & pos, std::vector & result ) {
+
+    // calc normal vectors
+    int nverts = (int)pos.size()/3;
+
+    int nfaces = mesh->GetNumCoarseFaces();
+    for (int i = 0; i < nfaces; ++i) {
+
+        OpenSubdiv::OsdHbrFace * f = mesh->GetFace(i);
+
+        float const * p0 = &pos[f->GetVertex(0)->GetID()*3],
+                    * p1 = &pos[f->GetVertex(1)->GetID()*3],
+                    * p2 = &pos[f->GetVertex(2)->GetID()*3];
+
+        float n[3];
+        cross( n, p0, p1, p2 );
+
+        for (int j = 0; j < f->GetNumVertices(); j++) {
+            int idx = f->GetVertex(j)->GetID() * 3;
+            result[idx  ] += n[0];
+            result[idx+1] += n[1];
+            result[idx+2] += n[2];
+        }
+    }
+    for (int i = 0; i < nverts; ++i)
+        normalize( &result[i*3] );
+}
+
+//------------------------------------------------------------------------------
+void
+updateGeom() {
+
+    int nverts = (int)g_positions.size() / 3;
+
+    std::vector vertex;
+    vertex.reserve(nverts*6);
+
+    const float *p = &g_positions[0];
+    const float *n = &g_normals[0];
+
+    for (int i = 0; i < nverts; ++i) {
+        float move = g_size*0.005f*cosf(p[0]*100/g_size+g_frame*0.01f);
+        vertex.push_back(p[0]);
+        vertex.push_back(p[1]+g_moveScale*move);
+        vertex.push_back(p[2]);
+        vertex.push_back(n[0]);
+        vertex.push_back(n[1]);
+        vertex.push_back(n[2]);
+        p += 3;
+        n += 3;
+    }
+
+    if (!g_vertexBuffer)
+        g_vertexBuffer = g_osdmesh->InitializeVertexBuffer(6);
+    g_vertexBuffer->UpdateData(&vertex[0], nverts);
+
+    Stopwatch s;
+    s.Start();
+
+    g_osdmesh->Subdivide(g_vertexBuffer, NULL);
+
+    s.Stop();
+    g_cpuTime = float(s.GetElapsed() * 1000.0f);
+    s.Start();
+    g_osdmesh->Synchronize();
+    s.Stop();
+    g_gpuTime = float(s.GetElapsed() * 1000.0f);
+
+    glBindBuffer(GL_ARRAY_BUFFER, g_vertexBuffer->GetGpuBuffer());
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+//-------------------------------------------------------------------------------
+void
+fitFrame() {
+
+    g_pan[0] = g_pan[1] = 0;
+    g_dolly = g_size;
+}
+
+//-------------------------------------------------------------------------------
+template 
+OpenSubdiv::HbrMesh * createPTexGeo(PtexTexture * r)
+{
+  PtexMetaData* meta = r->getMetaData();
+  if(meta->numKeys()<3) return NULL;
+
+  const float* vp;
+  const int *vi, *vc;
+  int nvp, nvi, nvc;
+
+  meta->getValue("PtexFaceVertCounts", vc, nvc);
+  if (nvc==0)
+      return NULL;
+
+  meta->getValue("PtexVertPositions", vp, nvp);
+  if (nvp==0)
+      return NULL;
+
+  meta->getValue("PtexFaceVertIndices", vi, nvi);
+  if (nvi==0)
+      return NULL;
+
+  static OpenSubdiv::HbrCatmarkSubdivision  _catmark;
+  static OpenSubdiv::HbrBilinearSubdivision  _bilinear;
+  OpenSubdiv::HbrMesh * mesh;
+  if(g_scheme == 0)
+      mesh = new OpenSubdiv::HbrMesh(&_catmark);
+  else
+      mesh = new OpenSubdiv::HbrMesh(&_bilinear);
+
+  g_positions.clear();
+  g_positions.reserve(nvp);
+
+  // compute model bounding
+  float min[3] = {vp[0], vp[1], vp[2]};
+  float max[3] = {vp[0], vp[1], vp[2]};
+  for (int i=0; iNewVertex(i, T());
+  }
+  for (int j=0; j<3; ++j) {
+      g_center[j] = (min[j] + max[j]) * 0.5f;
+      g_size += (max[j]-min[j])*(max[j]-min[j]);
+  }
+  g_size = sqrtf(g_size);
+
+  const int *fv = vi;
+  for (int i=0, ptxidx=0; i * face = mesh->NewFace(nv, (int *)fv, 0);
+
+      face->SetPtexIndex(ptxidx);
+      if(nv != 4)
+          ptxidx+=nv;
+      else
+          ptxidx++;
+
+      fv += nv;
+  }
+  mesh->SetInterpolateBoundaryMethod( OpenSubdiv::HbrMesh::k_InterpolateBoundaryEdgeOnly );
+//  set creases here
+//  applyTags( mesh, sh );
+  mesh->Finish();
+
+  return mesh;
+}
+
+
+//------------------------------------------------------------------------------
+void
+reshape(int width, int height) {
+
+    g_width = width;
+    g_height = height;
+}
+
+#if _MSC_VER
+    #define snprintf _snprintf
+#endif
+
+#define drawFmtString(x, y, fmt, ...)         \
+    { char line[1024]; \
+      snprintf(line, 1024, fmt, __VA_ARGS__); \
+      const char *p = line; \
+      glWindowPos2f(x, y); \
+      while(*p) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *p++); } }
+
+#define drawString(x, y, str)                 \
+    { const char *p = str; \
+      glWindowPos2f(x, y); \
+      while(*p) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *p++); } }
+
+//------------------------------------------------------------------------------
+const char *getKernelName(int kernel) {
+
+         if (kernel == OpenSubdiv::OsdKernelDispatcher::kCPU)
+        return "CPU";
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kOPENMP)
+        return "OpenMP";
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kCUDA)
+        return "Cuda";
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kGLSL)
+        return "GLSL";
+    else if (kernel == OpenSubdiv::OsdKernelDispatcher::kCL)
+        return "OpenCL";
+    return "Unknown";
+}
+
+//------------------------------------------------------------------------------
+static GLuint compileShader(GLenum shaderType, const char *section)
+{
+    const char *sources[2];
+    char define[1024];
+    sprintf(define,
+            "#define %s\n"
+            "#define USE_PTEX_COLOR %d\n"
+            "#define USE_PTEX_OCCLUSION %d\n"
+            "#define USE_PTEX_DISPLACEMENT %d\n",
+            section, g_color, g_occlusion, g_displacement);
+
+    sources[0] = define;
+    sources[1] = shaderSource;
+
+    GLuint shader = glCreateShader(shaderType);
+    glShaderSource(shader, 2, sources, NULL);
+    glCompileShader(shader);
+
+    GLint status;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+    if( status == GL_FALSE ) {
+        GLchar emsg[1024];
+        glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg);
+        fprintf(stderr, "Error compiling GLSL shader (%s): %s\n", section, emsg );
+        exit(0);
+    }
+
+    return shader;
+}
+
+void bindPTexture(OpenSubdiv::OsdPTexture *osdPTex, GLuint data, GLuint packing, GLuint pages, int samplerUnit)
+{
+    glProgramUniform1i(g_program, data, samplerUnit + 0);
+    glActiveTexture(GL_TEXTURE0 + samplerUnit + 0);
+    glBindTexture(GL_TEXTURE_2D_ARRAY, osdPTex->GetTexelsTexture());
+
+    glProgramUniform1i(g_program, packing, samplerUnit + 1);
+    glActiveTexture(GL_TEXTURE0 + samplerUnit + 1);
+    glBindTexture(GL_TEXTURE_BUFFER, osdPTex->GetLayoutTextureBuffer());
+
+    glProgramUniform1i(g_program, pages, samplerUnit + 2);
+    glActiveTexture(GL_TEXTURE0 + samplerUnit + 2);
+    glBindTexture(GL_TEXTURE_BUFFER, osdPTex->GetPagesTextureBuffer());
+
+    glActiveTexture(GL_TEXTURE0);
+}
+
+void linkDebugProgram() {
+
+    if (g_debugProgram)
+        glDeleteProgram(g_debugProgram);
+
+    GLuint vertexShader = compileShader(GL_VERTEX_SHADER,
+                                          "PTEX_DEBUG_VERTEX_SHADER");
+    GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER,
+                                          "PTEX_DEBUG_FRAGMENT_SHADER");
+
+    g_debugProgram = glCreateProgram();
+    glAttachShader(g_debugProgram, vertexShader);
+    glAttachShader(g_debugProgram, fragmentShader);
+    glLinkProgram(g_debugProgram);
+    glDeleteShader(fragmentShader);
+
+    GLint status;
+    glGetProgramiv(g_debugProgram, GL_LINK_STATUS, &status );
+    if( status == GL_FALSE ) {
+        GLchar emsg[1024];
+        glGetProgramInfoLog(g_debugProgram, sizeof(emsg), 0, emsg);
+        fprintf(stderr, "Error linking GLSL program : %s\n", emsg );
+        exit(0);
+    }
+
+    GLint texData = glGetUniformLocation(g_debugProgram, "ptexDebugData");
+    glProgramUniform1i(g_debugProgram, texData, 0);
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D_ARRAY, g_osdPTexImage->GetTexelsTexture());
+}
+
+void linkProgram() {
+
+    if (g_program)
+        glDeleteProgram(g_program);
+
+    GLuint vertexShader         = compileShader(GL_VERTEX_SHADER,
+                                                "VERTEX_SHADER");
+    GLuint geometryShader       = compileShader(GL_GEOMETRY_SHADER,
+                                                "GEOMETRY_SHADER");
+    GLuint fragmentShader       = compileShader(GL_FRAGMENT_SHADER,
+                                                "FRAGMENT_SHADER");
+
+    g_program = glCreateProgram();
+    glAttachShader(g_program, vertexShader);
+    glAttachShader(g_program, geometryShader);
+    glAttachShader(g_program, fragmentShader);
+
+    glBindAttribLocation(g_program, 0, "position");
+    glBindAttribLocation(g_program, 1, "normal");
+
+    glLinkProgram(g_program);
+
+    glDeleteShader(vertexShader);
+    glDeleteShader(geometryShader);
+    glDeleteShader(fragmentShader);
+
+    GLint status;
+    glGetProgramiv(g_program, GL_LINK_STATUS, &status );
+    if( status == GL_FALSE ) {
+        GLchar emsg[1024];
+        glGetProgramInfoLog(g_program, sizeof(emsg), 0, emsg);
+        fprintf(stderr, "Error linking GLSL program : %s\n", emsg );
+        exit(0);
+    }
+
+    // bind ptexture
+    GLint texIndices = glGetUniformLocation(g_program, "ptexIndices");
+    GLint ptexLevel = glGetUniformLocation(g_program, "ptexLevel");
+
+    glProgramUniform1i(g_program, ptexLevel, 1<GetPtexCoordinatesTextureBuffer(g_level));
+
+    // color ptex
+    GLint texData = glGetUniformLocation(g_program, "textureImage_Data");
+    GLint texPacking = glGetUniformLocation(g_program, "textureImage_Packing");
+    GLint texPages = glGetUniformLocation(g_program, "textureImage_Pages");
+    bindPTexture(g_osdPTexImage, texData, texPacking, texPages, 1);
+
+    // displacement ptex
+    if (g_displacement) {
+        texData = glGetUniformLocation(g_program, "textureDisplace_Data");
+        texPacking = glGetUniformLocation(g_program, "textureDisplace_Packing");
+        texPages = glGetUniformLocation(g_program, "textureDisplace_Pages");
+        bindPTexture(g_osdPTexDisplacement, texData, texPacking, texPages, 4);
+    }
+
+    // occlusion ptex
+    if (g_occlusion) {
+        texData = glGetUniformLocation(g_program, "textureOcclusion_Data");
+        texPacking = glGetUniformLocation(g_program, "textureOcclusion_Packing");
+        texPages = glGetUniformLocation(g_program, "textureOcclusion_Pages");
+        bindPTexture(g_osdPTexOcclusion, texData, texPacking, texPages, 7);
+    }
+
+}
+
+//------------------------------------------------------------------------------
+void
+createOsdMesh(int level, int kernel) {
+
+    Ptex::String ptexError;
+    PtexTexture *ptexColor = PtexTexture::open(g_ptexColorFile, ptexError, true);
+
+    // generate Hbr representation from ptex
+    OpenSubdiv::OsdHbrMesh * hmesh = createPTexGeo(ptexColor);
+    if(hmesh == NULL) return;
+
+    g_normals.resize(g_positions.size(),0.0f);
+    calcNormals( hmesh, g_positions, g_normals );
+
+    // generate Osd mesh from Hbr mesh
+    if (g_osdmesh) delete g_osdmesh;
+    g_osdmesh = new OpenSubdiv::OsdMesh();
+    g_osdmesh->Create(hmesh, level, kernel);
+    if (g_vertexBuffer) {
+        delete g_vertexBuffer;
+        g_vertexBuffer = NULL;
+    }
+
+    // Hbr mesh can be deleted
+    delete hmesh;
+
+    // update element array buffer
+    const std::vector &indices = g_osdmesh->GetFarMesh()->GetFaceVertices(level);
+
+    // generate oOsdPTexture
+    if (g_osdPTexDisplacement) delete g_osdPTexDisplacement;
+    if (g_osdPTexOcclusion) delete g_osdPTexOcclusion;
+    g_osdPTexDisplacement = NULL;
+    g_osdPTexOcclusion = NULL;
+
+    OpenSubdiv::OsdPTexture::SetGutterWidth(g_gutterWidth);
+    OpenSubdiv::OsdPTexture::SetPageMargin(g_gutterWidth*8);
+    OpenSubdiv::OsdPTexture::SetGutterDebug(g_gutterDebug);
+
+    if (g_osdPTexImage) delete g_osdPTexImage;
+    g_osdPTexImage = OpenSubdiv::OsdPTexture::Create(ptexColor, 0 /*targetmemory*/);
+    ptexColor->release();
+
+    if (g_ptexDisplacementFile) {
+        PtexTexture *ptexDisplacement = PtexTexture::open(g_ptexDisplacementFile, ptexError, true);
+        g_osdPTexDisplacement = OpenSubdiv::OsdPTexture::Create(ptexDisplacement, 0);
+        ptexDisplacement->release();
+    }
+    if (g_ptexOcclusionFile) {
+        PtexTexture *ptexOcclusion = PtexTexture::open(g_ptexOcclusionFile, ptexError, true);
+        g_osdPTexOcclusion = OpenSubdiv::OsdPTexture::Create(ptexOcclusion, 0);
+        ptexOcclusion->release();
+    }
+
+    // bind index buffer
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indexBuffer);
+
+    g_numIndices = indices.size();
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*g_numIndices, &(indices[0]), GL_STATIC_DRAW);
+
+    updateGeom();
+
+    linkProgram();
+    linkDebugProgram();
+}
+
+//------------------------------------------------------------------------------
+void
+drawNormals() {
+
+    float * data=0;
+    int datasize = g_osdmesh->GetTotalVertices() * g_vertexBuffer->GetNumElements();
+
+    data = new float[datasize];
+
+    glBindBuffer(GL_ARRAY_BUFFER, g_vertexBuffer->GetGpuBuffer());
+    glGetBufferSubData(GL_ARRAY_BUFFER,0,datasize*sizeof(float),data);
+
+    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+    glDisable(GL_LIGHTING);
+    glColor3f(0.0f, 0.0f, 0.5f);
+    glBegin(GL_LINES);
+
+    int start = g_osdmesh->GetFarMesh()->GetSubdivision()->GetFirstVertexOffset(g_level) *
+                g_vertexBuffer->GetNumElements();
+
+    for (int i=start; iGetGpuBuffer();
+    glEnableVertexAttribArray(0);
+    glEnableVertexAttribArray(1);
+    glBindBuffer(GL_ARRAY_BUFFER, bVertex);
+
+    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 6, 0);
+    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 6, (float*)12);
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indexBuffer);
+
+    glPolygonMode(GL_FRONT_AND_BACK, g_wire==0 ? GL_LINE : GL_FILL);
+
+//    glPatchParameteri(GL_PATCH_VERTICES, 4);
+//    glDrawElements(GL_PATCHES, g_numIndices, GL_UNSIGNED_INT, 0);
+    glDrawElements(GL_LINES_ADJACENCY, g_numIndices, GL_UNSIGNED_INT, 0);
+
+    glUseProgram(0);
+
+    if (g_drawNormals)
+        drawNormals();
+
+    glDisableVertexAttribArray(0);
+    glDisableVertexAttribArray(1);
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+    if (g_ptexDebug)
+        drawPtexLayout(g_ptexDebug-1);
+
+    glColor3f(1, 1, 1);
+    drawFmtString(10, 10, "LEVEL = %d", g_level);
+    drawFmtString(10, 30, "# of Vertices = %d", g_osdmesh->GetFarMesh()->GetNumVertices());
+    drawFmtString(10, 50, "KERNEL = %s", getKernelName(g_kernel));
+    drawFmtString(10, 70, "CPU TIME = %.3f ms", g_cpuTime);
+    drawFmtString(10, 90, "GPU TIME = %.3f ms", g_gpuTime);
+    g_fpsTimer.Stop();
+    drawFmtString(10, 110, "FPS = %3.1f", 1.0/g_fpsTimer.GetElapsed());
+    g_fpsTimer.Start();
+    drawFmtString(10, 130, "SUBDIVISION = %s", g_scheme==0 ? "CATMARK" : "BILINEAR");
+
+    drawString(10, g_height-10, "a:   ambient occlusion on/off");
+    drawString(10, g_height-30, "c:   color on/off");
+    drawString(10, g_height-50, "d:   displacement on/off");
+    drawString(10, g_height-70, "e:   show normal vector");
+    drawString(10, g_height-90, "f:   fit frame");
+    drawString(10, g_height-110, "w:   toggle wireframe");
+    drawString(10, g_height-130, "m:   toggle vertex moving");
+    drawString(10, g_height-150, "s:   bilinear / catmark");
+    drawString(10, g_height-170, "1-7: subdivision level");
+
+
+    glFinish();
+    glutSwapBuffers();
+}
+
+//------------------------------------------------------------------------------
+void mouse(int button, int state, int x, int y) {
+
+    g_prev_x = float(x);
+    g_prev_y = float(y);
+    g_mbutton[button] = !state;
+}
+
+//------------------------------------------------------------------------------
+void motion(int x, int y) {
+
+    if (g_mbutton[0] && !g_mbutton[1] && !g_mbutton[2]) {
+        // orbit
+        g_rotate[0] += x - g_prev_x;
+        g_rotate[1] += y - g_prev_y;
+    } else if (!g_mbutton[0] && g_mbutton[1] && !g_mbutton[2]) {
+        // pan
+        g_pan[0] -= g_dolly*(x - g_prev_x)/g_width;
+        g_pan[1] += g_dolly*(y - g_prev_y)/g_height;
+    } else if (g_mbutton[0] && g_mbutton[1] && !g_mbutton[2]) {
+        // dolly
+        g_dolly -= g_dolly*0.01f*(x - g_prev_x);
+        if(g_dolly <= 0.01) g_dolly = 0.01f;
+    }
+
+    g_prev_x = float(x);
+    g_prev_y = float(y);
+}
+
+//------------------------------------------------------------------------------
+void quit() {
+
+    if(g_osdmesh)
+        delete g_osdmesh;
+
+    if (g_vertexBuffer)
+        delete g_vertexBuffer;
+
+#ifdef OPENSUBDIV_HAS_CUDA
+    cudaDeviceReset();
+#endif
+    exit(0);
+}
+
+//------------------------------------------------------------------------------
+void kernelMenu(int k) {
+
+    g_kernel = k;
+    createOsdMesh(g_level, g_kernel);
+}
+
+//------------------------------------------------------------------------------
+void
+levelMenu(int l) {
+
+    g_level = l;
+    createOsdMesh(g_level, g_kernel);
+}
+
+//------------------------------------------------------------------------------
+void
+schemeMenu(int s) {
+
+    g_scheme = s;
+    createOsdMesh(g_level, g_kernel);
+}
+
+//------------------------------------------------------------------------------
+void
+menu(int m) {
+
+    // top menu
+}
+
+//------------------------------------------------------------------------------
+void
+keyboard(unsigned char key, int x, int y) {
+
+    switch (key) {
+        case 'q': quit();
+        case 'w': g_wire = (g_wire+1)%2; break;
+        case 'e': g_drawNormals = (g_drawNormals+1)%2; break;
+        case 'f': fitFrame(); break;
+        case 'a': if (g_osdPTexOcclusion) g_occlusion = !g_occlusion; linkProgram(); break;
+        case 'd': if (g_osdPTexDisplacement) g_displacement = !g_displacement; linkProgram();break;
+        case 'c': g_color = !g_color; linkProgram(); break;
+        case 's': schemeMenu(!g_scheme); break;
+        case 'm': g_moveScale = 1.0f - g_moveScale; break;
+        case 'p': g_ptexDebug++; break;
+        case 'o': g_ptexDebug = std::max(0, g_ptexDebug-1); break;
+        case 'g': g_gutterWidth = (g_gutterWidth+1)%8; createOsdMesh(g_level, g_kernel); break;
+        case 'h': g_gutterDebug = !g_gutterDebug; createOsdMesh(g_level, g_kernel); break;
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7': levelMenu(key-'0'); break;
+    }
+}
+
+//------------------------------------------------------------------------------
+void
+idle() {
+
+    g_frame++;
+    updateGeom();
+    glutPostRedisplay();
+
+    if(g_repeatCount != 0 && g_frame >= g_repeatCount)
+        quit();
+}
+
+//------------------------------------------------------------------------------
+void
+initGL() {
+
+    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
+    glEnable(GL_LIGHT0);
+    glColor3f(1, 1, 1);
+    glEnable(GL_DEPTH_TEST);
+    glDepthFunc(GL_LEQUAL);
+
+    GLfloat color[4] = {1, 1, 1, 1};
+    GLfloat position[4] = {5, 5, 10, 1};
+    GLfloat ambient[4] = {0.9f, 0.9f, 0.9f, 1.0f};
+    GLfloat diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+    GLfloat shininess = 25.0;
+
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &shininess);
+    glLightfv(GL_LIGHT0, GL_POSITION, position);
+    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
+    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
+}
+
+//------------------------------------------------------------------------------
+int main(int argc, char ** argv) {
+
+    glutInit(&argc, argv);
+
+    glutInitDisplayMode(GLUT_RGBA |GLUT_DOUBLE | GLUT_DEPTH);
+    glutInitWindowSize(1024, 1024);
+    glutCreateWindow("OpenSubdiv ptexViewer");
+
+    int lmenu = glutCreateMenu(levelMenu);
+    for(int i = 1; i < 8; ++i){
+        char level[16];
+        sprintf(level, "Level %d\n", i);
+        glutAddMenuEntry(level, i);
+    }
+    int smenu = glutCreateMenu(schemeMenu);
+    glutAddMenuEntry("Catmark", 0);
+    glutAddMenuEntry("Bilinear", 1);
+
+    // Register Osd compute kernels
+    OpenSubdiv::OsdCpuKernelDispatcher::Register();
+    OpenSubdiv::OsdGlslKernelDispatcher::Register();
+
+#if OPENSUBDIV_HAS_OPENCL
+    OpenSubdiv::OsdClKernelDispatcher::Register();
+#endif
+
+#if OPENSUBDIV_HAS_CUDA
+    OpenSubdiv::OsdCudaKernelDispatcher::Register();
+
+    // Note: This function randomly crashes with linux 5.0-dev driver.
+    // cudaGetDeviceProperties overrun stack..?
+    cudaGLSetGLDevice( cutGetMaxGflopsDeviceId() );
+#endif
+
+    int kmenu = glutCreateMenu(kernelMenu);
+    int nKernels = OpenSubdiv::OsdKernelDispatcher::kMAX;
+
+    for(int i = 0; i < nKernels; ++i)
+        if(OpenSubdiv::OsdKernelDispatcher::HasKernelType(
+               OpenSubdiv::OsdKernelDispatcher::KernelType(i)))
+            glutAddMenuEntry(getKernelName(i), i);
+
+    glutCreateMenu(menu);
+    glutAddSubMenu("Level", lmenu);
+    glutAddSubMenu("Scheme", smenu);
+    glutAddSubMenu("Kernel", kmenu);
+    glutAttachMenu(GLUT_RIGHT_BUTTON);
+
+    glutDisplayFunc(display);
+    glutReshapeFunc(reshape);
+    glutMouseFunc(mouse);
+    glutKeyboardFunc(keyboard);
+    glutMotionFunc(motion);
+    glewInit();
+    initGL();
+
+    for (int i = 1; i < argc; ++i) {
+        if (!strcmp(argv[i], "-d"))
+            g_level = atoi(argv[++i]);
+        else if (!strcmp(argv[i], "-c"))
+            g_repeatCount = atoi(argv[++i]);
+        else if (g_ptexColorFile == NULL)
+            g_ptexColorFile = argv[i];
+        else if (g_ptexDisplacementFile == NULL)
+            g_ptexDisplacementFile = argv[i];
+        else if (g_ptexOcclusionFile == NULL)
+            g_ptexOcclusionFile = argv[i];
+
+    }
+
+    if (g_ptexColorFile == NULL) {
+        printf("Usage: %s  [] [] \n", argv[0]);
+        return 1;
+    }
+
+    glGenBuffers(1, &g_indexBuffer);
+
+    createOsdMesh(g_level, g_kernel);
+
+    fitFrame();
+
+    glutIdleFunc(idle);
+    glutMainLoop();
+
+    quit();
+}
+
+//------------------------------------------------------------------------------
diff --git a/opensubdiv/CMakeLists.txt b/opensubdiv/CMakeLists.txt
index 42ac0b89..a79f4d76 100644
--- a/opensubdiv/CMakeLists.txt
+++ b/opensubdiv/CMakeLists.txt
@@ -65,4 +65,4 @@ add_subdirectory(osd)
 
 install( FILES version.h
          DESTINATION include/
-         PERMISSIONS OWNER_READ GROUP_READ WORLD_READ ) 
+         PERMISSIONS OWNER_READ GROUP_READ WORLD_READ )
diff --git a/opensubdiv/far/CMakeLists.txt b/opensubdiv/far/CMakeLists.txt
index b6940862..84270fe6 100644
--- a/opensubdiv/far/CMakeLists.txt
+++ b/opensubdiv/far/CMakeLists.txt
@@ -63,8 +63,10 @@ set(H_FILES
     meshFactory.h
     mesh.h
     subdivisionTables.h
+    table.h
+    vertexEditTables.h
 )    
 
 install( FILES ${H_FILES}
          DESTINATION include/far
-         PERMISSIONS OWNER_READ GROUP_READ WORLD_READ ) 
+         PERMISSIONS OWNER_READ GROUP_READ WORLD_READ )
diff --git a/opensubdiv/far/bilinearSubdivisionTables.h b/opensubdiv/far/bilinearSubdivisionTables.h
index e32a8c8d..70c3862f 100644
--- a/opensubdiv/far/bilinearSubdivisionTables.h
+++ b/opensubdiv/far/bilinearSubdivisionTables.h
@@ -83,14 +83,14 @@ public:
 
     // Memory required to store the indexing tables
     virtual int GetMemoryUsed() const;
-   
-    // Compute the positions of refined vertices using the specified kernels
-    virtual void Refine( int level, void * data=0 ) const;   
-    
-    // Table accessors
-    typename FarSubdivisionTables::template Table const & Get_F_IT( ) const { return _F_IT; } 
 
-    typename FarSubdivisionTables::template Table const & Get_F_ITa( ) const { return _F_ITa; } 
+    // Compute the positions of refined vertices using the specified kernels
+    virtual void Apply( int level, void * data=0 ) const;
+
+    // Table accessors
+    FarTable const & Get_F_IT( ) const { return _F_IT; }
+
+    FarTable const & Get_F_ITa( ) const { return _F_ITa; }
 
     // Returns the number of indexing tables needed to represent this particular
     // subdivision scheme.
@@ -100,7 +100,7 @@ private:
 
     friend class FarMeshFactory;
     friend class FarDispatcher;
-    
+
     // Constructor : build level table at depth 'level'
     FarBilinearSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int level );
 
@@ -112,12 +112,12 @@ private:
 
     // Compute-kernel applied to vertices resulting from the refinement of a vertex
     void computeVertexPoints(int offset, int level, int start, int end, void * clientdata) const;
-    
-    
+
+
 private:
-   
-    typename FarSubdivisionTables::template Table           _F_ITa;
-    typename FarSubdivisionTables::template Table  _F_IT;
+
+    FarTable           _F_ITa;
+    FarTable  _F_IT;
 };
 
 template  int
@@ -137,12 +137,12 @@ FarBilinearSubdivisionTables::GetMemoryUsed() const {
 // _F_ITa[1] : valence of the face
 //
 // _E_ITa[0] : index of the org / dest vertices of the parent edge
-// _E_ITa[1] : 
+// _E_ITa[1] :
 //
 // _V_ITa[0] : index of the parent vertex
 //
 template 
-FarBilinearSubdivisionTables::FarBilinearSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int maxlevel ) : 
+FarBilinearSubdivisionTables::FarBilinearSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int maxlevel ) :
     FarSubdivisionTables(mesh, maxlevel),
     _F_ITa(maxlevel+1),
     _F_IT(maxlevel+1)
@@ -160,12 +160,12 @@ FarBilinearSubdivisionTables::FarBilinearSubdivisionTables( FarMeshFactory<
     for (int level=1; level<=maxlevel; ++level) {
 
         // pointer to the first vertex corresponding to this level
-        this->_vertsOffsets[level] = factory._vertVertIdx[level-1] + 
-                                     factory._vertVertsList[level-1].size();
+        this->_vertsOffsets[level] = factory._vertVertIdx[level-1] +
+                                     (int)factory._vertVertsList[level-1].size();
 
         typename FarSubdivisionTables::VertexKernelBatch * batch = & (this->_batches[level-1]);
 
-        // Face vertices 
+        // Face vertices
         // "For each vertex, gather all the vertices from the parent face."
         int offset = 0;
         int * F_ITa = this->_F_ITa[level-1];
@@ -190,10 +190,10 @@ FarBilinearSubdivisionTables::FarBilinearSubdivisionTables( FarMeshFactory<
         _F_ITa.SetMarker(level, &F_ITa[2*batch->kernelF]);
         _F_IT.SetMarker(level, &F_IT[offset]);
 
-        // Edge vertices 
+        // Edge vertices
 
         // "Average the end-points of the parent edge"
-        unsigned int * E_IT = this->_E_IT[level-1];
+        int * E_IT = this->_E_IT[level-1];
         batch->kernelE = (int)factory._edgeVertsList[level].size();
         for (int i=0; i < batch->kernelE; ++i) {
 
@@ -208,8 +208,8 @@ FarBilinearSubdivisionTables::FarBilinearSubdivisionTables( FarMeshFactory<
 
         }
         this->_E_IT.SetMarker(level, &E_IT[2*batch->kernelE]);
-        
-        // Vertex vertices 
+
+        // Vertex vertices
 
         // "Pass down the parent vertex"
         offset = 0;
@@ -230,10 +230,10 @@ FarBilinearSubdivisionTables::FarBilinearSubdivisionTables( FarMeshFactory<
 }
 
 template  void
-FarBilinearSubdivisionTables::Refine( int level, void * clientdata ) const {
-    
+FarBilinearSubdivisionTables::Apply( int level, void * clientdata ) const {
+
     assert(this->_mesh and level>0);
-    
+
     typename FarSubdivisionTables::VertexKernelBatch const * batch = & (this->_batches[level-1]);
 
     FarDispatcher const * dispatch = this->_mesh->GetDispatcher();
@@ -256,11 +256,11 @@ FarBilinearSubdivisionTables::Refine( int level, void * clientdata ) const
 // Face-vertices compute Kernel - completely re-entrant
 //
 
-template  void 
+template  void
 FarBilinearSubdivisionTables::computeFacePoints( int offset, int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
@@ -268,10 +268,10 @@ FarBilinearSubdivisionTables::computeFacePoints( int offset, int level, int
     const unsigned int * F_IT = _F_IT[level-1];
 
     for (int i=start; iClear(clientdata);
-        
-        int h = F_ITa[2*i  ], 
+
+        int h = F_ITa[2*i  ],
             n = F_ITa[2*i+1];
         float weight = 1.0f/n;
 
@@ -286,18 +286,18 @@ FarBilinearSubdivisionTables::computeFacePoints( int offset, int level, int
 // Edge-vertices compute Kernel - completely re-entrant
 //
 
-template  void 
+template  void
 FarBilinearSubdivisionTables::computeEdgePoints( int offset,  int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
-    const unsigned int * E_IT = this->_E_IT[level-1];
-      
+    const int * E_IT = this->_E_IT[level-1];
+
     for (int i=start; iClear(clientdata);
 
         int eidx0 = E_IT[2*i+0],
@@ -315,11 +315,11 @@ FarBilinearSubdivisionTables::computeEdgePoints( int offset,  int level, in
 // Vertex-vertices compute Kernel - completely re-entrant
 //
 
-template  void 
+template  void
 FarBilinearSubdivisionTables::computeVertexPoints( int offset, int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
@@ -329,7 +329,7 @@ FarBilinearSubdivisionTables::computeVertexPoints( int offset, int level, i
 
         vdst->Clear(clientdata);
 
-        int p=V_ITa[i];   // index of the parent vertex 
+        int p=V_ITa[i];   // index of the parent vertex
 
         vdst->AddWithWeight( vsrc[p], 1.0f, clientdata );
         vdst->AddVaryingWithWeight( vsrc[p], 1.0f, clientdata );
diff --git a/opensubdiv/far/catmarkSubdivisionTables.h b/opensubdiv/far/catmarkSubdivisionTables.h
index 2e10908e..8702dd13 100644
--- a/opensubdiv/far/catmarkSubdivisionTables.h
+++ b/opensubdiv/far/catmarkSubdivisionTables.h
@@ -83,14 +83,14 @@ public:
 
     // Memory required to store the indexing tables
     virtual int GetMemoryUsed() const;
-   
-    // Compute the positions of refined vertices using the specified kernels
-    virtual void Refine( int level, void * data=0 ) const;   
-    
-    // Table accessors
-    typename FarSubdivisionTables::template Table const & Get_F_IT( ) const { return _F_IT; } 
 
-    typename FarSubdivisionTables::template Table const & Get_F_ITa( ) const { return _F_ITa; } 
+    // Compute the positions of refined vertices using the specified kernels
+    virtual void Apply( int level, void * data=0 ) const;
+
+    // Table accessors
+    FarTable const & Get_F_IT( ) const { return _F_IT; }
+
+    FarTable const & Get_F_ITa( ) const { return _F_ITa; }
 
     // Returns the number of indexing tables needed to represent this particular
     // subdivision scheme.
@@ -100,7 +100,7 @@ private:
 
     friend class FarMeshFactory;
     friend class FarDispatcher;
-    
+
     // Constructor : build level table at depth 'level'
     FarCatmarkSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int level );
 
@@ -113,15 +113,15 @@ private:
     // Compute-kernel applied to vertices resulting from the refinement of a vertex
     // Kernel "A" Handles the k_Smooth and k_Dart rules
     void computeVertexPointsA(int offset, bool pass, int level, int start, int end, void * clientdata) const;
-    
+
     // Compute-kernel applied to vertices resulting from the refinement of a vertex
     // Kernel "B" Handles the k_Crease and k_Corner rules
     void computeVertexPointsB(int offset, int level, int start, int end, void * clientdata) const;
-    
+
 private:
-   
-    typename FarSubdivisionTables::template Table           _F_ITa;
-    typename FarSubdivisionTables::template Table  _F_IT;
+
+    FarTable           _F_ITa;
+    FarTable  _F_IT;
 };
 
 template  int
@@ -139,7 +139,7 @@ FarCatmarkSubdivisionTables::GetMemoryUsed() const {
 // _F_ITa[1] : valence of the face
 //
 // _E_ITa[0] : index of the org / dest vertices of the parent edge
-// _E_ITa[1] : 
+// _E_ITa[1] :
 // _E_ITa[2] : index of vertices refined from the faces left / right
 // _E_ITa[3] : of the parent edge
 //
@@ -150,7 +150,7 @@ FarCatmarkSubdivisionTables::GetMemoryUsed() const {
 // _V_ITa[4] : index of adjacent edge 1 (k_Crease rule)
 //
 template 
-FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int maxlevel ) : 
+FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int maxlevel ) :
     FarSubdivisionTables(mesh, maxlevel),
     _F_ITa(maxlevel+1),
     _F_IT(maxlevel+1)
@@ -171,12 +171,12 @@ FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactory_vertsOffsets[level] = factory._vertVertIdx[level-1] + 
-                                     factory._vertVertsList[level-1].size();
+        this->_vertsOffsets[level] = factory._vertVertIdx[level-1] +
+                                     (int)factory._vertVertsList[level-1].size();
 
         typename FarSubdivisionTables::VertexKernelBatch * batch = & (this->_batches[level-1]);
 
-        // Face vertices 
+        // Face vertices
         // "For each vertex, gather all the vertices from the parent face."
         int offset = 0;
         int * F_ITa = this->_F_ITa[level-1];
@@ -201,17 +201,17 @@ FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactorykernelF]);
         _F_IT.SetMarker(level, &F_IT[offset]);
 
-        // Edge vertices 
+        // Edge vertices
 
         // Triangular interpolation mode :
         // see "smoothtriangle" tag introduced in prman 3.9 and HbrCatmarkSubdivision
-        typename HbrCatmarkSubdivision::TriangleSubdivision triangleMethod = 
+        typename HbrCatmarkSubdivision::TriangleSubdivision triangleMethod =
             dynamic_cast *>(factory._hbrMesh->GetSubdivision())->GetTriangleSubdivisionMethod();
 
         // "For each vertex, gather the 2 vertices from the parent edege and the
         // 2 child vertices from the faces to the left and right of that edge.
         // Adjust if edge has a crease or is on a boundary."
-        unsigned int * E_IT = this->_E_IT[level-1];
+        int * E_IT = this->_E_IT[level-1];
         float * E_W = this->_E_W[level-1];
         batch->kernelE = (int)factory._edgeVertsList[level].size();
         for (int i=0; i < batch->kernelE; ++i) {
@@ -257,10 +257,10 @@ FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactory_E_IT.SetMarker(level, &E_IT[4*batch->kernelE]);
         this->_E_W.SetMarker(level, &E_W[2*batch->kernelE]);
-        
-        // Vertex vertices 
 
-        batch->InitVertexKernels( factory._vertVertsList[level].size(), 0 );
+        // Vertex vertices
+
+        batch->InitVertexKernels( (int)factory._vertVertsList[level].size(), 0 );
 
         offset = 0;
         int * V_ITa = this->_V_ITa[level-1];
@@ -274,19 +274,19 @@ FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactory::Subdivide for more details about
-            // the multi-pass interpolation 
+            // the multi-pass interpolation
             int masks[2], npasses;
             float weights[2];
             masks[0] = pv->GetMask(false);
             masks[1] = pv->GetMask(true);
 
             // If the masks are identical, only a single pass is necessary. If the
-            // vertex is transitionning to another rule, two passes are necessary,
-            // except when transitionning from k_Dart to k_Smooth : the same
+            // vertex is transitioning to another rule, two passes are necessary,
+            // except when transitioning from k_Dart to k_Smooth : the same
             // compute kernel is applied twice. Combining this special case allows
             // to batch the compute kernels into fewer calls.
             if (masks[0] != masks[1] and (
-                not (masks[0]==HbrVertex::k_Smooth and 
+                not (masks[0]==HbrVertex::k_Smooth and
                      masks[1]==HbrVertex::k_Dart))) {
                 weights[1] = pv->GetFractionalMask();
                 weights[0] = 1.0f - weights[1];
@@ -315,11 +315,11 @@ FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactoryGetDestVertex()->GetID() ];
-                             
+
                             V_IT[offset++] = remap[ e->GetLeftFace()->Subdivide()->GetID() ];
 
                             e = e->GetPrev()->GetOpposite();
-                            
+
                             if (e==start) break;
                         }
                         break;
@@ -350,7 +350,7 @@ FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactory::k_Corner :
                         // in the case of a k_Crease / k_Corner pass combination, we
                         // need to set the valence to -1 to tell the "B" Kernel to
@@ -363,7 +363,7 @@ FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactory7)
-                // the k_Corner and k_Crease single-pass cases apply a weight of 1.0 
+                // the k_Corner and k_Crease single-pass cases apply a weight of 1.0
                 // but this value is inverted in the kernel
                 V_W[i] = 0.0;
             else
@@ -382,10 +382,10 @@ FarCatmarkSubdivisionTables::FarCatmarkSubdivisionTables( FarMeshFactory void
-FarCatmarkSubdivisionTables::Refine( int level, void * clientdata ) const {
-    
+FarCatmarkSubdivisionTables::Apply( int level, void * clientdata ) const {
+
     assert(this->_mesh and level>0);
-    
+
     typename FarSubdivisionTables::VertexKernelBatch const * batch = & (this->_batches[level-1]);
 
     FarDispatcher const * dispatch = this->_mesh->GetDispatcher();
@@ -412,11 +412,11 @@ FarCatmarkSubdivisionTables::Refine( int level, void * clientdata ) const {
 // Face-vertices compute Kernel - completely re-entrant
 //
 
-template  void 
+template  void
 FarCatmarkSubdivisionTables::computeFacePoints( int offset, int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
@@ -424,10 +424,10 @@ FarCatmarkSubdivisionTables::computeFacePoints( int offset, int level, int
     const unsigned int * F_IT = _F_IT[level-1];
 
     for (int i=start; iClear(clientdata);
-        
-        int h = F_ITa[2*i  ], 
+
+        int h = F_ITa[2*i  ],
             n = F_ITa[2*i+1];
         float weight = 1.0f/n;
 
@@ -442,36 +442,36 @@ FarCatmarkSubdivisionTables::computeFacePoints( int offset, int level, int
 // Edge-vertices compute Kernel - completely re-entrant
 //
 
-template  void 
+template  void
 FarCatmarkSubdivisionTables::computeEdgePoints( int offset,  int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
-    const unsigned int * E_IT = this->_E_IT[level-1];
+    const int * E_IT = this->_E_IT[level-1];
     const float * E_W = this->_E_W[level-1];
-      
+
     for (int i=start; iClear(clientdata);
 
         int eidx0 = E_IT[4*i+0],
             eidx1 = E_IT[4*i+1],
             eidx2 = E_IT[4*i+2],
             eidx3 = E_IT[4*i+3];
-            
+
         float vertWeight = E_W[i*2+0];
 
         // Fully sharp edge : vertWeight = 0.5f
         vdst->AddWithWeight( vsrc[eidx0], vertWeight, clientdata );
         vdst->AddWithWeight( vsrc[eidx1], vertWeight, clientdata );
-        
+
         if (eidx2!=-1) {
             // Apply fractional sharpness
             float faceWeight = E_W[i*2+1];
-            
+
             vdst->AddWithWeight( vsrc[eidx2], faceWeight, clientdata );
             vdst->AddWithWeight( vsrc[eidx3], faceWeight, clientdata );
         }
@@ -486,11 +486,11 @@ FarCatmarkSubdivisionTables::computeEdgePoints( int offset,  int level, int
 //
 
 // multi-pass kernel handling k_Crease and k_Corner rules
-template  void 
+template  void
 FarCatmarkSubdivisionTables::computeVertexPointsA( int offset, bool pass, int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
@@ -501,16 +501,16 @@ FarCatmarkSubdivisionTables::computeVertexPointsA( int offset, bool pass, i
 
         if (not pass)
             vdst->Clear(clientdata);
-        
+
         int     n=V_ITa[5*i+1],   // number of vertices in the _VO_IT array (valence)
-                p=V_ITa[5*i+2],   // index of the parent vertex 
+                p=V_ITa[5*i+2],   // index of the parent vertex
             eidx0=V_ITa[5*i+3],   // index of the first crease rule edge
             eidx1=V_ITa[5*i+4];   // index of the second crease rule edge
 
         float weight = pass ? V_W[i] : 1.0f - V_W[i];
 
-        // In the case of fractional weight, the weight must be inverted since 
-        // the value is shared with the k_Smooth kernel (statistically the 
+        // In the case of fractional weight, the weight must be inverted since
+        // the value is shared with the k_Smooth kernel (statistically the
         // k_Smooth kernel runs much more often than this one)
         if (weight>0.0f and weight<1.0f and n>0)
             weight=1.0f-weight;
@@ -531,11 +531,11 @@ FarCatmarkSubdivisionTables::computeVertexPointsA( int offset, bool pass, i
 }
 
 // multi-pass kernel handling k_Dart and k_Smooth rules
-template  void 
+template  void
 FarCatmarkSubdivisionTables::computeVertexPointsB( int offset, int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
@@ -544,25 +544,25 @@ FarCatmarkSubdivisionTables::computeVertexPointsB( int offset, int level, i
     const float * V_W = this->_V_W[level-1];
 
     for (int i=start; iClear(clientdata);
-        
+
         int h = V_ITa[5*i  ],     // offset of the vertices in the _V0_IT array
             n = V_ITa[5*i+1],     // number of vertices in the _VO_IT array (valence)
             p = V_ITa[5*i+2];     // index of the parent vertex
-            
+
         float weight = V_W[i],
                   wp = 1.0f/(n*n),
                   wv = (n-2.0f)*n*wp;
 
         vdst->AddWithWeight( vsrc[p], weight * wv, clientdata );
-        
+
         for (int j=0; jAddWithWeight( vsrc[V_IT[h+j*2  ]], weight * wp, clientdata );
             vdst->AddWithWeight( vsrc[V_IT[h+j*2+1]], weight * wp, clientdata );
         }
         vdst->AddVaryingWithWeight( vsrc[p], 1.0f, clientdata );
-    }    
+    }
 }
 
 } // end namespace OPENSUBDIV_VERSION
diff --git a/opensubdiv/far/dispatcher.h b/opensubdiv/far/dispatcher.h
index 9d9bab2b..32b46a74 100644
--- a/opensubdiv/far/dispatcher.h
+++ b/opensubdiv/far/dispatcher.h
@@ -64,13 +64,14 @@
 #include "../far/bilinearSubdivisionTables.h"
 #include "../far/catmarkSubdivisionTables.h"
 #include "../far/loopSubdivisionTables.h"
+#include "../far/vertexEditTables.h"
 
 namespace OpenSubdiv {
 namespace OPENSUBDIV_VERSION {
 
 // Compute dispatcher : allows client code to customize parts or the entire
 // computation process. This pattern aims at hiding the logic specific to
-// the subdivision algorithms and expose a simplified access to minimalistic  
+// the subdivision algorithms and expose a simplified access to minimalistic
 // compute kernels. By default, meshes revert to a default dispatcher that
 // implements single-threaded CPU kernels.
 //
@@ -87,10 +88,11 @@ protected:
     friend class FarBilinearSubdivisionTables;
     friend class FarCatmarkSubdivisionTables;
     friend class FarLoopSubdivisionTables;
+    friend class FarVertexEditTables;
     friend class FarMesh;
-    
+
     virtual void Refine(FarMesh * mesh, int maxlevel, void * clientdata=0) const;
-    
+
 
     virtual void ApplyBilinearFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const;
 
@@ -114,9 +116,11 @@ protected:
 
     virtual void ApplyLoopVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * clientdata) const;
 
+    virtual void ApplyVertexEdit(FarMesh *mesh, int offset, int level, void * clientdata) const;
+
 private:
     friend class FarMeshFactory;
-    
+
     static FarDispatcher _DefaultDispatcher;
 };
 
@@ -130,95 +134,108 @@ FarDispatcher::Refine( FarMesh * mesh, int maxlevel, void * data) cons
 
     FarSubdivisionTables const * tables = mesh->GetSubdivision();
 
+    FarVertexEditTables const * edits = mesh->GetVertexEdit();
+
     if ( (maxlevel < 0) )
         maxlevel=tables->GetMaxLevel();
     else
         maxlevel = std::min(maxlevel, tables->GetMaxLevel());
-    
-    for (int i=1; iRefine(i, data);
+
+    for (int i=1; iApply(i, data);
+        if (edits)
+            edits->Apply(i, data);
+    }
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyBilinearFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const {
-    FarBilinearSubdivisionTables const * subdivision = 
+    FarBilinearSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeFacePoints(offset, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyBilinearEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const {
-    FarBilinearSubdivisionTables const * subdivision = 
+    FarBilinearSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeEdgePoints(offset, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyBilinearVertexVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const {
-    FarBilinearSubdivisionTables const * subdivision = 
+    FarBilinearSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeVertexPoints(offset, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyCatmarkFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const {
-    FarCatmarkSubdivisionTables const * subdivision = 
+    FarCatmarkSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeFacePoints(offset, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyCatmarkEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const {
-    FarCatmarkSubdivisionTables const * subdivision = 
+    FarCatmarkSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeEdgePoints(offset, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyCatmarkVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const {
-    FarCatmarkSubdivisionTables const * subdivision = 
+    FarCatmarkSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeVertexPointsB(offset, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyCatmarkVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * clientdata) const {
-    FarCatmarkSubdivisionTables const * subdivision = 
+    FarCatmarkSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeVertexPointsA(offset, pass, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyLoopEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const {
-    FarLoopSubdivisionTables const * subdivision = 
+    FarLoopSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeEdgePoints(offset, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyLoopVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * clientdata) const {
-    FarLoopSubdivisionTables const * subdivision = 
+    FarLoopSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeVertexPointsB(offset, level, start, end, clientdata);
 }
 
-template  void 
+template  void
 FarDispatcher::ApplyLoopVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * clientdata) const {
-    FarLoopSubdivisionTables const * subdivision = 
+    FarLoopSubdivisionTables const * subdivision =
         dynamic_cast const *>(mesh->GetSubdivision());
     assert(subdivision);
     subdivision->computeVertexPointsA(offset, pass, level, start, end, clientdata);
 }
 
+template  void
+FarDispatcher::ApplyVertexEdit(FarMesh * mesh, int offset, int level, void * clientdata) const {
+
+    FarVertexEditTables const * vertexEdit = mesh->GetVertexEdit();
+    if (vertexEdit)
+        vertexEdit->editVertex(level, clientdata);
+}
+
 } // end namespace OPENSUBDIV_VERSION
 using namespace OPENSUBDIV_VERSION;
 
diff --git a/opensubdiv/far/loopSubdivisionTables.h b/opensubdiv/far/loopSubdivisionTables.h
index a2028bf4..65b7909c 100644
--- a/opensubdiv/far/loopSubdivisionTables.h
+++ b/opensubdiv/far/loopSubdivisionTables.h
@@ -82,14 +82,14 @@ template  class FarLoopSubdivisionTables : public FarSubdivi
 public:
 
     // Compute the positions of refined vertices using the specified kernels
-    virtual void Refine( int level, void * data=0 ) const;   
-    
-    
-private: 
+    virtual void Apply( int level, void * data=0 ) const;
+
+
+private:
 
     friend class FarMeshFactory;
     friend class FarDispatcher;
-   
+
     // Constructor : build level table at depth 'level'
     FarLoopSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int level );
 
@@ -100,7 +100,7 @@ private:
     // Compute-kernel applied to vertices resulting from the refinement of a vertex
     // Kernel "A" Handles the k_Smooth and k_Dart rules
     void computeVertexPointsA(int offset, bool pass, int level, int start, int end, void * clientdata) const;
-    
+
     // Compute-kernel applied to vertices resulting from the refinement of a vertex
     // Kernel "B" Handles the k_Crease and k_Corner rules
     void computeVertexPointsB(int offset,int level, int start, int end, void * clientdata) const;
@@ -114,7 +114,7 @@ private:
 // codices detail :
 //
 // _E_ITa[0] : index of the org / dest vertices of the parent edge
-// _E_ITa[1] : 
+// _E_ITa[1] :
 // _E_ITa[2] : index of vertices refined from the faces left / right
 // _E_ITa[3] : of the parent edge
 //
@@ -125,8 +125,8 @@ private:
 // _V_ITa[3] : index of adjacent edge 1 (k_Crease rule)
 //
 template 
-FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int maxlevel ) 
-    : FarSubdivisionTables(mesh, maxlevel) 
+FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory const & factory, FarMesh * mesh, int maxlevel )
+    : FarSubdivisionTables(mesh, maxlevel)
 {
     std::vector const & remap = factory._remapTable;
 
@@ -141,13 +141,13 @@ FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory con
     for (int level=1; level<=maxlevel; ++level) {
 
         // pointer to the first vertex corresponding to this level
-        this->_vertsOffsets[level] = factory._vertVertIdx[level-1] + 
-                                     factory._vertVertsList[level-1].size();
+        this->_vertsOffsets[level] = factory._vertVertIdx[level-1] +
+                                     (int)factory._vertVertsList[level-1].size();
 
         typename FarSubdivisionTables::VertexKernelBatch * batch = & (this->_batches[level-1]);
 
-        // Edge vertices 
-        unsigned int * E_IT = this->_E_IT[level-1];
+        // Edge vertices
+        int * E_IT = this->_E_IT[level-1];
         float * E_W = this->_E_W[level-1];
         batch->kernelE = (int)factory._edgeVertsList[level].size();
         for (int i=0; i < batch->kernelE; ++i) {
@@ -182,9 +182,9 @@ FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory con
         this->_E_IT.SetMarker(level, &E_IT[4*batch->kernelE]);
         this->_E_W.SetMarker(level, &E_W[2*batch->kernelE]);
 
-        // Vertex vertices 
+        // Vertex vertices
 
-        batch->InitVertexKernels( factory._vertVertsList[level].size(), 0 );
+        batch->InitVertexKernels( (int)factory._vertVertsList[level].size(), 0 );
 
         int offset = 0;
         int * V_ITa = this->_V_ITa[level-1];
@@ -198,19 +198,19 @@ FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory con
             assert(v and pv);
 
             // Look at HbrCatmarkSubdivision::Subdivide for more details about
-            // the multi-pass interpolation 
+            // the multi-pass interpolation
             int masks[2], npasses;
             float weights[2];
             masks[0] = pv->GetMask(false);
             masks[1] = pv->GetMask(true);
 
             // If the masks are identical, only a single pass is necessary. If the
-            // vertex is transitionning to another rule, two passes are necessary,
-            // except when transitionning from k_Dart to k_Smooth : the same
+            // vertex is transitioning to another rule, two passes are necessary,
+            // except when transitioning from k_Dart to k_Smooth : the same
             // compute kernel is applied twice. Combining this special case allows
             // to batch the compute kernels into fewer calls.
             if (masks[0] != masks[1] and (
-                not (masks[0]==HbrVertex::k_Smooth and 
+                not (masks[0]==HbrVertex::k_Smooth and
                      masks[1]==HbrVertex::k_Dart))) {
                 weights[1] = pv->GetFractionalMask();
                 weights[0] = 1.0f - weights[1];
@@ -241,7 +241,7 @@ FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory con
                             V_IT[offset++] = remap[ e->GetDestVertex()->GetID() ];
 
                             e = e->GetPrev()->GetOpposite();
-                            
+
                             if (e==start) break;
                         }
                         break;
@@ -272,7 +272,7 @@ FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory con
                         V_ITa[5*i+3] = remap[op.eidx[0]];
                         V_ITa[5*i+4] = remap[op.eidx[1]];
                         break;
-                    } 
+                    }
                     case HbrVertex::k_Corner :
                         // in the case of a k_Crease / k_Corner pass combination, we
                         // need to set the valence to -1 to tell the "B" Kernel to
@@ -284,7 +284,7 @@ FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory con
                 }
 
             if (rank>7)
-                // the k_Corner and k_Crease single-pass cases apply a weight of 1.0 
+                // the k_Corner and k_Crease single-pass cases apply a weight of 1.0
                 // but this value is inverted in the kernel
                 V_W[i] = 0.0;
             else
@@ -302,11 +302,11 @@ FarLoopSubdivisionTables::FarLoopSubdivisionTables( FarMeshFactory con
     }
 }
 
-template  void 
-FarLoopSubdivisionTables::Refine( int level, void * clientdata ) const
+template  void
+FarLoopSubdivisionTables::Apply( int level, void * clientdata ) const
 {
     assert(this->_mesh and level>0);
-    
+
     typename FarSubdivisionTables::VertexKernelBatch const * batch = & (this->_batches[level-1]);
 
     FarDispatcher const * dispatch = this->_mesh->GetDispatcher();
@@ -323,42 +323,42 @@ FarLoopSubdivisionTables::Refine( int level, void * clientdata ) const
         dispatch->ApplyLoopVertexVerticesKernelA(this->_mesh, offset, false, level, batch->kernelA1.first, batch->kernelA1.second, clientdata);
     if (batch->kernelA2.first < batch->kernelA2.second)
         dispatch->ApplyLoopVertexVerticesKernelA(this->_mesh, offset, true, level, batch->kernelA2.first, batch->kernelA2.second, clientdata);
-}  
+}
 
 //
 // Edge-vertices compute Kernel - completely re-entrant
 //
 
-template  void 
+template  void
 FarLoopSubdivisionTables::computeEdgePoints( int offset, int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
-    const unsigned int * E_IT = this->_E_IT[level-1];
+    const int * E_IT = this->_E_IT[level-1];
     const float * E_W = this->_E_W[level-1];
-      
+
     for (int i=start; iClear(clientdata);
-    
+
         int eidx0 = E_IT[4*i+0],
             eidx1 = E_IT[4*i+1],
             eidx2 = E_IT[4*i+2],
             eidx3 = E_IT[4*i+3];
-            
+
         float endPtWeight = E_W[i*2+0];
 
-        // Fully sharp edge : endPtWeight = 0.5f        
+        // Fully sharp edge : endPtWeight = 0.5f
         vdst->AddWithWeight( vsrc[eidx0], endPtWeight, clientdata );
         vdst->AddWithWeight( vsrc[eidx1], endPtWeight, clientdata );
-        
+
         if (eidx2!=-1) {
             // Apply fractional sharpness
             float oppPtWeight = E_W[i*2+1];
-            
+
             vdst->AddWithWeight( vsrc[eidx2], oppPtWeight, clientdata );
             vdst->AddWithWeight( vsrc[eidx3], oppPtWeight, clientdata );
         }
@@ -373,11 +373,11 @@ FarLoopSubdivisionTables::computeEdgePoints( int offset, int level, int sta
 //
 
 // multi-pass kernel handling k_Crease and k_Corner rules
-template  void 
+template  void
 FarLoopSubdivisionTables::computeVertexPointsA( int offset, bool pass, int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
@@ -385,19 +385,19 @@ FarLoopSubdivisionTables::computeVertexPointsA( int offset, bool pass, int
     const float * V_W = this->_V_W[level-1];
 
     for (int i=start; iClear(clientdata);
 
         int     n=V_ITa[5*i+1], // number of vertices in the _VO_IT array (valence)
-                p=V_ITa[5*i+2], // index of the parent vertex 
+                p=V_ITa[5*i+2], // index of the parent vertex
             eidx0=V_ITa[5*i+3], // index of the first crease rule edge
             eidx1=V_ITa[5*i+4]; // index of the second crease rule edge
 
         float weight = pass ? V_W[i] : 1.0f - V_W[i];
 
-        // In the case of fractional weight, the weight must be inverted since 
-        // the value is shared with the k_Smooth kernel (statistically the 
+        // In the case of fractional weight, the weight must be inverted since
+        // the value is shared with the k_Smooth kernel (statistically the
         // k_Smooth kernel runs much more often than this one)
         if (weight>0.0f and weight<1.0f and n>0)
             weight=1.0f-weight;
@@ -418,11 +418,11 @@ FarLoopSubdivisionTables::computeVertexPointsA( int offset, bool pass, int
 }
 
 // multi-pass kernel handling k_Dart and k_Smooth rules
-template  void 
+template  void
 FarLoopSubdivisionTables::computeVertexPointsB( int offset, int level, int start, int end, void * clientdata ) const {
 
     assert(this->_mesh);
-    
+
     U * vsrc = &this->_mesh->GetVertices().at(0),
       * vdst = vsrc + offset + start;
 
@@ -431,13 +431,13 @@ FarLoopSubdivisionTables::computeVertexPointsB( int offset, int level, int
     const float * V_W = this->_V_W[level-1];
 
     for (int i=start; iClear(clientdata);
-         
+
         int h = V_ITa[5*i  ], // offset of the vertices in the _V0_IT array
             n = V_ITa[5*i+1], // number of vertices in the _VO_IT array (valence)
-            p = V_ITa[5*i+2]; // index of the parent vertex 
-            
+            p = V_ITa[5*i+2]; // index of the parent vertex
+
         float weight = V_W[i],
                   wp = 1.0f/n,
                 beta = 0.25f * cosf((float)M_PI * 2.0f * wp) + 0.375f;
@@ -445,12 +445,12 @@ FarLoopSubdivisionTables::computeVertexPointsB( int offset, int level, int
         beta = (0.625f-beta)*wp;
 
         vdst->AddWithWeight( vsrc[p], weight * (1.0f-(beta*n)), clientdata);
-        
+
         for (int j=0; jAddWithWeight( vsrc[V_IT[h+j]], weight * beta );
 
         vdst->AddVaryingWithWeight( vsrc[p], 1.0f, clientdata );
-    }    
+    }
 }
 
 } // end namespace OPENSUBDIV_VERSION
diff --git a/opensubdiv/far/mesh.h b/opensubdiv/far/mesh.h
index 07e1c199..8ab4a214 100644
--- a/opensubdiv/far/mesh.h
+++ b/opensubdiv/far/mesh.h
@@ -68,6 +68,7 @@ namespace OPENSUBDIV_VERSION {
 template  class FarMeshFactory;
 template  class FarSubdivisionTables;
 template  class FarDispatcher;
+template  class FarVertexEditTables;
 
 // Core serialized subdivision mesh class.
 //
@@ -79,7 +80,7 @@ template  class FarDispatcher;
 
 template  class FarMesh {
 public:
-    
+
     ~FarMesh();
 
     // returns the subdivision method
@@ -93,18 +94,25 @@ public:
         k_BilinearQuads,
         k_Triangles,
     };
-    
-    // returns the type of patches described by the face vertices list 
+
+    // returns the type of patches described by the face vertices list
     PatchType GetPatchType() const { return _patchtype; }
 
     // returns the list of vertices in the mesh (from subdiv level 0 to N)
     std::vector & GetVertices() { return _vertices; }
-    
+
     U & GetVertex(int index) { return _vertices[index]; }
 
     // returns the list of indices of the vertices of the faces in the mesh
     std::vector const & GetFaceVertices(int level) const;
 
+    // returns the ptex coordinates for each face at a given level. The coordinates
+    // are stored as : (int) faceindex / (ushort) u_index / (ushort) v_index
+    std::vector const & GetPtexCoordinates(int level) const;
+
+    // returns vertex edit tables
+    FarVertexEditTables const * GetVertexEdit() const { return _vertexEdit; }
+
     // returns the number of coarse vertices held at the beginning of the vertex
     // buffer.
     int GetNumCoarseVertices() const;
@@ -112,14 +120,14 @@ public:
     // returns the total number of vertices in the mesh across across all depths
     int GetNumVertices() const { return (int)(_vertices.size()); }
 
-    // apply the subdivision tables to compute the positions of the vertices up 
+    // apply the subdivision tables to compute the positions of the vertices up
     // to 'level'
     void Subdivide(int level=-1);
 
-private:    
+private:
     friend class FarMeshFactory;
 
-    FarMesh() : _subdivision(0), _dispatcher(0) { }
+    FarMesh() : _subdivision(0), _dispatcher(0), _vertexEdit(0) { }
 
     // non-copyable, so these are not implemented:
     FarMesh(FarMesh const &);
@@ -130,13 +138,19 @@ private:
 
     // customizable compute dispatcher class
     FarDispatcher * _dispatcher;
-    
+
     // list of vertices (up to N levels of subdivision)
     std::vector _vertices;
 
     // list of vertex indices for each face
     std::vector< std::vector > _faceverts;
-    
+
+    // ptex coordinates for each face
+    std::vector< std::vector > _ptexcoordinates;
+
+    // hierarchical vertex edit tables
+    FarVertexEditTables * _vertexEdit;
+
     // XXX stub for adaptive work
     PatchType _patchtype;
 
@@ -146,8 +160,9 @@ private:
 
 template 
 FarMesh::~FarMesh()
-{ 
+{
     delete _subdivision;
+    delete _vertexEdit;
 }
 
 template  int
@@ -155,13 +170,21 @@ FarMesh::GetNumCoarseVertices() const {
     return _numCoarseVertices;
 }
 
-template  std::vector const & 
+template  std::vector const &
 FarMesh::GetFaceVertices(int level) const {
     if ( (level>=0) and (level<(int)_faceverts.size()) )
         return _faceverts[level];
     return _faceverts[0];
 }
 
+template  std::vector const &
+FarMesh::GetPtexCoordinates(int level) const {
+    if ( (level>=0) and (level<(int)_faceverts.size()) )
+        return _ptexcoordinates[level];
+    return _ptexcoordinates[0];
+}
+
+
 template  void
 FarMesh::Subdivide(int maxlevel) {
 
@@ -172,8 +195,11 @@ FarMesh::Subdivide(int maxlevel) {
     else
         maxlevel = std::min(maxlevel, _subdivision->GetMaxLevel());
 
-    for (int i=1; iRefine(i);
+    for (int i=1; iApply(i);
+        if (_vertexEdit)
+            _vertexEdit->Apply(i);
+    }
 }
 
 } // end namespace OPENSUBDIV_VERSION
diff --git a/opensubdiv/far/meshFactory.h b/opensubdiv/far/meshFactory.h
index 619f9df7..df408219 100644
--- a/opensubdiv/far/meshFactory.h
+++ b/opensubdiv/far/meshFactory.h
@@ -95,7 +95,7 @@ public:
     // Create a table-based mesh representation
     // XXXX : this creator will take the options for adaptive patch meshes
     FarMesh * Create( FarDispatcher * dispatch=0 );
-   
+
     // Maximum level of subidivision supported by this factory
     int GetMaxLevel() const { return _maxlevel; }
 
@@ -103,12 +103,12 @@ public:
     int GetNumFaceVerticesTotal(int level) const {
         return sumList *>(_faceVertsList, level);
     }
-    
+
     // Total number of edge vertices up to 'level'
     int GetNumEdgeVerticesTotal(int level) const {
         return sumList *>(_edgeVertsList, level);
     }
-    
+
     // Total number of vertex vertices up to 'level'
     int GetNumVertexVerticesTotal(int level) const {
         return sumList *>(_vertVertsList, level);
@@ -116,12 +116,12 @@ public:
 
     // Valence summation up to 'level'
     int GetNumAdjacentVertVerticesTotal(int level) const;
-   
+
     // Total number of faces across up to a level
     int GetNumFacesTotal(int level) const {
         return sumList *>(_facesList, level);
     }
-    
+
     // Return the corresponding index of the HbrVertex in the new mesh
     int GetVertexID( HbrVertex * v );
 
@@ -132,6 +132,7 @@ private:
     friend class FarBilinearSubdivisionTables;
     friend class FarCatmarkSubdivisionTables;
     friend class FarLoopSubdivisionTables;
+    friend class FarVertexEditTables;
 
     // Non-copyable, so these are not implemented:
     FarMeshFactory( FarMeshFactory const & );
@@ -144,23 +145,29 @@ private:
     static bool isLoop(HbrMesh * mesh);
 
     void copyTopology( std::vector & vec, int level );
-    
+
+    void generatePtexCoordinates( std::vector & vec, int level );
+
+    FarVertexEditTables * createVertexEdit(FarMesh * mesh);
+
     static void refine( HbrMesh * mesh, int maxlevel );
 
     template  static int sumList( std::vector > const & list, int level );
 
+    static bool compareNSubfaces(HbrVertexEdit const *a, HbrVertexEdit const *b);
+
     HbrMesh * _hbrMesh;
-    
-    int _maxlevel, 
-        _numVertices, 
+
+    int _maxlevel,
+        _numVertices,
         _numFaces;
 
     // per-level counters and offsets for each type of vertex (face,edge,vert)
     std::vector _faceVertIdx,
-                     _edgeVertIdx, 
+                     _edgeVertIdx,
                      _vertVertIdx;
 
-    // number of indices required for the vertex iteration table at each level                 
+    // number of indices required for the vertex iteration table at each level
     std::vector _vertVertsListSize;
 
     // remapping table to translate vertex ID's between Hbr indices and the
@@ -168,7 +175,7 @@ private:
     std::vector _remapTable;
 
     // lists of vertices sorted by type and level
-    std::vector *> > _faceVertsList, 
+    std::vector *> > _faceVertsList,
                                                _edgeVertsList,
                                                _vertVertsList;
 
@@ -176,21 +183,21 @@ private:
     std::vector *> > _facesList;
 };
 
-template  
-    template  int 
+template 
+    template  int
 FarMeshFactory::sumList( std::vector > const & list, int level) {
 
-    level = std::min(level, (int)list.size());  
+    level = std::min(level, (int)list.size());
     int total = 0;
     for (int i=0; i<=level; ++i)
         total += (int)list[i].size();
     return total;
 }
 
-template  int 
+template  int
 FarMeshFactory::GetNumAdjacentVertVerticesTotal(int level) const {
 
-    level = std::min(level, GetMaxLevel());  
+    level = std::min(level, GetMaxLevel());
     int total = 0;
     for (int i=0; i<=level; ++i)
         total += _vertVertsListSize[i];
@@ -214,17 +221,17 @@ FarMeshFactory::refine( HbrMesh * mesh, int maxlevel ) {
 // Assumption : the order of the vertices in the HbrMesh could be set in any
 // random order, so the builder runs 2 passes over the entire vertex list to
 // gather the counters needed to generate the indexing tables.
-template  
+template 
 FarMeshFactory::FarMeshFactory( HbrMesh * mesh, int maxlevel ) :
     _hbrMesh(mesh),
     _maxlevel(maxlevel),
     _numVertices(-1),
     _numFaces(-1),
-    _faceVertIdx(maxlevel+1,0), 
-    _edgeVertIdx(maxlevel+1,0), 
+    _faceVertIdx(maxlevel+1,0),
+    _edgeVertIdx(maxlevel+1,0),
     _vertVertIdx(maxlevel+1,0),
     _vertVertsListSize(maxlevel+1,0),
-    _faceVertsList(maxlevel+1), 
+    _faceVertsList(maxlevel+1),
     _edgeVertsList(maxlevel+1),
     _vertVertsList(maxlevel+1),
     _facesList(maxlevel+1)
@@ -235,11 +242,11 @@ FarMeshFactory::FarMeshFactory( HbrMesh * mesh, int maxlevel ) :
     int numVertices = mesh->GetNumVertices();
     int numFaces = mesh->GetNumFaces();
 
-    std::vector faceCounts(maxlevel+1,0), 
-                     edgeCounts(maxlevel+1,0), 
+    std::vector faceCounts(maxlevel+1,0),
+                     edgeCounts(maxlevel+1,0),
                      vertCounts(maxlevel+1,0);
 
-    // First pass (vertices) : count the vertices of each type for each depth 
+    // First pass (vertices) : count the vertices of each type for each depth
     // up to maxlevel (values are dependent on topology).
     int maxvertid=-1;
     for (int i=0; i::FarMeshFactory( HbrMesh * mesh, int maxlevel ) :
         assert(v);
 
         int depth = v->GetFace()->GetDepth();
-        
+
         if (depth>maxlevel)
             continue;
 
         if (depth==0 )
             vertCounts[depth]++;
-        
-        if (v->GetID()>maxvertid) 
+
+        if (v->GetID()>maxvertid)
             maxvertid = v->GetID();
-        
+
         if (not v->OnBoundary())
             _vertVertsListSize[depth] += v->GetValence();
         else if (v->GetValence()!=2)
             _vertVertsListSize[depth] ++;
-            
-        if (v->GetParentFace()) 
+
+        if (v->GetParentFace())
             faceCounts[depth]++;
-        else if (v->GetParentEdge()) 
+        else if (v->GetParentEdge())
             edgeCounts[depth]++;
-        else if (v->GetParentVertex()) 
+        else if (v->GetParentVertex())
             vertCounts[depth]++;
     }
-    
+
     // Per-level offset to the first vertex of each type in the global vertex map
     _vertVertsList[0].reserve( vertCounts[0] );
     for (int l=1; l<(maxlevel+1); ++l) {
         _faceVertIdx[l]= _vertVertIdx[l-1]+vertCounts[l-1];
         _edgeVertIdx[l]= _faceVertIdx[l]+faceCounts[l];
         _vertVertIdx[l]= _edgeVertIdx[l]+edgeCounts[l];
-    
+
         _faceVertsList[l].reserve( faceCounts[l] );
         _edgeVertsList[l].reserve( edgeCounts[l] );
         _vertVertsList[l].reserve( vertCounts[l] );
-    }                       
-    
+    }
+
     // reset counters
     faceCounts.assign(maxlevel+1,0);
     edgeCounts.assign(maxlevel+1,0);
-    
+
     _remapTable.resize( maxvertid+1, -1);
-    
-    // Second pass (vertices) : calculate the starting indices of the sub-tables 
+
+    // Second pass (vertices) : calculate the starting indices of the sub-tables
     // (face, edge, verts...) and populate the remapping table.
     for (int i=0; i::FarMeshFactory( HbrMesh * mesh, int maxlevel ) :
         assert(v);
 
         int depth = v->GetFace()->GetDepth();
-        
+
         if (depth>maxlevel)
             continue;
-        
+
         assert( _remapTable[ v->GetID() ] = -1 );
-        
-        if (depth==0) {        
+
+        if (depth==0) {
             _vertVertsList[ depth ].push_back( v );
             _remapTable[ v->GetID() ] = v->GetID();
         } else if (v->GetParentFace()) {
@@ -318,25 +325,25 @@ FarMeshFactory::FarMeshFactory( HbrMesh * mesh, int maxlevel ) :
             _vertVertsList[ depth ].push_back( v );
         }
     }
-    
+
     // Sort the the vertices that are the child of a vertex based on their weight
     // mask. The masks combinations are ordered so as to minimize the compute
     // kernel switching ( more information on this in the HbrVertex comparison
-    // function 'FarSubdivisionTables::compareVertices' ).   
+    // function 'FarSubdivisionTables::compareVertices' ).
     for (size_t i=1; i<_vertVertsList.size(); ++i)
         std::sort(_vertVertsList[i].begin(), _vertVertsList[i].end(),
-            FarSubdivisionTables::compareVertices);    
+            FarSubdivisionTables::compareVertices);
 
     // These vertices still need a remapped index
     for (int l=1; l<(maxlevel+1); ++l)
         for (size_t i=0; i<_vertVertsList[l].size(); ++i)
-            _remapTable[ _vertVertsList[l][i]->GetID() ]=_vertVertIdx[l]+i;
+            _remapTable[ _vertVertsList[l][i]->GetID() ]=_vertVertIdx[l]+(int)i;
+
 
-    
     // Third pass (faces) : populate the face lists.
     int fsize=0;
     for (int i=0; i * f = mesh->GetFace(i); 
+        HbrFace * f = mesh->GetFace(i);
         assert(f);
         if (f->GetDepth()==0)
             fsize += mesh->GetSubdivision()->GetFaceChildrenCount( f->GetNumVertices() );
@@ -359,12 +366,12 @@ FarMeshFactory::FarMeshFactory( HbrMesh * mesh, int maxlevel ) :
                    GetNumVertexVerticesTotal(maxlevel);
 }
 
-template  bool 
+template  bool
 FarMeshFactory::isBilinear(HbrMesh * mesh) {
     return typeid(*(mesh->GetSubdivision()))==typeid(HbrBilinearSubdivision);
 }
 
-template  bool 
+template  bool
 FarMeshFactory::isCatmark(HbrMesh * mesh) {
     return typeid(*(mesh->GetSubdivision()))==typeid(HbrCatmarkSubdivision);
 }
@@ -378,17 +385,17 @@ template  void
 FarMeshFactory::copyTopology( std::vector & vec, int level ) {
 
     assert( _hbrMesh );
-    
+
     int nv=-1;
     if ( isCatmark(_hbrMesh) or isBilinear(_hbrMesh) )
         nv=4;
     else if ( isLoop(_hbrMesh) )
         nv=3;
-  
+
     assert(nv>0);
-  
+
     vec.resize( nv * _facesList[level].size(), -1 );
-    
+
     for (int i=0; i<(int)_facesList[level].size(); ++i) {
         HbrFace * f = _facesList[level][i];
         assert( f and f->GetNumVertices()==nv);
@@ -396,7 +403,6 @@ FarMeshFactory::copyTopology( std::vector & vec, int level ) {
             vec[nv*i+j]=_remapTable[f->GetVertex(j)->GetID()];
     }
 }
-
 template  void
 copyVertex( T & dest, U const & src ) {
 }
@@ -406,14 +412,193 @@ copyVertex( T & dest, T const & src ) {
     dest = src;
 }
 
-template  FarMesh * 
+// XXX : this currently only supports Catmark / Bilinear schemes.
+template  void
+FarMeshFactory::generatePtexCoordinates( std::vector & vec, int level ) {
+
+    assert( _hbrMesh );
+
+    if (_facesList[level][0]->GetPtexIndex() == -1) return;
+
+    vec.resize( _facesList[level].size()*2, -1 );
+
+    for (int i=0; i<(int)_facesList[level].size(); ++i) {
+
+        HbrFace const * f = _facesList[level][i];
+        assert(f);
+
+        short u,v;
+        unsigned short ofs = 1, depth;
+        bool quad = true;
+
+        // track upwards towards coarse face, accumulating u,v indices
+        HbrFace const * p = f->GetParent();
+        for ( u=v=depth=0;  p!=NULL; depth++ ) {
+
+            int nverts = p->GetNumVertices();
+            if ( nverts != 4 ) {           // non-quad coarse face : stop accumulating offsets
+                quad = false;              // invert the coarse face index
+                break;
+            }
+
+            for (unsigned char i=0; iGetChild( i )==f ) {
+                    switch ( i ) {
+                      case 0 :                     break;
+                      case 1 : { u+=ofs;         } break;
+                      case 2 : { u+=ofs; v+=ofs; } break;
+                      case 3 : {         v+=ofs; } break;
+                    }
+                    break;
+                }
+            ofs = ofs << 1;
+            f = p;
+            p = f->GetParent();
+        }
+
+        vec[2*i] = quad ? f->GetPtexIndex() : -f->GetPtexIndex();
+        vec[2*i+1] = (int)u << 16;
+        vec[2*i+1] += v;
+    }
+}
+
+template  bool
+FarMeshFactory::compareNSubfaces(HbrVertexEdit const *a, HbrVertexEdit const *b) {
+
+    return a->GetNSubfaces() < b->GetNSubfaces();
+}
+
+template  FarVertexEditTables *
+FarMeshFactory::createVertexEdit(FarMesh *mesh) {
+
+    FarVertexEditTables * table = new FarVertexEditTables(mesh, _maxlevel);
+
+    std::vector*> const & hEdits = _hbrMesh->GetHierarchicalEdits();
+
+    std::vector const *> vertexEdits;
+    vertexEdits.reserve(hEdits.size());
+
+    for (int i=0; i<(int)hEdits.size(); ++i) {
+        HbrVertexEdit *vedit = dynamic_cast *>(hEdits[i]);
+        if (vedit) {
+            int editlevel = vedit->GetNSubfaces();
+            if (editlevel > _maxlevel)
+                continue;   // far table doesn't contain such level
+
+            vertexEdits.push_back(vedit);
+        }
+    }
+
+    // sort vertex edits by level
+    std::sort(vertexEdits.begin(), vertexEdits.end(), compareNSubfaces);
+
+    // uniquify edits with index and width
+    std::vector batchIndices;
+    std::vector batchSizes;
+    for(int i=0; i<(int)vertexEdits.size(); ++i) {
+        HbrVertexEdit const *vedit = vertexEdits[i];
+
+        // translate operation enum
+        typename FarVertexEditTables::Operation operation = (vedit->GetOperation() == HbrHierarchicalEdit::Set) ?
+            FarVertexEditTables::Set : FarVertexEditTables::Add;
+
+        // determine which batch this edit belongs to (create it if necessary)
+        int batchIndex = -1;
+        for(int i = 0; i<(int)table->_batches.size(); ++i) {
+            if(table->_batches[i]._index == vedit->GetIndex() &&
+               table->_batches[i]._width == vedit->GetWidth() &&
+               table->_batches[i]._operation == operation) {
+                batchIndex = i;
+                break;
+            }
+        }
+        if (batchIndex == -1) {
+            // create new batch
+            batchIndex = (int)table->_batches.size();
+            table->_batches.push_back(typename FarVertexEditTables::VertexEdit(vedit->GetIndex(), vedit->GetWidth(), operation));
+            batchSizes.push_back(0);
+        }
+        batchSizes[batchIndex]++;
+        batchIndices.push_back(batchIndex);
+    }
+
+    // allocate batches
+    int numBatches = table->GetNumBatches();
+    for(int i=0; i_batches[i]._offsets.SetMaxLevel(_maxlevel+1);
+        table->_batches[i]._values.SetMaxLevel(_maxlevel+1);
+        table->_batches[i]._offsets.Resize(batchSizes[i]);
+        table->_batches[i]._values.Resize(batchSizes[i] * table->_batches[i]._width);
+    }
+
+    // resolve vertexedits path to absolute offset and put them into corresponding batch
+    std::vector currentLevels(numBatches);
+    std::vector currentCounts(numBatches);
+    for(int i=0; i<(int)vertexEdits.size(); ++i){
+        HbrVertexEdit const *vedit = vertexEdits[i];
+
+        HbrFace * f = _hbrMesh->GetFace(vedit->GetFaceID());
+
+        int level = vedit->GetNSubfaces();
+        for (int j=0; jGetChild(vedit->GetSubface(j));
+
+        // remap vertex ID
+        int vertexID = f->GetVertex(vedit->GetVertexID())->GetID();
+        vertexID = _remapTable[vertexID];
+
+        int batchIndex = batchIndices[i];
+        int & batchLevel = currentLevels[batchIndex];
+        int & batchCount = currentCounts[batchIndex];
+        typename FarVertexEditTables::VertexEdit &batch = table->_batches[batchIndex];
+
+        // fill marker for skipped levels if exists
+        while(currentLevels[batchIndex] < level-1) {
+            batch._offsets.SetMarker(batchLevel+1, &batch._offsets[batchLevel][batchCount]);
+            batch._values.SetMarker(batchLevel+1, &batch._values[batchLevel][batchCount*batch._width]);
+            batchLevel++;
+            batchCount = 0;
+        }
+
+        // set absolute vertex offset and edit values
+        const float *values = vedit->GetEdit();
+        bool negate = (vedit->GetOperation() == HbrHierarchicalEdit::Subtract);
+
+        batch._offsets[level-1][batchCount] = vertexID;
+        for(int i=0; i::VertexEdit &batch = table->_batches[i];
+        int & batchLevel = currentLevels[i];
+        int & batchCount = currentCounts[i];
+
+        // fill marker for rest levels if exists
+        while(batchLevel < _maxlevel) {
+            batch._offsets.SetMarker(batchLevel+1, &batch._offsets[batchLevel][batchCount]);
+            batch._values.SetMarker(batchLevel+1, &batch._values[batchLevel][batchCount*batch._width]);
+            batchLevel++;
+            batchCount = 0;
+        }
+    }
+
+    return table;
+}
+
+template  FarMesh *
 FarMeshFactory::Create( FarDispatcher * dispatch ) {
 
     assert( _hbrMesh );
-    
+
     if (_maxlevel<1)
         return 0;
-    
+
     FarMesh * result = new FarMesh();
 
     if (dispatch)
@@ -422,19 +607,19 @@ FarMeshFactory::Create( FarDispatcher * dispatch ) {
         result->_dispatcher = & FarDispatcher::_DefaultDispatcher;
 
     if ( isBilinear( _hbrMesh ) ) {
-        result->_subdivision = 
+        result->_subdivision =
             new FarBilinearSubdivisionTables( *this, result, _maxlevel );
     } else if ( isCatmark( _hbrMesh ) ) {
-        result->_subdivision = 
+        result->_subdivision =
             new FarCatmarkSubdivisionTables( *this, result, _maxlevel );
     } else if ( isLoop(_hbrMesh) ) {
-        result->_subdivision = 
+        result->_subdivision =
             new FarLoopSubdivisionTables( *this, result, _maxlevel );
     } else
         assert(0);
 
     result->_numCoarseVertices = (int)_vertVertsList[0].size();
-    
+
     // Copy the data of the coarse vertices into the vertex buffer.
     // XXXX : we should figure out a test to make sure that the vertex
     //        class is not an empty placeholder (ex. non-interleaved data)
@@ -445,13 +630,21 @@ FarMeshFactory::Create( FarDispatcher * dispatch ) {
     // Populate topology (face verts indices)
     // XXXX : only k_BilinearQuads support for now - adaptive bicubic patches to come
     result->_patchtype = FarMesh::k_BilinearQuads;
-    
-    // XXXX : we should let the client decide which levels to copy, 
-    // they may only want vertices...
+
+    // XXXX : we should let the client control what to copy, most of this may be irrelevant
     result->_faceverts.resize(_maxlevel+1);
-    for (int l=1; l<=_maxlevel; ++l) 
+    for (int l=1; l<=_maxlevel; ++l)
         copyTopology(result->_faceverts[l], l);
 
+    result->_ptexcoordinates.resize(_maxlevel+1);
+    for (int l=1; l<=_maxlevel; ++l)
+        generatePtexCoordinates(result->_ptexcoordinates[l], l);
+
+    // Create VertexEditTables if necessary
+    if (_hbrMesh->HasVertexEdits()) {
+        result->_vertexEdit = createVertexEdit(result);
+    }
+
     return result;
 }
 
diff --git a/opensubdiv/far/subdivisionTables.h b/opensubdiv/far/subdivisionTables.h
index f4bef3d3..63f058b0 100644
--- a/opensubdiv/far/subdivisionTables.h
+++ b/opensubdiv/far/subdivisionTables.h
@@ -62,6 +62,7 @@
 #include 
 
 #include "../version.h"
+#include "../far/table.h"
 
 template  class HbrFace;
 template  class HbrHalfedge;
@@ -89,7 +90,7 @@ template  class FarMeshFactory;
 // __W : fractional weight of the vertex (based on sharpness & topology)
 // __ITa : codex for the two previous tables
 
-// For more details see : "Feature Adaptive GPU Rendering of Catmull-Clark 
+// For more details see : "Feature Adaptive GPU Rendering of Catmull-Clark
 // Subdivision Surfaces"  p.3 - par. 3.2
 template  class FarSubdivisionTables {
 public:
@@ -102,17 +103,17 @@ public:
 
     // Memory required to store the indexing tables
     virtual int GetMemoryUsed() const;
-    
+
     // Compute the positions of refined vertices using the specified kernels
-    virtual void Refine( int level, void * clientdata=0 ) const=0;
-    
+    virtual void Apply( int level, void * clientdata=0 ) const=0;
+
     // Pointer back to the mesh owning the table
-    FarMesh * GetMesh() { return _mesh; }    
+    FarMesh * GetMesh() { return _mesh; }
 
     // The index of the first vertex that belongs to the level of subdivision
     // represented by this set of FarCatmarkSubdivisionTables
     int GetFirstVertexOffset( int level ) const;
-    
+
     // Number of vertices children of a face at a given level (always 0 for Loop)
     int GetNumFaceVertices( int level ) const;
 
@@ -121,70 +122,27 @@ public:
 
     // Number of vertices children of a vertex at a given level
     int GetNumVertexVertices( int level ) const;
-    
+
     // Total number of vertices at a given level
     int GetNumVertices( int level ) const;
-    
+
     // Indexing tables accessors
 
-    // Generic multi-level indexing table : the indices across all the subdivision
-    // levels are stored in a single std::vector. The table class holds a sequence
-    // of markers pointing to the first index at the beginning of the sequence
-    // describing a given level (note that "level 1" vertices are obtained by using 
-    // the indices starting at "level 0" of the tables)
-    template  class Table {
-        std::vector   _data;     // table data
-        std::vector _markers;  // pointers to the first datum at each level
-    public:
-
-        Table(int maxlevel) : _markers(maxlevel) { }
-
-        // Returns the memory required to store the data in this table.
-        int GetMemoryUsed() const {
-            return (int)_data.size() * sizeof(Type);
-        }
-
-        // Saves a pointer indicating the beginning of data pertaining to "level"
-        // of subdivision
-        void SetMarker(int level, Type * marker) {
-            _markers[level] = marker;
-        }
-
-        // Resize the table to size (also resets markers)
-        void Resize(int size) { 
-            _data.resize(size); 
-            _markers[0] = &_data[0]; 
-        }
-        
-        // Returns a pointer to the data at the beginning of level "level" of
-        // subdivision
-        Type * operator[](int level) { 
-            assert(level>=0 and level<(int)_markers.size());
-            return _markers[level];
-        }
-
-        // Returns a const pointer to the data at the beginning of level "level"
-        // of subdivision
-        const Type * operator[](int level) const {
-            return const_cast(this)->operator[](level);
-        }
-    };
-    
     // Returns the edge vertices indexing table
-    Table const & Get_E_IT() const { return _E_IT; } 
+    FarTable const &          Get_E_IT() const { return _E_IT; }
 
     // Returns the edge vertices weights table
-    Table const &        Get_E_W() const { return _E_W; }
+    FarTable const &        Get_E_W() const { return _E_W; }
 
     // Returns the vertex vertices codex table
-    Table const &          Get_V_ITa() const { return _V_ITa; } 
+    FarTable const &          Get_V_ITa() const { return _V_ITa; }
 
     // Returns the vertex vertices indexing table
-    Table const & Get_V_IT() const { return _V_IT; } 
+    FarTable const & Get_V_IT() const { return _V_IT; }
 
     // Returns the vertex vertices weights table
-    Table const &        Get_V_W() const { return _V_W; }
-    
+    FarTable const &        Get_V_W() const { return _V_W; }
+
     // Returns the number of indexing tables needed to represent this particular
     // subdivision scheme.
     virtual int GetNumTables() const { return 5; }
@@ -196,7 +154,7 @@ protected:
 
     // Returns an integer based on the order in which the kernels are applied
     static int getMaskRanking( unsigned char mask0, unsigned char mask1 );
-    
+
     // Compares to vertices based on the ranking of their hbr masks combination
     static bool compareVertices( HbrVertex const * x, HbrVertex const * y );
 
@@ -207,10 +165,10 @@ protected:
         std::pair kernelB;  // first / last vertex vertex batch (kernel B)
         std::pair kernelA1; // first / last vertex vertex batch (kernel A pass 1)
         std::pair kernelA2; // first / last vertex vertex batch (kernel A pass 2)
-        
+
         VertexKernelBatch() : kernelF(0), kernelE(0) { }
 
-        void InitVertexKernels(int a, int b) { 
+        void InitVertexKernels(int a, int b) {
             kernelB.first = kernelA1.first = kernelA2.first = a;
             kernelB.second = kernelA1.second = kernelA2.second = b;
         }
@@ -218,21 +176,21 @@ protected:
         void AddVertex( int index, int rank ) {
             // expand the range of kernel batches based on vertex index and rank
             if (rank<7) {
-                if (index < kernelB.first) 
+                if (index < kernelB.first)
                     kernelB.first=index;
-                if (index > kernelB.second) 
+                if (index > kernelB.second)
                     kernelB.second=index;
             }
             if ((rank>2) and (rank<8)) {
-                if (index < kernelA2.first) 
+                if (index < kernelA2.first)
                     kernelA2.first=index;
-                if (index > kernelA2.second) 
+                if (index > kernelA2.second)
                     kernelA2.second=index;
             }
             if (rank>6) {
-                if (index < kernelA1.first) 
+                if (index < kernelA1.first)
                     kernelA1.first=index;
-                if (index > kernelA1.second) 
+                if (index > kernelA1.second)
                     kernelA1.second=index;
             }
         }
@@ -244,23 +202,23 @@ protected:
 
 protected:
     // mesh that owns this subdivisionTable
-    FarMesh * _mesh; 
+    FarMesh * _mesh;
 
-    Table _E_IT;  // vertices from edge refinement
-    Table        _E_W;   // weigths
+    FarTable          _E_IT;  // vertices from edge refinement
+    FarTable        _E_W;   // weigths
 
-    Table          _V_ITa; // vertices from vertex refinement
-    Table _V_IT;  // indices of adjacent vertices
-    Table        _V_W;   // weights
+    FarTable          _V_ITa; // vertices from vertex refinement
+    FarTable _V_IT;  // indices of adjacent vertices
+    FarTable        _V_W;   // weights
 
     std::vector _batches; // batches of vertices for kernel execution
- 
+
     std::vector _vertsOffsets; // offset to the first vertex of each level
 private:
 };
 
-template  
-FarSubdivisionTables::FarSubdivisionTables( FarMesh * mesh, int maxlevel ) : 
+template 
+FarSubdivisionTables::FarSubdivisionTables( FarMesh * mesh, int maxlevel ) :
     _mesh(mesh),
     _E_IT(maxlevel+1),
     _E_W(maxlevel+1),
@@ -277,7 +235,7 @@ FarSubdivisionTables::FarSubdivisionTables( FarMesh * mesh, int maxlev
 // of Corner, Crease, Dart and Smooth topological configurations. This matrix is
 // somewhat arbitrary as it is possible to perform some permutations in the
 // ordering without adverse effects, but it does try to minimize kernel switching
-// during the exececution of Refine(). This table is identical for both the Loop
+// during the exececution of Apply(). This table is identical for both the Loop
 // and Catmull-Clark schemes.
 //
 // The matrix is derived from this table :
@@ -290,10 +248,10 @@ FarSubdivisionTables::FarSubdivisionTables( FarMesh * mesh, int maxlev
 //           +----+----+----+----+----+----+----+----+----+----+
 // Rank      | 0  | 1  | 2  | 3  | 4  | 5  | 6  | 7  | 8  | 9  |
 //           +----+----+----+----+----+----+----+----+----+----+
-// with : 
+// with :
 //     - A : compute kernel applying k_Crease / k_Corner rules
 //     - B : compute kernel applying k_Smooth / k_Dart rules
-template  int 
+template  int
 FarSubdivisionTables::getMaskRanking( unsigned char mask0, unsigned char mask1 ) {
     static short masks[4][4] = { {    0,    1,    6,    4 },
                                  { 0xFF,    2,    5,    3 },
@@ -313,46 +271,46 @@ template  bool
 FarSubdivisionTables::compareVertices( HbrVertex const * x, HbrVertex const * y ) {
 
     // Masks of the parent vertex decide for the current vertex.
-    HbrVertex * px=x->GetParentVertex(), 
+    HbrVertex * px=x->GetParentVertex(),
                  * py=y->GetParentVertex();
 
     assert( (getMaskRanking(px->GetMask(false), px->GetMask(true) )!=0xFF) and
             (getMaskRanking(py->GetMask(false), py->GetMask(true) )!=0xFF) );
 
-    return getMaskRanking(px->GetMask(false), px->GetMask(true) ) < 
+    return getMaskRanking(px->GetMask(false), px->GetMask(true) ) <
            getMaskRanking(py->GetMask(false), py->GetMask(true) );
 }
 
-template  int 
-FarSubdivisionTables::GetFirstVertexOffset( int level ) const { 
+template  int
+FarSubdivisionTables::GetFirstVertexOffset( int level ) const {
     assert(level>=0 and level<=(int)_vertsOffsets.size());
-    return _vertsOffsets[level]; 
+    return _vertsOffsets[level];
 }
 
-template  int 
+template  int
 FarSubdivisionTables::GetNumFaceVertices( int level ) const {
     assert(level>=0 and level<=(int)_batches.size());
     return _batches[level-1].kernelF;
 }
 
-template  int 
+template  int
 FarSubdivisionTables::GetNumEdgeVertices( int level ) const {
     assert(level>=0 and level<=(int)_batches.size());
     return _batches[level-1].kernelE;
 }
 
-template  int 
+template  int
 FarSubdivisionTables::GetNumVertexVertices( int level ) const {
     assert(level>=0 and level<=(int)_batches.size());
     if (level==0)
         return _mesh->GetNumCoarseVertices();
     else
         return std::max( _batches[level-1].kernelB.second,
-                   std::max(_batches[level-1].kernelA1.second, 
+                   std::max(_batches[level-1].kernelA1.second,
                        _batches[level-1].kernelA2.second));
 }
 
-template  int 
+template  int
 FarSubdivisionTables::GetNumVertices( int level ) const {
     assert(level>=0 and level<=(int)_batches.size());
     if (level==0)
diff --git a/opensubdiv/far/table.h b/opensubdiv/far/table.h
new file mode 100644
index 00000000..76e66e77
--- /dev/null
+++ b/opensubdiv/far/table.h
@@ -0,0 +1,127 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+#ifndef FAR_TABLE_H
+#define FAR_TABLE_H
+
+#include "../version.h"
+
+namespace OpenSubdiv {
+namespace OPENSUBDIV_VERSION {
+
+// Generic multi-level indexing table : the indices across all the subdivision
+// levels are stored in a single std::vector. The table class holds a sequence
+// of markers pointing to the first index at the beginning of the sequence
+// describing a given level (note that "level 1" vertices are obtained by using
+// the indices starting at "level 0" of the tables)
+template  class FarTable {
+    std::vector   _data;     // table data
+    std::vector _markers;  // pointers to the first datum at each level
+public:
+
+    FarTable() { }
+
+    FarTable(int maxlevel) : _markers(maxlevel) { }
+
+    // Reset max level and clear data
+    void SetMaxLevel(int maxlevel) {
+        _data.clear();
+        _markers.resize(maxlevel);
+    }
+
+    // Returns the memory required to store the data in this table.
+    int GetMemoryUsed() const {
+        return (int)_data.size() * sizeof(Type);
+    }
+
+    // Returns the number of elements in level "level"
+    int GetNumElements(int level) const {
+        assert(level>=0 and level<((int)_markers.size()-1));
+        return (int)(_markers[level+1] - _markers[level]);
+    }
+
+    // Saves a pointer indicating the beginning of data pertaining to "level"
+    // of subdivision
+    void SetMarker(int level, Type * marker) {
+        _markers[level] = marker;
+    }
+
+    // Resize the table to size (also resets markers)
+    void Resize(int size) {
+        _data.resize(size);
+        _markers[0] = &_data[0];
+    }
+
+    // Returns a pointer to the data at the beginning of level "level" of
+    // subdivision
+    Type * operator[](int level) {
+        assert(level>=0 and level<(int)_markers.size());
+        return _markers[level];
+    }
+
+    // Returns a const pointer to the data at the beginning of level "level"
+    // of subdivision
+    const Type * operator[](int level) const {
+        return const_cast(this)->operator[](level);
+    }
+};
+
+} // end namespace OPENSUBDIV_VERSION
+using namespace OPENSUBDIV_VERSION;
+
+} // end namespace OpenSubdiv
+
+#endif /* FAR_TABLE_H */
diff --git a/opensubdiv/far/vertexEditTables.h b/opensubdiv/far/vertexEditTables.h
new file mode 100644
index 00000000..055bef9c
--- /dev/null
+++ b/opensubdiv/far/vertexEditTables.h
@@ -0,0 +1,215 @@
+//
+//     Copyright (C) Pixar. All rights reserved.
+//
+//     This license governs use of the accompanying software. If you
+//     use the software, you accept this license. If you do not accept
+//     the license, do not use the software.
+//
+//     1. Definitions
+//     The terms "reproduce," "reproduction," "derivative works," and
+//     "distribution" have the same meaning here as under U.S.
+//     copyright law.  A "contribution" is the original software, or
+//     any additions or changes to the software.
+//     A "contributor" is any person or entity that distributes its
+//     contribution under this license.
+//     "Licensed patents" are a contributor's patent claims that read
+//     directly on its contribution.
+//
+//     2. Grant of Rights
+//     (A) Copyright Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free copyright license to reproduce its contribution,
+//     prepare derivative works of its contribution, and distribute
+//     its contribution or any derivative works that you create.
+//     (B) Patent Grant- Subject to the terms of this license,
+//     including the license conditions and limitations in section 3,
+//     each contributor grants you a non-exclusive, worldwide,
+//     royalty-free license under its licensed patents to make, have
+//     made, use, sell, offer for sale, import, and/or otherwise
+//     dispose of its contribution in the software or derivative works
+//     of the contribution in the software.
+//
+//     3. Conditions and Limitations
+//     (A) No Trademark License- This license does not grant you
+//     rights to use any contributor's name, logo, or trademarks.
+//     (B) If you bring a patent claim against any contributor over
+//     patents that you claim are infringed by the software, your
+//     patent license from such contributor to the software ends
+//     automatically.
+//     (C) If you distribute any portion of the software, you must
+//     retain all copyright, patent, trademark, and attribution
+//     notices that are present in the software.
+//     (D) If you distribute any portion of the software in source
+//     code form, you may do so only under this license by including a
+//     complete copy of this license with your distribution. If you
+//     distribute any portion of the software in compiled or object
+//     code form, you may only do so under a license that complies
+//     with this license.
+//     (E) The software is licensed "as-is." You bear the risk of
+//     using it. The contributors give no express warranties,
+//     guarantees or conditions. You may have additional consumer
+//     rights under your local laws which this license cannot change.
+//     To the extent permitted under your local laws, the contributors
+//     exclude the implied warranties of merchantability, fitness for
+//     a particular purpose and non-infringement.
+//
+#ifndef FAR_VERTEX_EDIT_TABLES_H
+#define FAR_VERTEX_EDIT_TABLES_H
+
+#include 
+#include 
+#include 
+
+#include "../version.h"
+#include "../far/table.h"
+#include "../far/dispatcher.h"
+#include "../hbr/hierarchicalEdit.h"
+
+template  class HbrFace;
+template  class HbrHalfedge;
+template  class HbrVertex;
+template  class HbrMesh;
+
+namespace OpenSubdiv {
+namespace OPENSUBDIV_VERSION {
+
+template  class FarMesh;
+template  class FarMeshFactory;
+
+template  class FarVertexEditTables {
+public:
+    FarVertexEditTables( FarMesh * mesh, int maxlevel);
+
+    // type of edit operation. This enum matches to HbrHiearachicalEdit::Operation
+    enum Operation {
+        Set,
+        Add
+    };
+
+    // Compute the positions of edited vertices
+    void Apply(int level, void * clientdata=0) const;
+
+    int GetNumBatches() const {
+        return (int)_batches.size();
+    }
+
+    // this class holds a batch for vertex edit. each batch has unique index/width/operation
+    class VertexEdit {
+    public:
+        VertexEdit(int index, int width, Operation operation);
+
+        // copy vertex id and edit values into table
+        void Append(int level, int vertexID, const float *values, bool negate);
+
+        // Compute-kernel applied to vertices
+        void ApplyVertexEdit(U * vsrc, int level) const;
+
+        // Edit tables accessors
+
+        // Returns the edit offset table
+        FarTable const & Get_Offsets() const { return _offsets; }
+
+        // Returns the edit values table
+        FarTable const & Get_Values() const { return _values; }
+
+        Operation GetOperation() const { return _operation; }
+
+        int GetPrimvarOffset() const { return _index; }
+
+        int GetPrimvarWidth() const { return _width; } 
+
+    private:
+        friend class FarMeshFactory;
+
+        FarTable _offsets;  // absolute vertex index array for edits
+        FarTable        _values;   // edit values array
+
+        int _index;                       // primvar offset in vertex
+        int _width;                       // numElements per vertex in values
+        Operation _operation;             // edit operation (Set, Add)
+    };
+
+    VertexEdit const & GetBatch(int index) const {
+        return _batches[index];
+    }
+
+protected:
+    friend class FarMeshFactory;
+    friend class FarDispatcher;
+
+    // Compute-kernel applied to vertices
+    void editVertex(int level, void *clientdata) const;
+
+    // mesh that owns this vertexEditTable
+    FarMesh * _mesh;
+
+    std::vector _batches;
+};
+
+template 
+FarVertexEditTables::VertexEdit::VertexEdit(int index, int width, Operation operation) :
+    _index(index),
+    _width(width),
+    _operation(operation) {
+}
+
+template 
+void
+FarVertexEditTables::VertexEdit::ApplyVertexEdit(U * vsrc, int level) const
+{
+    int n = _offsets.GetNumElements(level-1);
+    const unsigned int * offsets = _offsets[level-1];
+    const float * values = _values[level-1];
+
+    for(int i=0; iApplyVertexEditAdd(const float *), vdst->ApplyVertexEditSet(const float *)
+        if (_operation == FarVertexEditTables::Set) {
+            HbrVertexEdit vedit(0, 0, 0, 0, 0, _width, false, HbrVertexEdit::Set, const_cast(&values[i*_width]));
+            vdst->ApplyVertexEdit(vedit);
+        } else {
+            HbrVertexEdit vedit(0, 0, 0, 0, 0, _width, false, HbrVertexEdit::Add, const_cast(&values[i*_width]));
+            vdst->ApplyVertexEdit(vedit);
+        }
+    }
+}
+
+template 
+FarVertexEditTables::FarVertexEditTables( FarMesh * mesh, int maxlevel) :
+    _mesh(mesh) {
+}
+
+
+template  void
+FarVertexEditTables::Apply( int level, void * clientdata ) const {
+
+    assert(this->_mesh and level>0);
+
+    FarDispatcher const * dispatch = this->_mesh->GetDispatcher();
+    assert(dispatch);
+
+    dispatch->ApplyVertexEdit(this->_mesh, 0, level, clientdata);
+}
+
+template  void
+FarVertexEditTables::editVertex(int level, void *clientdata) const {
+
+    assert(this->_mesh);
+
+    U * vsrc = &this->_mesh->GetVertices().at(0);
+
+    for(int i=0; i<(int)_batches.size(); ++i) {
+        _batches[i].ApplyVertexEdit(vsrc, level);
+    }
+}
+
+} // end namespace OPENSUBDIV_VERSION
+using namespace OPENSUBDIV_VERSION;
+
+} // end namespace OpenSubdiv
+
+#endif /* FAR_VERTEX_EDIT_TABLES_H */
diff --git a/opensubdiv/hbr/CMakeLists.txt b/opensubdiv/hbr/CMakeLists.txt
index 2f06bb62..74522c03 100644
--- a/opensubdiv/hbr/CMakeLists.txt
+++ b/opensubdiv/hbr/CMakeLists.txt
@@ -73,8 +73,8 @@ set(H_FILES
     subdivision.h
     vertexEdit.h
     vertex.h
-)    
+)
 
 install( FILES ${H_FILES}
          DESTINATION include/hbr
-         PERMISSIONS OWNER_READ GROUP_READ WORLD_READ ) 
+         PERMISSIONS OWNER_READ GROUP_READ WORLD_READ )
diff --git a/opensubdiv/hbr/allocator.h b/opensubdiv/hbr/allocator.h
index 1e35f6d1..40910a30 100644
--- a/opensubdiv/hbr/allocator.h
+++ b/opensubdiv/hbr/allocator.h
@@ -90,7 +90,7 @@ public:
     void SetMemStatsIncrement(void (*increment)(unsigned long bytes)) { m_increment = increment; }
 
     void SetMemStatsDecrement(void (*decrement)(unsigned long bytes)) { m_decrement = decrement; }
-    
+
 private:
     size_t *m_memorystat;
     const int m_blocksize;
@@ -111,10 +111,10 @@ private:
     HbrMemStatFunction m_increment;
     HbrMemStatFunction m_decrement;
 };
-					  
+
 template 
 HbrAllocator::HbrAllocator(size_t *memorystat, int blocksize, void (*increment)(unsigned long bytes), void (*decrement)(unsigned long bytes), size_t elemsize)
-    : m_memorystat(memorystat), m_blocksize(blocksize), m_elemsize(elemsize), m_blocks(0), m_nblocks(0), m_blockCapacity(0), m_freecount(0), m_increment(increment), m_decrement(decrement) {
+    : m_memorystat(memorystat), m_blocksize(blocksize), m_elemsize((int)elemsize), m_blocks(0), m_nblocks(0), m_blockCapacity(0), m_freecount(0), m_increment(increment), m_decrement(decrement) {
 }
 
 template 
@@ -125,15 +125,15 @@ HbrAllocator::~HbrAllocator() {
 template 
 void HbrAllocator::Clear() {
     for (int i = 0; i < m_nblocks; ++i) {
-	// Run the destructors (placement)
-	T* blockptr = m_blocks[i];
-	T* startblock = blockptr;
-	for (int j = 0; j < m_blocksize; ++j) {
-	    blockptr->~T();
-	    blockptr = (T*) ((char*) blockptr + m_elemsize);
-	}
-	free(startblock);
-	if (m_decrement) m_decrement(m_blocksize * m_elemsize);
+        // Run the destructors (placement)
+        T* blockptr = m_blocks[i];
+        T* startblock = blockptr;
+        for (int j = 0; j < m_blocksize; ++j) {
+            blockptr->~T();
+            blockptr = (T*) ((char*) blockptr + m_elemsize);
+        }
+        free(startblock);
+        if (m_decrement) m_decrement(m_blocksize * m_elemsize);
         *m_memorystat -= m_blocksize * m_elemsize;
     }
     free(m_blocks);
@@ -145,40 +145,40 @@ void HbrAllocator::Clear() {
 }
 
 template 
-T* 
+T*
 HbrAllocator::Allocate() {
     if (!m_freecount) {
 
-	// Allocate a new block
-	T* block = (T*) malloc(m_blocksize * m_elemsize);
-	T* blockptr = block;
-	// Run the constructors on each element using placement new
-	for (int i = 0; i < m_blocksize; ++i) {
-	    new (blockptr) T();
-	    blockptr = (T*) ((char*) blockptr + m_elemsize);
-	}
-	if (m_increment) m_increment(m_blocksize * m_elemsize);
+        // Allocate a new block
+        T* block = (T*) malloc(m_blocksize * m_elemsize);
+        T* blockptr = block;
+        // Run the constructors on each element using placement new
+        for (int i = 0; i < m_blocksize; ++i) {
+            new (blockptr) T();
+            blockptr = (T*) ((char*) blockptr + m_elemsize);
+        }
+        if (m_increment) m_increment(m_blocksize * m_elemsize);
         *m_memorystat += m_blocksize * m_elemsize;
-	
-	// Put the block's entries on the free list
-	blockptr = block;
-	for (int i = 0; i < m_blocksize - 1; ++i) {
-	    T* next = (T*) ((char*) blockptr + m_elemsize);
-	    blockptr->GetNext() = next;
-	    blockptr = next;
-	}
-	blockptr->GetNext() = 0;
-	m_freelist = block;
 
-	// Keep track of the newly allocated block
-	if (m_nblocks + 1 >= m_blockCapacity) {
-	    m_blockCapacity = m_blockCapacity * 2;
-	    if (m_blockCapacity < 1) m_blockCapacity = 1;
-	    m_blocks = (T**) realloc(m_blocks, m_blockCapacity * sizeof(T*));
-	}
-	m_blocks[m_nblocks] = block;
-	m_nblocks++;
-	m_freecount += m_blocksize;
+        // Put the block's entries on the free list
+        blockptr = block;
+        for (int i = 0; i < m_blocksize - 1; ++i) {
+            T* next = (T*) ((char*) blockptr + m_elemsize);
+            blockptr->GetNext() = next;
+            blockptr = next;
+        }
+        blockptr->GetNext() = 0;
+        m_freelist = block;
+
+        // Keep track of the newly allocated block
+        if (m_nblocks + 1 >= m_blockCapacity) {
+            m_blockCapacity = m_blockCapacity * 2;
+            if (m_blockCapacity < 1) m_blockCapacity = 1;
+            m_blocks = (T**) realloc(m_blocks, m_blockCapacity * sizeof(T*));
+        }
+        m_blocks[m_nblocks] = block;
+        m_nblocks++;
+        m_freecount += m_blocksize;
     }
     T* obj = m_freelist;
     m_freelist = obj->GetNext();
diff --git a/opensubdiv/hbr/bilinear.h b/opensubdiv/hbr/bilinear.h
index 03f24933..ab07de6a 100644
--- a/opensubdiv/hbr/bilinear.h
+++ b/opensubdiv/hbr/bilinear.h
@@ -69,29 +69,30 @@ template 
 class HbrBilinearSubdivision : public HbrSubdivision {
 public:
     HbrBilinearSubdivision()
-	: HbrSubdivision() {}
+        : HbrSubdivision() {}
 
     virtual HbrSubdivision* Clone() const {
         return new HbrBilinearSubdivision();
     }
-    
+
     virtual void Refine(HbrMesh* mesh, HbrFace* face);
-    virtual HbrFace* RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face, HbrVertex* vertex);    
+    virtual HbrFace* RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face, HbrVertex* vertex);
     virtual void GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge);
     virtual void GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* vertex);
 
     virtual bool HasLimit(HbrMesh* mesh, HbrFace* face);
     virtual bool HasLimit(HbrMesh* mesh, HbrHalfedge* edge);
     virtual bool HasLimit(HbrMesh* mesh, HbrVertex* vertex);
-    
+
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrFace* face);
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrHalfedge* edge);
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrVertex* vertex);
 
     virtual bool VertexIsExtraordinary(HbrMesh* /* mesh */, HbrVertex* vertex) { return vertex->GetValence() != 4; }
+    virtual bool FaceIsExtraordinary(HbrMesh* /* mesh */, HbrFace* face) { return face->GetNumVertices() != 4; }
 
     virtual int GetFaceChildrenCount(int nvertices) const { return nvertices; }
-    
+
 private:
 
     // Transfers facevarying data from a parent face to a child face
@@ -143,14 +144,14 @@ HbrBilinearSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* fac
     // allocate a new block of facevarying storage specific to the
     // child face.
     bool fv0IsSmooth, fv1IsSmooth, fv3IsSmooth;
-    
+
     childVertex = child->GetVertex(extraordinary ? 0 : (index+0)%4);
     fv0IsSmooth = v->IsFVarAllSmooth();
     if (!fv0IsSmooth) {
         childVertex->NewFVarData(child);
     }
     HbrFVarData& fv0 = childVertex->GetFVarData(child);
-    
+
     edge = face->GetEdge(index);
     GuaranteeNeighbor(mesh, edge);
     assert(edge->GetOrgVertex() == v);
@@ -162,7 +163,7 @@ HbrBilinearSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* fac
     HbrFVarData& fv1 = childVertex->GetFVarData(child);
 
     edge = edge->GetPrev();
-    GuaranteeNeighbor(mesh, edge);    
+    GuaranteeNeighbor(mesh, edge);
     assert(edge == face->GetEdge((index + nv - 1) % nv));
     assert(edge->GetDestVertex() == v);
     childVertex = child->GetVertex(extraordinary ? 3 : (index+3)%4);
@@ -314,7 +315,7 @@ HbrBilinearSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* fac
             starte = bestedge;
             w = 0;
             if (HbrHalfedge* e = starte) {
-                assert(starte->GetOrgVertex() == v);	
+                assert(starte->GetOrgVertex() == v);
                 do {
                     if (e->GetFVarSharpness(fvaritem) || !e->GetRightFace()) {
                         bestface = e->GetLeftFace();
@@ -331,7 +332,7 @@ HbrBilinearSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* fac
             }
             assert(j != bestface->GetNumVertices());
             fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
-	
+
         }
         // Smooth rule. Here, we can take a shortcut if we know that
         // the vertex is smooth and some other vertex has completely
@@ -388,7 +389,7 @@ HbrBilinearSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* fac
                 fv1.AddWithWeight(oppFace->GetFVarData(j), fvarindex, fvarwidth, weight);
             }
         }
-		
+
 
         // Edge subdivision rule
         edge = edge->GetPrev();
@@ -396,11 +397,11 @@ HbrBilinearSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* fac
         if (fvarinterp == HbrMesh::k_InterpolateBoundaryNone ||
             edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {
 
-            // Sharp edge rule						 
+            // Sharp edge rule
             fv3.SetWithWeight(face->GetFVarData((index + nv - 1) % nv), fvarindex, fvarwidth, 0.5f);
             fv3.AddWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.5f);
         } else if (!fv3IsSmooth || !fv3.IsInitialized()) {
-            // Smooth edge subdivision. Add 0.25 of adjacent vertices	
+            // Smooth edge subdivision. Add 0.25 of adjacent vertices
             fv3.SetWithWeight(face->GetFVarData((index + nv - 1) % nv), fvarindex, fvarwidth, 0.25f);
             fv3.AddWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.25f);
             // Local subdivided face vertex
@@ -418,13 +419,6 @@ HbrBilinearSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* fac
     fv0.SetInitialized();
     fv1.SetInitialized();
     fv3.SetInitialized();
-
-    // Special handling of ptex index for extraordinary faces: make
-    // sure the children get their indices reassigned to be
-    // consecutive within the block reserved for the parent.
-    if (face->GetNumVertices() != 4 && face->GetPtexIndex() != -1) {
-        child->SetPtexIndex(face->GetPtexIndex() + index);
-    }
 }
 
 template 
@@ -433,15 +427,15 @@ HbrBilinearSubdivision::transferEditsToChild(HbrFace* face, HbrFace* ch
 
     // Hand down pointers to hierarchical edits
     if (HbrHierarchicalEdit** edits = face->GetHierarchicalEdits()) {
-	while (HbrHierarchicalEdit* edit = *edits) {
-	    if (!edit->IsRelevantToFace(face)) break;
-	    if (edit->GetNSubfaces() > face->GetDepth() &&
-		(edit->GetSubface(face->GetDepth()) == index)) {
-		child->SetHierarchicalEdits(edits);
-		break;
-	    }
-	    edits++;
-	}
+        while (HbrHierarchicalEdit* edit = *edits) {
+            if (!edit->IsRelevantToFace(face)) break;
+            if (edit->GetNSubfaces() > face->GetDepth() &&
+                (edit->GetSubface(face->GetDepth()) == index)) {
+                child->SetHierarchicalEdits(edits);
+                break;
+            }
+            edits++;
+        }
     }
 }
 
@@ -464,50 +458,57 @@ HbrBilinearSubdivision::Refine(HbrMesh* mesh, HbrFace* face) {
     // parametric space through the refinement. If we split an
     // extraordinary face then it doesn't matter.
     for (int i = 0; i < nv; ++i) {
-	if (!face->GetChild(i)) {
+        if (!face->GetChild(i)) {
 #ifdef HBR_DEBUG
-	    std::cerr << "Kid " << i << "\n";
+            std::cerr << "Kid " << i << "\n";
 #endif
-	    HbrVertex* vertex = edge->GetOrgVertex();
-	    if (extraordinary) {
-		vertices[0] = vertex->Subdivide();
-		vertices[1] = edge->Subdivide();
-		vertices[2] = face->Subdivide();
-		vertices[3] = prevedge->Subdivide();
-	    } else {
-		vertices[i] = vertex->Subdivide();
-		vertices[(i+1)%4] = edge->Subdivide();
-		vertices[(i+2)%4] = face->Subdivide();
-		vertices[(i+3)%4] = prevedge->Subdivide();
-	    }
-	    child = mesh->NewFace(4, vertices, face, i);
+            HbrVertex* vertex = edge->GetOrgVertex();
+            if (extraordinary) {
+                vertices[0] = vertex->Subdivide();
+                vertices[1] = edge->Subdivide();
+                vertices[2] = face->Subdivide();
+                vertices[3] = prevedge->Subdivide();
+            } else {
+                vertices[i] = vertex->Subdivide();
+                vertices[(i+1)%4] = edge->Subdivide();
+                vertices[(i+2)%4] = face->Subdivide();
+                vertices[(i+3)%4] = prevedge->Subdivide();
+            }
+            child = mesh->NewFace(4, vertices, face, i);
 #ifdef HBR_DEBUG
-	    std::cerr << "Creating face " << *child << " during refine\n";
+            std::cerr << "Creating face " << *child << " during refine\n";
 #endif
 
-	    // Hand down edge sharpnesses
+            // Hand down edge sharpnesses
             childedge = vertex->Subdivide()->GetEdge(edge->Subdivide());
             assert(childedge);
-	    if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		SubdivideCreaseWeight(edge, edge->GetDestVertex(), childedge);
-	    }
+            if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                HbrSubdivision::SubdivideCreaseWeight(edge, edge->GetDestVertex(), childedge);
+            }
             childedge->CopyFVarInfiniteSharpness(edge);
 
             childedge = prevedge->Subdivide()->GetEdge(vertex->Subdivide());
             assert(childedge);
-	    if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		SubdivideCreaseWeight(prevedge, prevedge->GetOrgVertex(), childedge);
-	    }
+            if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                HbrSubdivision::SubdivideCreaseWeight(prevedge, prevedge->GetOrgVertex(), childedge);
+            }
             childedge->CopyFVarInfiniteSharpness(prevedge);
 
-	    if (mesh->GetTotalFVarWidth()) {
-		transferFVarToChild(mesh, face, child, i);
-	    }
+            if (mesh->GetTotalFVarWidth()) {
+                transferFVarToChild(mesh, face, child, i);
+            }
 
-	    transferEditsToChild(face, child, i);
-	}
-	prevedge = edge;
-	edge = edge->GetNext();
+            // Special handling of ptex index for extraordinary faces: make
+            // sure the children get their indices reassigned to be
+            // consecutive within the block reserved for the parent.
+            if (face->GetNumVertices() != 4 && face->GetPtexIndex() != -1) {
+                child->SetPtexIndex(face->GetPtexIndex() + i);
+            }
+
+            transferEditsToChild(face, child, i);
+        }
+        prevedge = edge;
+        edge = edge->GetNext();
     }
 }
 
@@ -518,72 +519,79 @@ HbrBilinearSubdivision::RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face
 #ifdef HBR_DEBUG
     std::cerr << "    forcing refine on " << *face << " at " << *vertex << '\n';
 #endif
-    
+
     // Create new quadrilateral children faces from this face
     HbrHalfedge* edge = face->GetFirstEdge();
     HbrHalfedge* prevedge = edge->GetPrev();
     HbrHalfedge* childedge;
     int nv = face->GetNumVertices();
-    float sharpness;    
+    float sharpness;
     bool extraordinary = (nv != 4);
     // The funny indexing on vertices is done only for
     // non-extraordinary faces in order to correctly preserve
     // parametric space through the refinement. If we split an
     // extraordinary face then it doesn't matter.
     for (int i = 0; i < nv; ++i) {
-	if (edge->GetOrgVertex() == vertex) {
-	    if (!face->GetChild(i)) {
-		HbrFace* child;
-		HbrVertex* vertices[4];
-		if (extraordinary) {
-		    vertices[0] = vertex->Subdivide();
-		    vertices[1] = edge->Subdivide();
-		    vertices[2] = face->Subdivide();
-		    vertices[3] = prevedge->Subdivide();
-		} else {
-		    vertices[i] = vertex->Subdivide();
-		    vertices[(i+1)%4] = edge->Subdivide();
-		    vertices[(i+2)%4] = face->Subdivide();
-		    vertices[(i+3)%4] = prevedge->Subdivide();
-		}
+        if (edge->GetOrgVertex() == vertex) {
+            if (!face->GetChild(i)) {
+                HbrFace* child;
+                HbrVertex* vertices[4];
+                if (extraordinary) {
+                    vertices[0] = vertex->Subdivide();
+                    vertices[1] = edge->Subdivide();
+                    vertices[2] = face->Subdivide();
+                    vertices[3] = prevedge->Subdivide();
+                } else {
+                    vertices[i] = vertex->Subdivide();
+                    vertices[(i+1)%4] = edge->Subdivide();
+                    vertices[(i+2)%4] = face->Subdivide();
+                    vertices[(i+3)%4] = prevedge->Subdivide();
+                }
 #ifdef HBR_DEBUG
-		std::cerr << "Kid " << i << "\n";
-		std::cerr << "  subdivision created " << *vertices[0] << '\n';
-		std::cerr << "  subdivision created " << *vertices[1] << '\n';
-		std::cerr << "  subdivision created " << *vertices[2] << '\n';
-		std::cerr << "  subdivision created " << *vertices[3] << '\n';
+                std::cerr << "Kid " << i << "\n";
+                std::cerr << "  subdivision created " << *vertices[0] << '\n';
+                std::cerr << "  subdivision created " << *vertices[1] << '\n';
+                std::cerr << "  subdivision created " << *vertices[2] << '\n';
+                std::cerr << "  subdivision created " << *vertices[3] << '\n';
 #endif
-		child = mesh->NewFace(4, vertices, face, i);
+                child = mesh->NewFace(4, vertices, face, i);
 #ifdef HBR_DEBUG
-		std::cerr << "Creating face " << *child << " during refine\n";
+                std::cerr << "Creating face " << *child << " during refine\n";
 #endif
-		// Hand down edge sharpness
+                // Hand down edge sharpness
                 childedge = vertex->Subdivide()->GetEdge(edge->Subdivide());
                 assert(childedge);
-		if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		    SubdivideCreaseWeight(edge, edge->GetDestVertex(), childedge);
-		}
+                if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                    HbrSubdivision::SubdivideCreaseWeight(edge, edge->GetDestVertex(), childedge);
+                }
                 childedge->CopyFVarInfiniteSharpness(edge);
 
                 childedge = prevedge->Subdivide()->GetEdge(vertex->Subdivide());
                 assert(childedge);
-		if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		    SubdivideCreaseWeight(prevedge, prevedge->GetOrgVertex(), childedge);
-		}
+                if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                    HbrSubdivision::SubdivideCreaseWeight(prevedge, prevedge->GetOrgVertex(), childedge);
+                }
                 childedge->CopyFVarInfiniteSharpness(prevedge);
 
-		if (mesh->GetTotalFVarWidth()) {
-		    transferFVarToChild(mesh, face, child, i);		
-		}
+                if (mesh->GetTotalFVarWidth()) {
+                    transferFVarToChild(mesh, face, child, i);
+                }
 
-		transferEditsToChild(face, child, i);
-		return child;
-	    } else {
-		return face->GetChild(i);
-	    }
-	}
-	prevedge = edge;
-	edge = edge->GetNext();
+                // Special handling of ptex index for extraordinary faces: make
+                // sure the children get their indices reassigned to be
+                // consecutive within the block reserved for the parent.
+                if (face->GetNumVertices() != 4 && face->GetPtexIndex() != -1) {
+                    child->SetPtexIndex(face->GetPtexIndex() + i);
+                }
+
+                transferEditsToChild(face, child, i);
+                return child;
+            } else {
+                return face->GetChild(i);
+            }
+        }
+        prevedge = edge;
+        edge = edge->GetNext();
     }
     return 0;
 }
@@ -592,7 +600,7 @@ template 
 void
 HbrBilinearSubdivision::GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge) {
     if (edge->GetOpposite()) {
-	return;
+        return;
     }
 
     // For the given edge: if the parent of either of its incident
@@ -604,83 +612,83 @@ HbrBilinearSubdivision::GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* e
     HbrFace* parentFace = edge->GetOrgVertex()->GetParentFace();
     HbrHalfedge* parentEdge = edge->GetDestVertex()->GetParentEdge();
     if (!parentFace) {
-	destParentWasEdge = false;
-	parentFace = edge->GetDestVertex()->GetParentFace();
-	parentEdge = edge->GetOrgVertex()->GetParentEdge();
+        destParentWasEdge = false;
+        parentFace = edge->GetDestVertex()->GetParentFace();
+        parentEdge = edge->GetOrgVertex()->GetParentEdge();
     }
-	
+
     if (parentFace) {
 
-	// Make sure we deal with a parent halfedge which is
-	// associated with the parent face
-	if (parentEdge->GetFace() != parentFace) {
-	    parentEdge = parentEdge->GetOpposite();
-	}
-	// If one of the vertices had a parent face, the other one MUST
-	// have been a child of an edge
-	assert(parentEdge && parentEdge->GetFace() == parentFace);
+        // Make sure we deal with a parent halfedge which is
+        // associated with the parent face
+        if (parentEdge->GetFace() != parentFace) {
+            parentEdge = parentEdge->GetOpposite();
+        }
+        // If one of the vertices had a parent face, the other one MUST
+        // have been a child of an edge
+        assert(parentEdge && parentEdge->GetFace() == parentFace);
 #ifdef HBR_DEBUG
-	std::cerr << "\nparent edge is " << *parentEdge << "\n";
+        std::cerr << "\nparent edge is " << *parentEdge << "\n";
 #endif
 
-	// The vertex to refine at depends on whether the
-	// destination or origin vertex of this edge had a parent
-	// edge
-	if (destParentWasEdge) {
-	    RefineFaceAtVertex(mesh, parentFace, parentEdge->GetOrgVertex());
-	} else {
-	    RefineFaceAtVertex(mesh, parentFace, parentEdge->GetDestVertex());
-	}
+        // The vertex to refine at depends on whether the
+        // destination or origin vertex of this edge had a parent
+        // edge
+        if (destParentWasEdge) {
+            RefineFaceAtVertex(mesh, parentFace, parentEdge->GetOrgVertex());
+        } else {
+            RefineFaceAtVertex(mesh, parentFace, parentEdge->GetDestVertex());
+        }
 
-	// It should always be the case that the opposite now exists -
-	// we can't have a boundary case here
-	assert(edge->GetOpposite());
+        // It should always be the case that the opposite now exists -
+        // we can't have a boundary case here
+        assert(edge->GetOpposite());
     } else {
-	HbrVertex* parentVertex = edge->GetOrgVertex()->GetParentVertex();
-	parentEdge = edge->GetDestVertex()->GetParentEdge();
-	if (!parentVertex) {
-	    parentVertex = edge->GetDestVertex()->GetParentVertex();
-	    parentEdge = edge->GetOrgVertex()->GetParentEdge();
-	}
+        HbrVertex* parentVertex = edge->GetOrgVertex()->GetParentVertex();
+        parentEdge = edge->GetDestVertex()->GetParentEdge();
+        if (!parentVertex) {
+            parentVertex = edge->GetDestVertex()->GetParentVertex();
+            parentEdge = edge->GetOrgVertex()->GetParentEdge();
+        }
 
-	if (parentVertex) {
-		
-	    assert(parentEdge);
+        if (parentVertex) {
+
+            assert(parentEdge);
 
 #ifdef HBR_DEBUG
-	    std::cerr << "\nparent edge is " << *parentEdge << "\n";
+            std::cerr << "\nparent edge is " << *parentEdge << "\n";
 #endif
-		
-	    // 1. Go up to the parent of my face
 
-	    parentFace = edge->GetFace()->GetParent();
+            // 1. Go up to the parent of my face
+
+            parentFace = edge->GetFace()->GetParent();
 #ifdef HBR_DEBUG
-	    std::cerr << "\nparent face is " << *parentFace << "\n";
+            std::cerr << "\nparent face is " << *parentFace << "\n";
 #endif
 
-	    // 2. Ask the opposite face (if it exists) to refine
-	    if (parentFace) {
+            // 2. Ask the opposite face (if it exists) to refine
+            if (parentFace) {
 
-		// A vertex can be associated with either of two
-		// parent halfedges. If the parent edge that we're
-		// interested in doesn't match then we should look at
-		// its opposite
-		if (parentEdge->GetFace() != parentFace)
-		    parentEdge = parentEdge->GetOpposite();
-		assert(parentEdge->GetFace() == parentFace);
+                // A vertex can be associated with either of two
+                // parent halfedges. If the parent edge that we're
+                // interested in doesn't match then we should look at
+                // its opposite
+                if (parentEdge->GetFace() != parentFace)
+                    parentEdge = parentEdge->GetOpposite();
+                assert(parentEdge->GetFace() == parentFace);
 
-		// Make sure the parent edge has its neighbor as well
-		GuaranteeNeighbor(mesh, parentEdge);
+                // Make sure the parent edge has its neighbor as well
+                GuaranteeNeighbor(mesh, parentEdge);
 
-		// Now access that neighbor and refine it
-		if (parentEdge->GetRightFace()) {
-		    RefineFaceAtVertex(mesh, parentEdge->GetRightFace(), parentVertex);
+                // Now access that neighbor and refine it
+                if (parentEdge->GetRightFace()) {
+                    RefineFaceAtVertex(mesh, parentEdge->GetRightFace(), parentVertex);
 
-		    // FIXME: assertion?
-		    assert(edge->GetOpposite());
-		}
-	    }
-	}
+                    // FIXME: assertion?
+                    assert(edge->GetOpposite());
+                }
+            }
+        }
     }
 }
 
@@ -699,10 +707,10 @@ HbrBilinearSubdivision::GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* ve
     if (parentFace) {
 
 #ifdef HBR_DEBUG
-	std::cerr << "  forcing full refine on parent face\n";
+        std::cerr << "  forcing full refine on parent face\n";
 #endif
-	Refine(mesh, parentFace);
-	return;
+        Refine(mesh, parentFace);
+        return;
     }
 
     // Otherwise if the vertex is a child of an edge, we need to
@@ -713,29 +721,29 @@ HbrBilinearSubdivision::GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* ve
     if (parentEdge) {
 
 #ifdef HBR_DEBUG
-	std::cerr << "  forcing full refine on adjacent faces of parent edge\n";
+        std::cerr << "  forcing full refine on adjacent faces of parent edge\n";
 #endif
-	HbrVertex* dest = parentEdge->GetDestVertex();
-	HbrVertex* org = parentEdge->GetOrgVertex();
-	GuaranteeNeighbor(mesh, parentEdge);
-	parentFace = parentEdge->GetLeftFace();
-	RefineFaceAtVertex(mesh, parentFace, dest);
-	RefineFaceAtVertex(mesh, parentFace, org);
+        HbrVertex* dest = parentEdge->GetDestVertex();
+        HbrVertex* org = parentEdge->GetOrgVertex();
+        GuaranteeNeighbor(mesh, parentEdge);
+        parentFace = parentEdge->GetLeftFace();
+        RefineFaceAtVertex(mesh, parentFace, dest);
+        RefineFaceAtVertex(mesh, parentFace, org);
 
 #ifdef HBR_DEBUG
-	std::cerr << "    on the right face?\n";
+        std::cerr << "    on the right face?\n";
 #endif
-	parentFace = parentEdge->GetRightFace();
-	// The right face may not necessarily exist even after
-	// GuaranteeNeighbor
-	if (parentFace) {
-	    RefineFaceAtVertex(mesh, parentFace, dest);
-	    RefineFaceAtVertex(mesh, parentFace, org);
-	}
+        parentFace = parentEdge->GetRightFace();
+        // The right face may not necessarily exist even after
+        // GuaranteeNeighbor
+        if (parentFace) {
+            RefineFaceAtVertex(mesh, parentFace, dest);
+            RefineFaceAtVertex(mesh, parentFace, org);
+        }
 #ifdef HBR_DEBUG
-	std::cerr << "  end force\n";
+        std::cerr << "  end force\n";
 #endif
-	return;
+        return;
     }
 
     // The last case: the vertex is a child of a vertex. In this case
@@ -745,20 +753,20 @@ HbrBilinearSubdivision::GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* ve
     if (parentVertex) {
 
 #ifdef HBR_DEBUG
-	std::cerr << "  recursive parent vertex guarantee call\n";
+        std::cerr << "  recursive parent vertex guarantee call\n";
 #endif
-	parentVertex->GuaranteeNeighbors();
-	
-	// And then we refine all the face neighbors of the
-	// parentVertex
-	HbrHalfedge* start = parentVertex->GetIncidentEdge(), *edge;
-	edge = start;
-	while (edge) {
-	    HbrFace* f = edge->GetLeftFace();
-	    RefineFaceAtVertex(mesh, f, parentVertex);
-	    edge = parentVertex->GetNextEdge(edge);
-	    if (edge == start) break;	    
-	}
+        parentVertex->GuaranteeNeighbors();
+
+        // And then we refine all the face neighbors of the
+        // parentVertex
+        HbrHalfedge* start = parentVertex->GetIncidentEdge(), *edge;
+        edge = start;
+        while (edge) {
+            HbrFace* f = edge->GetLeftFace();
+            RefineFaceAtVertex(mesh, f, parentVertex);
+            edge = parentVertex->GetNextEdge(edge);
+            if (edge == start) break;
+        }
     }
 }
 
@@ -769,9 +777,9 @@ HbrBilinearSubdivision::HasLimit(HbrMesh* mesh, HbrFace* face) {
     if (face->IsHole()) return false;
     // A limit face exists if all the bounding edges have limit curves
     for (int i = 0; i < face->GetNumVertices(); ++i) {
-	if (!HasLimit(mesh, face->GetEdge(i))) {
-	    return false;
-	}
+        if (!HasLimit(mesh, face->GetEdge(i))) {
+            return false;
+        }
     }
     return true;
 }
@@ -787,14 +795,14 @@ bool
 HbrBilinearSubdivision::HasLimit(HbrMesh* /* mesh */, HbrVertex* vertex) {
     vertex->GuaranteeNeighbors();
     switch (vertex->GetMask(false)) {
-	case HbrVertex::k_Smooth:
-	case HbrVertex::k_Dart:
-	    return !vertex->OnBoundary();
-	    break;
-	case HbrVertex::k_Crease:
-	case HbrVertex::k_Corner:
-	default:
-	    return true;
+        case HbrVertex::k_Smooth:
+        case HbrVertex::k_Dart:
+            return !vertex->OnBoundary();
+            break;
+        case HbrVertex::k_Crease:
+        case HbrVertex::k_Corner:
+        default:
+            return true;
     }
 }
 
@@ -803,22 +811,22 @@ HbrVertex*
 HbrBilinearSubdivision::Subdivide(HbrMesh* mesh, HbrFace* face) {
 
     // Face rule: simply average all vertices on the face
-    HbrVertex* v = mesh->NewVertex();    
+    HbrVertex* v = mesh->NewVertex();
     T& data = v->GetData();
     int nv = face->GetNumVertices();
     float weight = 1.0f / nv;
 
     HbrHalfedge* edge = face->GetFirstEdge();
     for (int i = 0; i < face->GetNumVertices(); ++i) {
-	HbrVertex* w = edge->GetOrgVertex();
-	// If there are vertex edits we have to make sure the edit
-	// has been applied
-	if (mesh->HasVertexEdits()) {
-	    w->GuaranteeNeighbors();
-	}
-	data.AddWithWeight(w->GetData(), weight);
-	data.AddVaryingWithWeight(w->GetData(), weight);
-	edge = edge->GetNext();
+        HbrVertex* w = edge->GetOrgVertex();
+        // If there are vertex edits we have to make sure the edit
+        // has been applied
+        if (mesh->HasVertexEdits()) {
+            w->GuaranteeNeighbors();
+        }
+        data.AddWithWeight(w->GetData(), weight);
+        data.AddVaryingWithWeight(w->GetData(), weight);
+        edge = edge->GetNext();
     }
 #ifdef HBR_DEBUG
     std::cerr << "Subdividing at " << *face << "\n";
@@ -827,7 +835,7 @@ HbrBilinearSubdivision::Subdivide(HbrMesh* mesh, HbrFace* face) {
     // Set the extraordinary flag if the face had anything other than
     // 4 vertices
     if (nv != 4) v->SetExtraordinary();
-    
+
 #ifdef HBR_DEBUG
     std::cerr << "  created " << *v << "\n";
 #endif
@@ -842,7 +850,7 @@ HbrBilinearSubdivision::Subdivide(HbrMesh* mesh, HbrHalfedge* edge) {
     float esharp = edge->GetSharpness();
     std::cerr << "Subdividing at " << *edge << " (sharpness = " << esharp << ")";
 #endif
-    
+
     HbrVertex* v = mesh->NewVertex();
     T& data = v->GetData();
 
@@ -852,14 +860,14 @@ HbrBilinearSubdivision::Subdivide(HbrMesh* mesh, HbrHalfedge* edge) {
     if (mesh->HasCreaseEdits()) {
         edge->GuaranteeNeighbor();
     }
-    
+
     // If there's the possibility of vertex edits on either vertex, we
     // have to make sure the edit has been applied
     if (mesh->HasVertexEdits()) {
-	edge->GetOrgVertex()->GuaranteeNeighbors();
-	edge->GetDestVertex()->GuaranteeNeighbors();
+        edge->GetOrgVertex()->GuaranteeNeighbors();
+        edge->GetDestVertex()->GuaranteeNeighbors();
     }
-    
+
     // Average the two end points
     data.AddWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
     data.AddWithWeight(edge->GetDestVertex()->GetData(), 0.5f);
@@ -879,7 +887,7 @@ HbrVertex*
 HbrBilinearSubdivision::Subdivide(HbrMesh* mesh, HbrVertex* vertex) {
 
     HbrVertex* v;
-    
+
     // If there are vertex edits we have to make sure the edit has
     // been applied by guaranteeing the neighbors of the
     // vertex. Unfortunately in this case, we can't share the data
@@ -889,37 +897,37 @@ HbrBilinearSubdivision::Subdivide(HbrMesh* mesh, HbrVertex* vertex) {
 
         v = mesh->NewVertex();
         T& data = v->GetData();
-        
+
         // Just copy the old value
         data.AddWithWeight(vertex->GetData(), 1.0f);
-        
-        // Varying data is always just propogated down
+
+        // Varying data is always just propagated down
         data.AddVaryingWithWeight(vertex->GetData(), 1.0f);
-        
+
     } else {
         // Create a new vertex that just shares the same data
         v = mesh->NewVertex(vertex->GetData());
     }
-    
+
 #ifdef HBR_DEBUG
-    std::cerr << "Subdividing at " << *vertex << "\n";    
+    std::cerr << "Subdividing at " << *vertex << "\n";
     std::cerr << "  created " << *v << "\n";
 #endif
     // Inherit extraordinary flag and sharpness
     if (vertex->IsExtraordinary()) v->SetExtraordinary();
     float sharp = vertex->GetSharpness();
     if (sharp >= HbrVertex::k_InfinitelySharp) {
-	v->SetSharpness(HbrVertex::k_InfinitelySharp);
+        v->SetSharpness(HbrVertex::k_InfinitelySharp);
     } else if (sharp > HbrVertex::k_Smooth) {
-	sharp -= 1.0f;
-	if (sharp < (float) HbrVertex::k_Smooth) {
-	    sharp = (float) HbrVertex::k_Smooth;
-	}
-	v->SetSharpness(sharp);
+        sharp -= 1.0f;
+        if (sharp < (float) HbrVertex::k_Smooth) {
+            sharp = (float) HbrVertex::k_Smooth;
+        }
+        v->SetSharpness(sharp);
     } else {
-	v->SetSharpness(HbrVertex::k_Smooth);
+        v->SetSharpness(HbrVertex::k_Smooth);
     }
-    return v;    
+    return v;
 }
 
 } // end namespace OPENSUBDIV_VERSION
diff --git a/opensubdiv/hbr/catmark.h b/opensubdiv/hbr/catmark.h
index 9644309a..feaead00 100644
--- a/opensubdiv/hbr/catmark.h
+++ b/opensubdiv/hbr/catmark.h
@@ -69,29 +69,30 @@ template 
 class HbrCatmarkSubdivision : public HbrSubdivision {
 public:
     HbrCatmarkSubdivision()
-	: HbrSubdivision(), triangleSubdivision(k_Normal) {}
+        : HbrSubdivision(), triangleSubdivision(k_Normal) {}
 
     HbrCatmarkSubdivision(const HbrCatmarkSubdivision &old)
-	: HbrSubdivision(), triangleSubdivision(old.triangleSubdivision) {}    
+        : HbrSubdivision(), triangleSubdivision(old.triangleSubdivision) {}
 
     virtual HbrSubdivision* Clone() const {
         return new HbrCatmarkSubdivision(*this);
     }
 
     virtual void Refine(HbrMesh* mesh, HbrFace* face);
-    virtual HbrFace* RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face, HbrVertex* vertex);    
+    virtual HbrFace* RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face, HbrVertex* vertex);
     virtual void GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge);
     virtual void GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* vertex);
 
     virtual bool HasLimit(HbrMesh* mesh, HbrFace* face);
     virtual bool HasLimit(HbrMesh* mesh, HbrHalfedge* edge);
     virtual bool HasLimit(HbrMesh* mesh, HbrVertex* vertex);
-    
+
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrFace* face);
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrHalfedge* edge);
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrVertex* vertex);
 
     virtual bool VertexIsExtraordinary(HbrMesh* /* mesh */, HbrVertex* vertex) { return vertex->GetValence() != 4; }
+    virtual bool FaceIsExtraordinary(HbrMesh* /* mesh */, HbrFace* face) { return face->GetNumVertices() != 4; }
 
     // Triangle subdivision rules, which modifies the rules for
     // triangular faces in order to make them smoother. The "normal"
@@ -103,15 +104,15 @@ public:
     // triangular; after one level of refinement everything becomes
     // quads.
     enum TriangleSubdivision {
-	k_Normal,
-	k_Old,
-	k_New
+        k_Normal,
+        k_Old,
+        k_New
     };
     TriangleSubdivision GetTriangleSubdivisionMethod() const { return triangleSubdivision; }
     void SetTriangleSubdivisionMethod(TriangleSubdivision method) { triangleSubdivision = method; }
 
     virtual int GetFaceChildrenCount(int nvertices) const { return nvertices; }
-    
+
 private:
 
     // Transfers facevarying data from a parent face to a child face
@@ -119,7 +120,7 @@ private:
 
     // Transfers vertex and edge edits from a parent face to a child face
     void transferEditsToChild(HbrFace* face, HbrFace* child, int index);
-    
+
     TriangleSubdivision triangleSubdivision;
 };
 
@@ -164,14 +165,14 @@ HbrCatmarkSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face
     // allocate a new block of facevarying storage specific to the
     // child face.
     bool fv0IsSmooth, fv1IsSmooth, fv3IsSmooth;
-    
+
     childVertex = child->GetVertex(extraordinary ? 0 : (index+0)%4);
     fv0IsSmooth = v->IsFVarAllSmooth();
     if (!fv0IsSmooth) {
         childVertex->NewFVarData(child);
     }
     HbrFVarData& fv0 = childVertex->GetFVarData(child);
-    
+
     edge = face->GetEdge(index);
     GuaranteeNeighbor(mesh, edge);
     assert(edge->GetOrgVertex() == v);
@@ -183,7 +184,7 @@ HbrCatmarkSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face
     HbrFVarData& fv1 = childVertex->GetFVarData(child);
 
     edge = edge->GetPrev();
-    GuaranteeNeighbor(mesh, edge);    
+    GuaranteeNeighbor(mesh, edge);
     assert(edge == face->GetEdge((index + nv - 1) % nv));
     assert(edge->GetDestVertex() == v);
     childVertex = child->GetVertex(extraordinary ? 3 : (index+3)%4);
@@ -335,7 +336,7 @@ HbrCatmarkSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face
             starte = bestedge;
             w = 0;
             if (HbrHalfedge* e = starte) {
-                assert(starte->GetOrgVertex() == v);	
+                assert(starte->GetOrgVertex() == v);
                 do {
                     if (e->GetFVarSharpness(fvaritem) || !e->GetRightFace()) {
                         bestface = e->GetLeftFace();
@@ -352,7 +353,7 @@ HbrCatmarkSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face
             }
             assert(j != bestface->GetNumVertices());
             fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
-	
+
         }
         // Smooth rule. Here, we can take a shortcut if we know that
         // the vertex is smooth and some other vertex has completely
@@ -409,7 +410,7 @@ HbrCatmarkSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face
                 fv1.AddWithWeight(oppFace->GetFVarData(j), fvarindex, fvarwidth, weight);
             }
         }
-		
+
 
         // Edge subdivision rule
         edge = edge->GetPrev();
@@ -417,11 +418,11 @@ HbrCatmarkSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face
         if (fvarinterp == HbrMesh::k_InterpolateBoundaryNone ||
             edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {
 
-            // Sharp edge rule						 
+            // Sharp edge rule
             fv3.SetWithWeight(face->GetFVarData((index + nv - 1) % nv), fvarindex, fvarwidth, 0.5f);
             fv3.AddWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.5f);
         } else if (!fv3IsSmooth || !fv3.IsInitialized()) {
-            // Smooth edge subdivision. Add 0.25 of adjacent vertices	
+            // Smooth edge subdivision. Add 0.25 of adjacent vertices
             fv3.SetWithWeight(face->GetFVarData((index + nv - 1) % nv), fvarindex, fvarwidth, 0.25f);
             fv3.AddWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.25f);
             // Local subdivided face vertex
@@ -440,12 +441,6 @@ HbrCatmarkSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face
     fv1.SetInitialized();
     fv3.SetInitialized();
 
-    // Special handling of ptex index for extraordinary faces: make
-    // sure the children get their indices reassigned to be
-    // consecutive within the block reserved for the parent.
-    if (face->GetNumVertices() != 4 && face->GetPtexIndex() != -1) {
-        child->SetPtexIndex(face->GetPtexIndex() + index);
-    }
 }
 
 template 
@@ -454,15 +449,15 @@ HbrCatmarkSubdivision::transferEditsToChild(HbrFace* face, HbrFace* chi
 
     // Hand down pointers to hierarchical edits
     if (HbrHierarchicalEdit** edits = face->GetHierarchicalEdits()) {
-	while (HbrHierarchicalEdit* edit = *edits) {
-	    if (!edit->IsRelevantToFace(face)) break;
-	    if (edit->GetNSubfaces() > face->GetDepth() &&
-		(edit->GetSubface(face->GetDepth()) == index)) {
-		child->SetHierarchicalEdits(edits);
-		break;
-	    }
-	    edits++;
-	}
+        while (HbrHierarchicalEdit* edit = *edits) {
+            if (!edit->IsRelevantToFace(face)) break;
+            if (edit->GetNSubfaces() > face->GetDepth() &&
+                (edit->GetSubface(face->GetDepth()) == index)) {
+                child->SetHierarchicalEdits(edits);
+                break;
+            }
+            edits++;
+        }
     }
 }
 
@@ -485,52 +480,59 @@ HbrCatmarkSubdivision::Refine(HbrMesh* mesh, HbrFace* face) {
     // parametric space through the refinement. If we split an
     // extraordinary face then it doesn't matter.
     for (int i = 0; i < nv; ++i) {
-	if (!face->GetChild(i)) {
+        if (!face->GetChild(i)) {
 #ifdef HBR_DEBUG
-	    std::cerr << "Kid " << i << "\n";
+            std::cerr << "Kid " << i << "\n";
 #endif
-	    HbrVertex* vertex = edge->GetOrgVertex();
-	    if (extraordinary) {
-		vertices[0] = vertex->Subdivide();
-		vertices[1] = edge->Subdivide();
-		vertices[2] = face->Subdivide();
-		vertices[3] = prevedge->Subdivide();
-	    } else {
-		vertices[i] = vertex->Subdivide();
-		vertices[(i+1)%4] = edge->Subdivide();
-		vertices[(i+2)%4] = face->Subdivide();
-		vertices[(i+3)%4] = prevedge->Subdivide();
-	    }
-	    child = mesh->NewFace(4, vertices, face, i);
+            HbrVertex* vertex = edge->GetOrgVertex();
+            if (extraordinary) {
+                vertices[0] = vertex->Subdivide();
+                vertices[1] = edge->Subdivide();
+                vertices[2] = face->Subdivide();
+                vertices[3] = prevedge->Subdivide();
+            } else {
+                vertices[i] = vertex->Subdivide();
+                vertices[(i+1)%4] = edge->Subdivide();
+                vertices[(i+2)%4] = face->Subdivide();
+                vertices[(i+3)%4] = prevedge->Subdivide();
+            }
+            child = mesh->NewFace(4, vertices, face, i);
 #ifdef HBR_DEBUG
-	    std::cerr << "Creating face " << *child << " during refine\n";
+            std::cerr << "Creating face " << *child << " during refine\n";
 #endif
 
-	    // Hand down edge sharpnesses
+            // Hand down edge sharpnesses
             childedge = vertex->Subdivide()->GetEdge(edge->Subdivide());
             assert(childedge);
-	    if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		HbrSubdivision::SubdivideCreaseWeight(
+            if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                HbrSubdivision::SubdivideCreaseWeight(
                     edge, edge->GetDestVertex(), childedge);
-	    }
+            }
             childedge->CopyFVarInfiniteSharpness(edge);
 
             childedge = prevedge->Subdivide()->GetEdge(vertex->Subdivide());
             assert(childedge);
-	    if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		HbrSubdivision::SubdivideCreaseWeight(
+            if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                HbrSubdivision::SubdivideCreaseWeight(
                     prevedge, prevedge->GetOrgVertex(), childedge);
-	    }
+            }
             childedge->CopyFVarInfiniteSharpness(prevedge);
 
-	    if (mesh->GetTotalFVarWidth()) {
-		transferFVarToChild(mesh, face, child, i);
-	    }
+            if (mesh->GetTotalFVarWidth()) {
+                transferFVarToChild(mesh, face, child, i);
+            }
 
-	    transferEditsToChild(face, child, i);
-	}
-	prevedge = edge;
-	edge = edge->GetNext();
+            // Special handling of ptex index for extraordinary faces: make
+            // sure the children get their indices reassigned to be
+            // consecutive within the block reserved for the parent.
+            if (face->GetNumVertices() != 4 && face->GetPtexIndex() != -1) {
+                child->SetPtexIndex(face->GetPtexIndex() + i);
+            }
+
+            transferEditsToChild(face, child, i);
+        }
+        prevedge = edge;
+        edge = edge->GetNext();
     }
 }
 
@@ -541,74 +543,81 @@ HbrCatmarkSubdivision::RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face,
 #ifdef HBR_DEBUG
     std::cerr << "    forcing refine on " << *face << " at " << *vertex << '\n';
 #endif
-    
+
     // Create new quadrilateral children faces from this face
     HbrHalfedge* edge = face->GetFirstEdge();
     HbrHalfedge* prevedge = edge->GetPrev();
     HbrHalfedge* childedge;
     int nv = face->GetNumVertices();
-    float sharpness;    
+    float sharpness;
     bool extraordinary = (nv != 4);
     // The funny indexing on vertices is done only for
     // non-extraordinary faces in order to correctly preserve
     // parametric space through the refinement. If we split an
     // extraordinary face then it doesn't matter.
     for (int i = 0; i < nv; ++i) {
-	if (edge->GetOrgVertex() == vertex) {
-	    if (!face->GetChild(i)) {
-		HbrFace* child;
-		HbrVertex* vertices[4];
-		if (extraordinary) {
-		    vertices[0] = vertex->Subdivide();
-		    vertices[1] = edge->Subdivide();
-		    vertices[2] = face->Subdivide();
-		    vertices[3] = prevedge->Subdivide();
-		} else {
-		    vertices[i] = vertex->Subdivide();
-		    vertices[(i+1)%4] = edge->Subdivide();
-		    vertices[(i+2)%4] = face->Subdivide();
-		    vertices[(i+3)%4] = prevedge->Subdivide();
-		}
+        if (edge->GetOrgVertex() == vertex) {
+            if (!face->GetChild(i)) {
+                HbrFace* child;
+                HbrVertex* vertices[4];
+                if (extraordinary) {
+                    vertices[0] = vertex->Subdivide();
+                    vertices[1] = edge->Subdivide();
+                    vertices[2] = face->Subdivide();
+                    vertices[3] = prevedge->Subdivide();
+                } else {
+                    vertices[i] = vertex->Subdivide();
+                    vertices[(i+1)%4] = edge->Subdivide();
+                    vertices[(i+2)%4] = face->Subdivide();
+                    vertices[(i+3)%4] = prevedge->Subdivide();
+                }
 #ifdef HBR_DEBUG
-		std::cerr << "Kid " << i << "\n";
-		std::cerr << "  subdivision created " << *vertices[0] << '\n';
-		std::cerr << "  subdivision created " << *vertices[1] << '\n';
-		std::cerr << "  subdivision created " << *vertices[2] << '\n';
-		std::cerr << "  subdivision created " << *vertices[3] << '\n';
+                std::cerr << "Kid " << i << "\n";
+                std::cerr << "  subdivision created " << *vertices[0] << '\n';
+                std::cerr << "  subdivision created " << *vertices[1] << '\n';
+                std::cerr << "  subdivision created " << *vertices[2] << '\n';
+                std::cerr << "  subdivision created " << *vertices[3] << '\n';
 #endif
-		child = mesh->NewFace(4, vertices, face, i);
+                child = mesh->NewFace(4, vertices, face, i);
 #ifdef HBR_DEBUG
-		std::cerr << "Creating face " << *child << " during refine\n";
+                std::cerr << "Creating face " << *child << " during refine\n";
 #endif
-		// Hand down edge sharpness
+                // Hand down edge sharpness
                 childedge = vertex->Subdivide()->GetEdge(edge->Subdivide());
                 assert(childedge);
-		if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		    HbrSubdivision::SubdivideCreaseWeight(
+                if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                    HbrSubdivision::SubdivideCreaseWeight(
                         edge, edge->GetDestVertex(), childedge);
-		}
+                }
                 childedge->CopyFVarInfiniteSharpness(edge);
 
                 childedge = prevedge->Subdivide()->GetEdge(vertex->Subdivide());
                 assert(childedge);
-		if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		    HbrSubdivision::SubdivideCreaseWeight(
+                if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                    HbrSubdivision::SubdivideCreaseWeight(
                         prevedge, prevedge->GetOrgVertex(), childedge);
-		}
+                }
                 childedge->CopyFVarInfiniteSharpness(prevedge);
 
-		if (mesh->GetTotalFVarWidth()) {
-		    transferFVarToChild(mesh, face, child, i);		
-		}
+                if (mesh->GetTotalFVarWidth()) {
+                    transferFVarToChild(mesh, face, child, i);
+                }
 
-		transferEditsToChild(face, child, i);
-		return child;
-	    } else {
-		return face->GetChild(i);
-	    }
-	}
-	prevedge = edge;
-	edge = edge->GetNext();
+                // Special handling of ptex index for extraordinary faces: make
+                // sure the children get their indices reassigned to be
+                // consecutive within the block reserved for the parent.
+                if (face->GetNumVertices() != 4 && face->GetPtexIndex() != -1) {
+                    child->SetPtexIndex(face->GetPtexIndex() + i);
+                }
+
+                transferEditsToChild(face, child, i);
+                return child;
+            } else {
+                return face->GetChild(i);
+            }
+        }
+        prevedge = edge;
+        edge = edge->GetNext();
     }
     return 0;
 }
@@ -617,7 +626,7 @@ template 
 void
 HbrCatmarkSubdivision::GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge) {
     if (edge->GetOpposite()) {
-	return;
+        return;
     }
 
     // For the given edge: if the parent of either of its incident
@@ -629,83 +638,83 @@ HbrCatmarkSubdivision::GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* ed
     HbrFace* parentFace = edge->GetOrgVertex()->GetParentFace();
     HbrHalfedge* parentEdge = edge->GetDestVertex()->GetParentEdge();
     if (!parentFace) {
-	destParentWasEdge = false;
-	parentFace = edge->GetDestVertex()->GetParentFace();
-	parentEdge = edge->GetOrgVertex()->GetParentEdge();
+        destParentWasEdge = false;
+        parentFace = edge->GetDestVertex()->GetParentFace();
+        parentEdge = edge->GetOrgVertex()->GetParentEdge();
     }
-	
+
     if (parentFace) {
 
-	// Make sure we deal with a parent halfedge which is
-	// associated with the parent face
-	if (parentEdge->GetFace() != parentFace) {
-	    parentEdge = parentEdge->GetOpposite();
-	}
-	// If one of the vertices had a parent face, the other one MUST
-	// have been a child of an edge
-	assert(parentEdge && parentEdge->GetFace() == parentFace);
+        // Make sure we deal with a parent halfedge which is
+        // associated with the parent face
+        if (parentEdge->GetFace() != parentFace) {
+            parentEdge = parentEdge->GetOpposite();
+        }
+        // If one of the vertices had a parent face, the other one MUST
+        // have been a child of an edge
+        assert(parentEdge && parentEdge->GetFace() == parentFace);
 #ifdef HBR_DEBUG
-	std::cerr << "\nparent edge is " << *parentEdge << "\n";
+        std::cerr << "\nparent edge is " << *parentEdge << "\n";
 #endif
 
-	// The vertex to refine at depends on whether the
-	// destination or origin vertex of this edge had a parent
-	// edge
-	if (destParentWasEdge) {
-	    RefineFaceAtVertex(mesh, parentFace, parentEdge->GetOrgVertex());
-	} else {
-	    RefineFaceAtVertex(mesh, parentFace, parentEdge->GetDestVertex());
-	}
+        // The vertex to refine at depends on whether the
+        // destination or origin vertex of this edge had a parent
+        // edge
+        if (destParentWasEdge) {
+            RefineFaceAtVertex(mesh, parentFace, parentEdge->GetOrgVertex());
+        } else {
+            RefineFaceAtVertex(mesh, parentFace, parentEdge->GetDestVertex());
+        }
 
-	// It should always be the case that the opposite now exists -
-	// we can't have a boundary case here
-	assert(edge->GetOpposite());
+        // It should always be the case that the opposite now exists -
+        // we can't have a boundary case here
+        assert(edge->GetOpposite());
     } else {
-	HbrVertex* parentVertex = edge->GetOrgVertex()->GetParentVertex();
-	parentEdge = edge->GetDestVertex()->GetParentEdge();
-	if (!parentVertex) {
-	    parentVertex = edge->GetDestVertex()->GetParentVertex();
-	    parentEdge = edge->GetOrgVertex()->GetParentEdge();
-	}
+        HbrVertex* parentVertex = edge->GetOrgVertex()->GetParentVertex();
+        parentEdge = edge->GetDestVertex()->GetParentEdge();
+        if (!parentVertex) {
+            parentVertex = edge->GetDestVertex()->GetParentVertex();
+            parentEdge = edge->GetOrgVertex()->GetParentEdge();
+        }
 
-	if (parentVertex) {
-		
-	    assert(parentEdge);
+        if (parentVertex) {
+
+            assert(parentEdge);
 
 #ifdef HBR_DEBUG
-	    std::cerr << "\nparent edge is " << *parentEdge << "\n";
+            std::cerr << "\nparent edge is " << *parentEdge << "\n";
 #endif
-		
-	    // 1. Go up to the parent of my face
 
-	    parentFace = edge->GetFace()->GetParent();
+            // 1. Go up to the parent of my face
+
+            parentFace = edge->GetFace()->GetParent();
 #ifdef HBR_DEBUG
-	    std::cerr << "\nparent face is " << *parentFace << "\n";
+            std::cerr << "\nparent face is " << *parentFace << "\n";
 #endif
 
-	    // 2. Ask the opposite face (if it exists) to refine
-	    if (parentFace) {
+            // 2. Ask the opposite face (if it exists) to refine
+            if (parentFace) {
 
-		// A vertex can be associated with either of two
-		// parent halfedges. If the parent edge that we're
-		// interested in doesn't match then we should look at
-		// its opposite
-		if (parentEdge->GetFace() != parentFace)
-		    parentEdge = parentEdge->GetOpposite();
-		assert(parentEdge->GetFace() == parentFace);
+                // A vertex can be associated with either of two
+                // parent halfedges. If the parent edge that we're
+                // interested in doesn't match then we should look at
+                // its opposite
+                if (parentEdge->GetFace() != parentFace)
+                    parentEdge = parentEdge->GetOpposite();
+                assert(parentEdge->GetFace() == parentFace);
 
-		// Make sure the parent edge has its neighbor as well
-		GuaranteeNeighbor(mesh, parentEdge);
+                // Make sure the parent edge has its neighbor as well
+                GuaranteeNeighbor(mesh, parentEdge);
 
-		// Now access that neighbor and refine it
-		if (parentEdge->GetRightFace()) {
-		    RefineFaceAtVertex(mesh, parentEdge->GetRightFace(), parentVertex);
+                // Now access that neighbor and refine it
+                if (parentEdge->GetRightFace()) {
+                    RefineFaceAtVertex(mesh, parentEdge->GetRightFace(), parentVertex);
 
-		    // FIXME: assertion?
-		    assert(edge->GetOpposite());
-		}
-	    }
-	}
+                    // FIXME: assertion?
+                    assert(edge->GetOpposite());
+                }
+            }
+        }
     }
 }
 
@@ -724,10 +733,10 @@ HbrCatmarkSubdivision::GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* ver
     if (parentFace) {
 
 #ifdef HBR_DEBUG
-	std::cerr << "  forcing full refine on parent face\n";
+        std::cerr << "  forcing full refine on parent face\n";
 #endif
-	Refine(mesh, parentFace);
-	return;
+        Refine(mesh, parentFace);
+        return;
     }
 
     // Otherwise if the vertex is a child of an edge, we need to
@@ -738,29 +747,29 @@ HbrCatmarkSubdivision::GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* ver
     if (parentEdge) {
 
 #ifdef HBR_DEBUG
-	std::cerr << "  forcing full refine on adjacent faces of parent edge\n";
+        std::cerr << "  forcing full refine on adjacent faces of parent edge\n";
 #endif
-	HbrVertex* dest = parentEdge->GetDestVertex();
-	HbrVertex* org = parentEdge->GetOrgVertex();
-	GuaranteeNeighbor(mesh, parentEdge);
-	parentFace = parentEdge->GetLeftFace();
-	RefineFaceAtVertex(mesh, parentFace, dest);
-	RefineFaceAtVertex(mesh, parentFace, org);
+        HbrVertex* dest = parentEdge->GetDestVertex();
+        HbrVertex* org = parentEdge->GetOrgVertex();
+        GuaranteeNeighbor(mesh, parentEdge);
+        parentFace = parentEdge->GetLeftFace();
+        RefineFaceAtVertex(mesh, parentFace, dest);
+        RefineFaceAtVertex(mesh, parentFace, org);
 
 #ifdef HBR_DEBUG
-	std::cerr << "    on the right face?\n";
+        std::cerr << "    on the right face?\n";
 #endif
-	parentFace = parentEdge->GetRightFace();
-	// The right face may not necessarily exist even after
-	// GuaranteeNeighbor
-	if (parentFace) {
-	    RefineFaceAtVertex(mesh, parentFace, dest);
-	    RefineFaceAtVertex(mesh, parentFace, org);
-	}
+        parentFace = parentEdge->GetRightFace();
+        // The right face may not necessarily exist even after
+        // GuaranteeNeighbor
+        if (parentFace) {
+            RefineFaceAtVertex(mesh, parentFace, dest);
+            RefineFaceAtVertex(mesh, parentFace, org);
+        }
 #ifdef HBR_DEBUG
-	std::cerr << "  end force\n";
+        std::cerr << "  end force\n";
 #endif
-	return;
+        return;
     }
 
     // The last case: the vertex is a child of a vertex. In this case
@@ -770,20 +779,20 @@ HbrCatmarkSubdivision::GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* ver
     if (parentVertex) {
 
 #ifdef HBR_DEBUG
-	std::cerr << "  recursive parent vertex guarantee call\n";
+        std::cerr << "  recursive parent vertex guarantee call\n";
 #endif
-	parentVertex->GuaranteeNeighbors();
-	
-	// And then we refine all the face neighbors of the
-	// parentVertex
-	HbrHalfedge* start = parentVertex->GetIncidentEdge(), *edge;
-	edge = start;
-	while (edge) {
-	    HbrFace* f = edge->GetLeftFace();
-	    RefineFaceAtVertex(mesh, f, parentVertex);
-	    edge = parentVertex->GetNextEdge(edge);
-	    if (edge == start) break;	    
-	}
+        parentVertex->GuaranteeNeighbors();
+
+        // And then we refine all the face neighbors of the
+        // parentVertex
+        HbrHalfedge* start = parentVertex->GetIncidentEdge(), *edge;
+        edge = start;
+        while (edge) {
+            HbrFace* f = edge->GetLeftFace();
+            RefineFaceAtVertex(mesh, f, parentVertex);
+            edge = parentVertex->GetNextEdge(edge);
+            if (edge == start) break;
+        }
     }
 }
 
@@ -794,9 +803,9 @@ HbrCatmarkSubdivision::HasLimit(HbrMesh* mesh, HbrFace* face) {
     if (face->IsHole()) return false;
     // A limit face exists if all the bounding edges have limit curves
     for (int i = 0; i < face->GetNumVertices(); ++i) {
-	if (!HasLimit(mesh, face->GetEdge(i))) {
-	    return false;
-	}
+        if (!HasLimit(mesh, face->GetEdge(i))) {
+            return false;
+        }
     }
     return true;
 }
@@ -804,14 +813,14 @@ HbrCatmarkSubdivision::HasLimit(HbrMesh* mesh, HbrFace* face) {
 template 
 bool
 HbrCatmarkSubdivision::HasLimit(HbrMesh* mesh, HbrHalfedge* edge) {
-    // 	A sharp edge has a limit curve if both endpoints have limits.
-    // 	A smooth edge has a limit if both endpoints have limits and
-    // 	the edge isn't on the boundary.
+    //  A sharp edge has a limit curve if both endpoints have limits.
+    //  A smooth edge has a limit if both endpoints have limits and
+    //  the edge isn't on the boundary.
 
     if (edge->GetSharpness() >= HbrHalfedge::k_InfinitelySharp) return true;
-    
+
     if (!HasLimit(mesh, edge->GetOrgVertex()) || !HasLimit(mesh, edge->GetDestVertex())) return false;
-    
+
     return !edge->IsBoundary();
 }
 
@@ -820,21 +829,21 @@ bool
 HbrCatmarkSubdivision::HasLimit(HbrMesh* /* mesh */, HbrVertex* vertex) {
     vertex->GuaranteeNeighbors();
     switch (vertex->GetMask(false)) {
-	case HbrVertex::k_Smooth:
-	case HbrVertex::k_Dart:
-	    return !vertex->OnBoundary();
-	    break;
-	case HbrVertex::k_Crease:
-	case HbrVertex::k_Corner:
-	default:
-	    if (vertex->IsVolatile()) {
-		// Search for any incident semisharp boundary edge
+        case HbrVertex::k_Smooth:
+        case HbrVertex::k_Dart:
+            return !vertex->OnBoundary();
+            break;
+        case HbrVertex::k_Crease:
+        case HbrVertex::k_Corner:
+        default:
+            if (vertex->IsVolatile()) {
+                // Search for any incident semisharp boundary edge
                 HbrHalfedge* start = vertex->GetIncidentEdge(), *edge, *next;
                 edge = start;
                 while (edge) {
-		    if (edge->IsBoundary() && edge->GetSharpness() < HbrHalfedge::k_InfinitelySharp) {
-			return false;
-		    }
+                    if (edge->IsBoundary() && edge->GetSharpness() < HbrHalfedge::k_InfinitelySharp) {
+                        return false;
+                    }
                     next = vertex->GetNextEdge(edge);
                     if (next == start) {
                         break;
@@ -847,9 +856,9 @@ HbrCatmarkSubdivision::HasLimit(HbrMesh* /* mesh */, HbrVertex* vertex)
                     } else {
                         edge = next;
                     }
-		}
-	    }
-	    return true;
+                }
+            }
+            return true;
     }
 }
 
@@ -858,22 +867,22 @@ HbrVertex*
 HbrCatmarkSubdivision::Subdivide(HbrMesh* mesh, HbrFace* face) {
 
     // Face rule: simply average all vertices on the face
-    HbrVertex* v = mesh->NewVertex();    
+    HbrVertex* v = mesh->NewVertex();
     T& data = v->GetData();
     int nv = face->GetNumVertices();
     float weight = 1.0f / nv;
 
     HbrHalfedge* edge = face->GetFirstEdge();
     for (int i = 0; i < face->GetNumVertices(); ++i) {
-	HbrVertex* w = edge->GetOrgVertex();
-	// If there are vertex edits we have to make sure the edit
-	// has been applied
-	if (mesh->HasVertexEdits()) {
-	    w->GuaranteeNeighbors();
-	}
-	data.AddWithWeight(w->GetData(), weight);
-	data.AddVaryingWithWeight(w->GetData(), weight);
-	edge = edge->GetNext();
+        HbrVertex* w = edge->GetOrgVertex();
+        // If there are vertex edits we have to make sure the edit
+        // has been applied
+        if (mesh->HasVertexEdits()) {
+            w->GuaranteeNeighbors();
+        }
+        data.AddWithWeight(w->GetData(), weight);
+        data.AddVaryingWithWeight(w->GetData(), weight);
+        edge = edge->GetNext();
     }
 #ifdef HBR_DEBUG
     std::cerr << "Subdividing at " << *face << "\n";
@@ -882,7 +891,7 @@ HbrCatmarkSubdivision::Subdivide(HbrMesh* mesh, HbrFace* face) {
     // Set the extraordinary flag if the face had anything other than
     // 4 vertices
     if (nv != 4) v->SetExtraordinary();
-    
+
 #ifdef HBR_DEBUG
     std::cerr << "  created " << *v << "\n";
 #endif
@@ -907,12 +916,12 @@ HbrCatmarkSubdivision::OldTriangleSubdivide(HbrMesh* mesh, HbrFace* fac
     data.Clear();
 
     float weight = 1.0f / 6.0f;
-    
+
     for (int i = 0; i < 3; ++i) {
-	HbrVertex* w = face->GetVertex(i);
-	HbrHalfedge* e = face->GetEdge(i);
-	data.AddWithWeight(w->Subdivide()->GetData(), weight);
-	data.AddWithWeight(e->Subdivide()->GetData(), weight);
+        HbrVertex* w = face->GetVertex(i);
+        HbrHalfedge* e = face->GetEdge(i);
+        data.AddWithWeight(w->Subdivide()->GetData(), weight);
+        data.AddWithWeight(e->Subdivide()->GetData(), weight);
     }
 }
 #endif
@@ -958,7 +967,7 @@ HbrCatmarkSubdivision::OldTriangleSubdivide(HbrMesh* mesh, HbrFace* fac
    If there is a mixture of triangular and non-triangular faces, the
    weights are interpolated. */
 
-#define HBR_SMOOTH_TRI_EDGE_WEIGHT	0.470f	/* from Mathematica */
+#define HBR_SMOOTH_TRI_EDGE_WEIGHT      0.470f  /* from Mathematica */
 
 template 
 HbrVertex*
@@ -972,66 +981,66 @@ HbrCatmarkSubdivision::Subdivide(HbrMesh* mesh, HbrHalfedge* edge) {
 #ifdef HBR_DEBUG
     std::cerr << "Subdividing at " << *edge << " (sharpness = " << esharp << ")";
 #endif
-    
+
     HbrVertex* v = mesh->NewVertex();
     T& data = v->GetData();
 
     // If there's the possibility of vertex edits on either vertex, we
     // have to make sure the edit has been applied
     if (mesh->HasVertexEdits()) {
-	edge->GetOrgVertex()->GuaranteeNeighbors();
-	edge->GetDestVertex()->GuaranteeNeighbors();
+        edge->GetOrgVertex()->GuaranteeNeighbors();
+        edge->GetDestVertex()->GuaranteeNeighbors();
     }
-    
+
     if (!edge->IsBoundary() && esharp <= 1.0f) {
 
-	// Of the two half-edges, pick one of them consistently such
-	// that the left and right faces are also consistent through
-	// multi-threading. It doesn't matter as far as the
-	// theoretical calculation is concerned, but it is desirable
-	// to be consistent about it in the face of the limitations of
-	// floating point commutativity. So we always pick the
-	// half-edge such that its incident face is the smallest of
-	// the two faces, as far as the face paths are concerned.
-	if (edge->GetOpposite() && edge->GetOpposite()->GetFace()->GetPath() < edge->GetFace()->GetPath()) {
-	    edge = edge->GetOpposite();
-	}
-	
-	// Handle both the smooth and fractional sharpness cases.  We
-	// lerp between the sharp case (average of the two end points)
-	// and the unsharp case (average of two end points plus two
-	// face averages).
+        // Of the two half-edges, pick one of them consistently such
+        // that the left and right faces are also consistent through
+        // multi-threading. It doesn't matter as far as the
+        // theoretical calculation is concerned, but it is desirable
+        // to be consistent about it in the face of the limitations of
+        // floating point commutativity. So we always pick the
+        // half-edge such that its incident face is the smallest of
+        // the two faces, as far as the face paths are concerned.
+        if (edge->GetOpposite() && edge->GetOpposite()->GetFace()->GetPath() < edge->GetFace()->GetPath()) {
+            edge = edge->GetOpposite();
+        }
 
-	float leftWeight, rightWeight, faceWeight, vertWeight;
-	HbrFace* rf = edge->GetRightFace();
-	HbrFace* lf = edge->GetLeftFace();
+        // Handle both the smooth and fractional sharpness cases.  We
+        // lerp between the sharp case (average of the two end points)
+        // and the unsharp case (average of two end points plus two
+        // face averages).
 
-	// The standard catmull-clark rule for face weights is 0.25.
-	// The modified, new triangle subdivision rule uses a value of
-	// SMOOTH_TRI_EDGE_WEIGHT as defined above. We lerp between
-	// the right and left weights as needed.
-	leftWeight = (triangleSubdivision == k_New && lf->GetNumVertices() == 3) ? HBR_SMOOTH_TRI_EDGE_WEIGHT : 0.25f;
-	rightWeight = (triangleSubdivision == k_New && rf->GetNumVertices() == 3) ? HBR_SMOOTH_TRI_EDGE_WEIGHT : 0.25f;
-	faceWeight = 0.5f * (leftWeight + rightWeight);	
-	vertWeight = 0.5f * (1.0f - 2.0f * faceWeight);
+        float leftWeight, rightWeight, faceWeight, vertWeight;
+        HbrFace* rf = edge->GetRightFace();
+        HbrFace* lf = edge->GetLeftFace();
 
-	// Lerp the face weight between non sharp contribution and
-	// sharp contribution (which is zero)
-	faceWeight *= (1.0f - esharp);
-	
-	// Lerp the vert weight between non sharp contribution and
-	// sharp contribution of 0.5f
-	vertWeight = 0.5f * esharp + (1.0f - esharp) * vertWeight;
-	
-	data.AddWithWeight(edge->GetOrgVertex()->GetData(), vertWeight);
-	data.AddWithWeight(edge->GetDestVertex()->GetData(), vertWeight);
+        // The standard catmull-clark rule for face weights is 0.25.
+        // The modified, new triangle subdivision rule uses a value of
+        // SMOOTH_TRI_EDGE_WEIGHT as defined above. We lerp between
+        // the right and left weights as needed.
+        leftWeight = (triangleSubdivision == k_New && lf->GetNumVertices() == 3) ? HBR_SMOOTH_TRI_EDGE_WEIGHT : 0.25f;
+        rightWeight = (triangleSubdivision == k_New && rf->GetNumVertices() == 3) ? HBR_SMOOTH_TRI_EDGE_WEIGHT : 0.25f;
+        faceWeight = 0.5f * (leftWeight + rightWeight);
+        vertWeight = 0.5f * (1.0f - 2.0f * faceWeight);
 
-	data.AddWithWeight(lf->Subdivide()->GetData(), faceWeight);
-	data.AddWithWeight(rf->Subdivide()->GetData(), faceWeight);
+        // Lerp the face weight between non sharp contribution and
+        // sharp contribution (which is zero)
+        faceWeight *= (1.0f - esharp);
+
+        // Lerp the vert weight between non sharp contribution and
+        // sharp contribution of 0.5f
+        vertWeight = 0.5f * esharp + (1.0f - esharp) * vertWeight;
+
+        data.AddWithWeight(edge->GetOrgVertex()->GetData(), vertWeight);
+        data.AddWithWeight(edge->GetDestVertex()->GetData(), vertWeight);
+
+        data.AddWithWeight(lf->Subdivide()->GetData(), faceWeight);
+        data.AddWithWeight(rf->Subdivide()->GetData(), faceWeight);
     } else {
-	// Fully sharp edge, just average the two end points
-	data.AddWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
-	data.AddWithWeight(edge->GetDestVertex()->GetData(), 0.5f);
+        // Fully sharp edge, just average the two end points
+        data.AddWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
+        data.AddWithWeight(edge->GetDestVertex()->GetData(), 0.5f);
     }
 
     // Varying data is always the average of two end points
@@ -1051,7 +1060,7 @@ HbrCatmarkSubdivision::Subdivide(HbrMesh* mesh, HbrVertex* vertex) {
     // Ensure the ring of faces around this vertex exists before
     // we compute the valence
     vertex->GuaranteeNeighbors();
-    
+
     float valence = static_cast(vertex->GetValence());
     float invvalencesquared = 1.0f / (valence * valence);
 
@@ -1071,76 +1080,76 @@ HbrCatmarkSubdivision::Subdivide(HbrMesh* mesh, HbrVertex* vertex) {
     // subdivision, then use fractional mask weights to weigh
     // each weighing
     if (masks[0] != masks[1]) {
-	weights[1] = vertex->GetFractionalMask();
-	weights[0] = 1.0f - weights[1];
-	passes = 2;
+        weights[1] = vertex->GetFractionalMask();
+        weights[0] = 1.0f - weights[1];
+        passes = 2;
     } else {
-	weights[0] = 1.0f;
-	weights[1] = 0.0f;
-	passes = 1;
+        weights[0] = 1.0f;
+        weights[1] = 0.0f;
+        passes = 1;
     }
     for (int i = 0; i < passes; ++i) {
-	switch (masks[i]) {
-	    case HbrVertex::k_Smooth:
-	    case HbrVertex::k_Dart: {
-		// Compute n-2/n of the old vertex value
-		data.AddWithWeight(vertex->GetData(), weights[i] * invvalencesquared * valence * (valence - 2));
-		// Add 1 / n^2 * surrounding edge vertices and surrounding face
-		// subdivided vertices
+        switch (masks[i]) {
+            case HbrVertex::k_Smooth:
+            case HbrVertex::k_Dart: {
+                // Compute n-2/n of the old vertex value
+                data.AddWithWeight(vertex->GetData(), weights[i] * invvalencesquared * valence * (valence - 2));
+                // Add 1 / n^2 * surrounding edge vertices and surrounding face
+                // subdivided vertices
                 HbrSubdivision::AddSurroundingVerticesWithWeight(
                     mesh, vertex, weights[i] * invvalencesquared, &data);
 
-		HbrHalfedge* start = vertex->GetIncidentEdge(), *edge;
-		edge = start;
-		while (edge) {
-		    HbrFace* f = edge->GetLeftFace();
-		    data.AddWithWeight(f->Subdivide()->GetData(), weights[i] * invvalencesquared);
-		    edge = vertex->GetNextEdge(edge);
-		    if (edge == start) break;
-		}
-		break;
-	    }
-	    case HbrVertex::k_Crease: {
-		// Compute 3/4 of old vertex value
-		data.AddWithWeight(vertex->GetData(), weights[i] * 0.75f);
+                HbrHalfedge* start = vertex->GetIncidentEdge(), *edge;
+                edge = start;
+                while (edge) {
+                    HbrFace* f = edge->GetLeftFace();
+                    data.AddWithWeight(f->Subdivide()->GetData(), weights[i] * invvalencesquared);
+                    edge = vertex->GetNextEdge(edge);
+                    if (edge == start) break;
+                }
+                break;
+            }
+            case HbrVertex::k_Crease: {
+                // Compute 3/4 of old vertex value
+                data.AddWithWeight(vertex->GetData(), weights[i] * 0.75f);
 
-		// Add 0.125f of the (hopefully only two!) neighbouring
-		// sharp edges
+                // Add 0.125f of the (hopefully only two!) neighbouring
+                // sharp edges
                 HbrSubdivision::AddCreaseEdgesWithWeight(
                     mesh, vertex, i == 1, weights[i] * 0.125f, &data);
-		break;
-	    }
-	    case HbrVertex::k_Corner:
-	    default: {
-		// Just copy the old value
-		data.AddWithWeight(vertex->GetData(), weights[i]);
-		break;
-	    }
-	}
+                break;
+            }
+            case HbrVertex::k_Corner:
+            default: {
+                // Just copy the old value
+                data.AddWithWeight(vertex->GetData(), weights[i]);
+                break;
+            }
+        }
     }
 
-    // Varying data is always just propogated down
+    // Varying data is always just propagated down
     data.AddVaryingWithWeight(vertex->GetData(), 1.0f);
-    
+
 #ifdef HBR_DEBUG
-    std::cerr << "Subdividing at " << *vertex << "\n";    
+    std::cerr << "Subdividing at " << *vertex << "\n";
     std::cerr << "  created " << *v << "\n";
 #endif
     // Inherit extraordinary flag and sharpness
     if (vertex->IsExtraordinary()) v->SetExtraordinary();
     float sharp = vertex->GetSharpness();
     if (sharp >= HbrVertex::k_InfinitelySharp) {
-	v->SetSharpness(HbrVertex::k_InfinitelySharp);
+        v->SetSharpness(HbrVertex::k_InfinitelySharp);
     } else if (sharp > HbrVertex::k_Smooth) {
-	sharp -= 1.0f;
-	if (sharp < (float) HbrVertex::k_Smooth) {
-	    sharp = (float) HbrVertex::k_Smooth;
-	}
-	v->SetSharpness(sharp);
+        sharp -= 1.0f;
+        if (sharp < (float) HbrVertex::k_Smooth) {
+            sharp = (float) HbrVertex::k_Smooth;
+        }
+        v->SetSharpness(sharp);
     } else {
-	v->SetSharpness(HbrVertex::k_Smooth);
+        v->SetSharpness(HbrVertex::k_Smooth);
     }
-    return v;    
+    return v;
 }
 
 } // end namespace OPENSUBDIV_VERSION
diff --git a/opensubdiv/hbr/cornerEdit.h b/opensubdiv/hbr/cornerEdit.h
index 840afde6..177917ff 100644
--- a/opensubdiv/hbr/cornerEdit.h
+++ b/opensubdiv/hbr/cornerEdit.h
@@ -68,7 +68,7 @@ template 
 std::ostream& operator<<(std::ostream& out, const HbrCornerEdit& path) {
     out << "vertex path = (" << path.faceid << ' ';
     for (int i = 0; i < path.nsubfaces; ++i) {
-	out << static_cast(path.subfaces[i]) << ' ';
+        out << static_cast(path.subfaces[i]) << ' ';
     }
     return out << static_cast(path.vertexid) << "), sharpness = " << path.sharpness;
 }
@@ -79,39 +79,39 @@ class HbrCornerEdit : public HbrHierarchicalEdit {
 public:
 
     HbrCornerEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _vertexid, typename HbrHierarchicalEdit::Operation _op, float _sharpness)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), op(_op), sharpness(_sharpness) {
-    }	
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), op(_op), sharpness(_sharpness) {
+    }
 
     HbrCornerEdit(int _faceid, int _nsubfaces, int *_subfaces, int _vertexid, typename HbrHierarchicalEdit::Operation _op, float _sharpness)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), op(_op), sharpness(_sharpness) {
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), op(_op), sharpness(_sharpness) {
     }
-    
+
     virtual ~HbrCornerEdit() {}
 
     friend std::ostream& operator<<  (std::ostream& out, const HbrCornerEdit& path);
 
     virtual void ApplyEditToFace(HbrFace* face) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {	
-	    // Modify vertex sharpness. Note that we could actually do
-	    // this in ApplyEditToVertex as well!
-	    float sharp;
-	    if (op == HbrHierarchicalEdit::Set) {
-		sharp = sharpness;
-	    } else if (op == HbrHierarchicalEdit::Add) {
-		sharp = face->GetVertex(vertexid)->GetSharpness() + sharpness;
-	    } else if (op == HbrHierarchicalEdit::Subtract) {
-		sharp = face->GetVertex(vertexid)->GetSharpness() - sharpness;
-	    }
-	    if (sharp < HbrVertex::k_Smooth) {
-		sharp = HbrVertex::k_Smooth;
-	    }
-	    if (sharp > HbrVertex::k_InfinitelySharp) {
-		sharp = HbrVertex::k_InfinitelySharp;
-	    }
-	    face->GetVertex(vertexid)->SetSharpness(sharp);
-	}
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
+            // Modify vertex sharpness. Note that we could actually do
+            // this in ApplyEditToVertex as well!
+            float sharp;
+            if (op == HbrHierarchicalEdit::Set) {
+                sharp = sharpness;
+            } else if (op == HbrHierarchicalEdit::Add) {
+                sharp = face->GetVertex(vertexid)->GetSharpness() + sharpness;
+            } else if (op == HbrHierarchicalEdit::Subtract) {
+                sharp = face->GetVertex(vertexid)->GetSharpness() - sharpness;
+            }
+            if (sharp < HbrVertex::k_Smooth) {
+                sharp = HbrVertex::k_Smooth;
+            }
+            if (sharp > HbrVertex::k_InfinitelySharp) {
+                sharp = HbrVertex::k_InfinitelySharp;
+            }
+            face->GetVertex(vertexid)->SetSharpness(sharp);
+        }
     }
-    
+
 private:
     // ID of the edge (you can think of this also as the id of the
     // origin vertex of the two-vertex length edge)
diff --git a/opensubdiv/hbr/creaseEdit.h b/opensubdiv/hbr/creaseEdit.h
index a15722cd..8d4bd8cb 100644
--- a/opensubdiv/hbr/creaseEdit.h
+++ b/opensubdiv/hbr/creaseEdit.h
@@ -68,7 +68,7 @@ template 
 std::ostream& operator<<(std::ostream& out, const HbrCreaseEdit& path) {
     out << "edge path = (" << path.faceid << ' ';
     for (int i = 0; i < path.nsubfaces; ++i) {
-	out << static_cast(path.subfaces[i]) << ' ';
+        out << static_cast(path.subfaces[i]) << ' ';
     }
     return out << static_cast(path.edgeid) << "), sharpness = " << path.sharpness;
 }
@@ -79,41 +79,41 @@ class HbrCreaseEdit : public HbrHierarchicalEdit {
 public:
 
     HbrCreaseEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _edgeid, typename HbrHierarchicalEdit::Operation _op, float _sharpness)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), edgeid(_edgeid), op(_op), sharpness(_sharpness) {
-    }	
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), edgeid(_edgeid), op(_op), sharpness(_sharpness) {
+    }
 
     HbrCreaseEdit(int _faceid, int _nsubfaces, int *_subfaces, int _edgeid, typename HbrHierarchicalEdit::Operation _op, float _sharpness)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), edgeid(_edgeid), op(_op), sharpness(_sharpness) {
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), edgeid(_edgeid), op(_op), sharpness(_sharpness) {
     }
-    
+
     virtual ~HbrCreaseEdit() {}
 
     friend std::ostream& operator<<  (std::ostream& out, const HbrCreaseEdit& path);
 
     virtual void ApplyEditToFace(HbrFace* face) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {	
-	    // Modify edge sharpness
-	    float sharp;
-	    if (op == HbrHierarchicalEdit::Set) {
-		sharp = sharpness;
-	    } else if (op == HbrHierarchicalEdit::Add) {
-		sharp = face->GetEdge(edgeid)->GetSharpness() + sharpness;
-	    } else if (op == HbrHierarchicalEdit::Subtract) {
-		sharp = face->GetEdge(edgeid)->GetSharpness() - sharpness;
-	    }
-	    if (sharp < HbrHalfedge::k_Smooth)
-		sharp = HbrHalfedge::k_Smooth;
-	    if (sharp > HbrHalfedge::k_InfinitelySharp)
-		sharp = HbrHalfedge::k_InfinitelySharp;
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
+            // Modify edge sharpness
+            float sharp;
+            if (op == HbrHierarchicalEdit::Set) {
+                sharp = sharpness;
+            } else if (op == HbrHierarchicalEdit::Add) {
+                sharp = face->GetEdge(edgeid)->GetSharpness() + sharpness;
+            } else if (op == HbrHierarchicalEdit::Subtract) {
+                sharp = face->GetEdge(edgeid)->GetSharpness() - sharpness;
+            }
+            if (sharp < HbrHalfedge::k_Smooth)
+                sharp = HbrHalfedge::k_Smooth;
+            if (sharp > HbrHalfedge::k_InfinitelySharp)
+                sharp = HbrHalfedge::k_InfinitelySharp;
             // We have to make sure the neighbor of the edge exists at
             // this point. Otherwise, if it comes into being late, it
             // will clobber the overriden sharpness and we will lose
             // the edit.
             face->GetEdge(edgeid)->GuaranteeNeighbor();
             face->GetEdge(edgeid)->SetSharpness(sharp);
-	}
+        }
     }
-    
+
 private:
     // ID of the edge (you can think of this also as the id of the
     // origin vertex of the two-vertex length edge)
diff --git a/opensubdiv/hbr/face.h b/opensubdiv/hbr/face.h
index 4eabc8d8..4bd5db02 100644
--- a/opensubdiv/hbr/face.h
+++ b/opensubdiv/hbr/face.h
@@ -62,7 +62,7 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 
 #include "../hbr/fvarData.h"
 #include "../hbr/allocator.h"
@@ -87,30 +87,31 @@ template  std::ostream& operator<<(std::ostream& out, const HbrFace&
 // A descriptor for a path to a face
 struct HbrFacePath {
     void Print() const {
-	printf("%d", topface);
-	for (std::list::const_iterator i = remainder.begin(); i != remainder.end(); ++i) {
-	    printf(" %d", *i);
-	}
-	printf("\n");
-    }   
+        printf("%d", topface);
+        for (std::vector::const_reverse_iterator i = remainder.rbegin(); i != remainder.rend(); ++i) {
+            printf(" %d", *i);
+        }
+        printf("\n");
+    }
 
     int topface;
-    std::list remainder;
+    // Note that the elements in remainder are stored in reverse order.
+    std::vector remainder;
     friend bool operator< (const HbrFacePath& x, const HbrFacePath& y);
 };
 
 inline bool operator< (const HbrFacePath& x, const HbrFacePath& y) {
     if (x.topface != y.topface) {
-	return x.topface < y.topface;
+        return x.topface < y.topface;
     } else if (x.remainder.size() != y.remainder.size()) {
-	return x.remainder.size() < y.remainder.size();
+        return x.remainder.size() < y.remainder.size();
     } else {
-	std::list::const_iterator i = x.remainder.begin();
-	std::list::const_iterator j = y.remainder.begin();	
-	for ( ; i != x.remainder.end(); ++i, ++j) {
-	    if (*i != *j) return (*i < *j);
-	}
-	return true;
+        std::vector::const_reverse_iterator i = x.remainder.rbegin();
+        std::vector::const_reverse_iterator j = y.remainder.rbegin();
+        for ( ; i != x.remainder.rend(); ++i, ++j) {
+            if (*i != *j) return (*i < *j);
+        }
+        return true;
     }
 }
 
@@ -129,7 +130,7 @@ public:
 
     // Returns the mesh to which this face belongs
     HbrMesh* GetMesh() const { return mesh; }
-    
+
     // Return number of vertices
     int GetNumVertices() const { return nvertices; }
 
@@ -139,7 +140,7 @@ public:
     // Return the first halfedge of the face
     HbrHalfedge* GetFirstEdge() const {
         if (nvertices > 4) {
-            return const_cast*>(&extraedges[0]);            
+            return const_cast*>(&extraedges[0]);
         } else {
             return const_cast*>(&edges[0]);
         }
@@ -154,17 +155,17 @@ public:
 
     // Return the parent of this face
     HbrFace* GetParent() const { return parent; }
-    
+
     // Set the child
     void SetChild(int index, HbrFace* face);
 
     // Return the child with the indicated index
     HbrFace* GetChild(int index) const {
-	if (!children || index < 0 || index >= mesh->GetSubdivision()->GetFaceChildrenCount(nvertices)) return 0;
-	return children[index];
+        if (!children || index < 0 || index >= mesh->GetSubdivision()->GetFaceChildrenCount(nvertices)) return 0;
+        return children[index];
     }
 
-    // Subdivide the face into a vertex if needed and return 
+    // Subdivide the face into a vertex if needed and return
     HbrVertex* Subdivide();
 
     // Remove the reference to subdivided vertex
@@ -259,26 +260,27 @@ public:
     HbrFace*& GetNext() { return parent; }
 
     HbrFacePath GetPath() const {
-	HbrFacePath path;
-	const HbrFace* f = this, *p = GetParent();
-	while (p) {
+        HbrFacePath path;
+        path.remainder.reserve(GetDepth());
+        const HbrFace* f = this, *p = GetParent();
+        while (p) {
             int nchildren = mesh->GetSubdivision()->GetFaceChildrenCount(p->nvertices);
-	    for (int i = 0; i < nchildren; ++i) {
-		if (p->children[i] == f) {
-		    path.remainder.push_front(i);
-		    break;
-		}
-	    }
-	    f = p;
-	    p = f->GetParent();
-	}
-	path.topface = f->GetID();
-	assert(GetDepth() == 0 || static_cast(path.remainder.size()) == GetDepth());
-	return path;
+            for (int i = 0; i < nchildren; ++i) {
+                if (p->children[i] == f) {
+                    path.remainder.push_back(i);
+                    break;
+                }
+            }
+            f = p;
+            p = f->GetParent();
+        }
+        path.topface = f->GetID();
+        assert(GetDepth() == 0 || static_cast(path.remainder.size()) == GetDepth());
+        return path;
     }
 
     void PrintPath() const {
-	GetPath().Print();
+        GetPath().Print();
     }
 
     // Returns the blind pointer to client data
@@ -290,8 +292,8 @@ public:
     void SetClientData(void *data) {
         clientData = data;
     }
-    
-    
+
+
 private:
 
     // Mesh to which this face belongs
@@ -305,12 +307,12 @@ private:
 
     // Ptex index
     int ptexindex;
-    
+
     // Number of vertices (and number of edges)
     int nvertices;
 
     // Halfedge array for this face
-    // HbrHalfedge::GetIndex() relies on this being size 4 
+    // HbrHalfedge::GetIndex() relies on this being size 4
     HbrHalfedge edges[4];
 
     // Edge storage if this face is not a triangle or quad
@@ -333,18 +335,18 @@ private:
     StitchEdge **stitchEdges;
     void **stitchDatas;
 #endif
-    
+
     // Pointer to a list of hierarchical edits applicable to this face
     HbrHierarchicalEdit** edits;
 
     // Blind client data pointer
     void * clientData;
-    
+
     // Depth of the face in the mesh hierarchy - coarse faces are
     // level 0. (Hmmm.. is it safe to assume that we'll never
     // subdivide to greater than 255?)
     unsigned char depth;
-    
+
     unsigned short hole:1;
     unsigned short coarse:1;
     unsigned short protect:1;
@@ -406,12 +408,12 @@ HbrFace::Initialize(HbrMesh* m, HbrFace* _parent, int childindex, int f
     int fvarbitsSizePerEdge = ((fvarcount + 15) / 16);
 
     if (nv > 4) {
-        
+
         // If we have more than four vertices, we ignore the
         // overallocation and allocate our own buffers for stitch
         // edges and facevarying data.
 #ifdef HBRSTITCH
-        if (mesh->GetStitchCount()) {        
+        if (mesh->GetStitchCount()) {
             stitchEdges = new StitchEdge*[mesh->GetStitchCount() * nv];
             stitchDatas = new void*[nv];
             for (i = 0; i < mesh->GetStitchCount() * nv; ++i) {
@@ -433,8 +435,8 @@ HbrFace::Initialize(HbrMesh* m, HbrFace* _parent, int childindex, int f
 
         // We also ignore the edge array and allocate extra storage -
         // this simplifies GetNext and GetPrev math in HbrHalfede
-	extraedges = new HbrHalfedge[nv];
-        
+        extraedges = new HbrHalfedge[nv];
+
     } else {
         // Under four vertices: upstream allocation for the class has
         // been over allocated to include storage for stitchEdges
@@ -458,12 +460,12 @@ HbrFace::Initialize(HbrMesh* m, HbrFace* _parent, int childindex, int f
             fvarbits = (unsigned int*) buffer;
         }
     }
-    
+
     // Must do this before we create edges
     if (_parent) {
-	_parent->SetChild(childindex, this);
+        _parent->SetChild(childindex, this);
     }
-    
+
     // Edges must be constructed in this two part approach: we must
     // ensure that opposite/next/previous ptrs are all set up
     // correctly, before we can begin adding incident edges to
@@ -471,16 +473,16 @@ HbrFace::Initialize(HbrMesh* m, HbrFace* _parent, int childindex, int f
     int next;
     unsigned int *curfvarbits = fvarbits;
     for (i = 0, next = 1; i < nv; ++i, ++next) {
-	if (next == nv) next = 0;
-	HbrHalfedge* opposite = vertices[next]->GetEdge(vertices[i]);
-	GetEdge(i)->Initialize(opposite, i, vertices[i], curfvarbits, this);
-	if (opposite) opposite->SetOpposite(GetEdge(i));
+        if (next == nv) next = 0;
+        HbrHalfedge* opposite = vertices[next]->GetEdge(vertices[i]);
+        GetEdge(i)->Initialize(opposite, i, vertices[i], curfvarbits, this);
+        if (opposite) opposite->SetOpposite(GetEdge(i));
         if (fvarbits) {
             curfvarbits = curfvarbits + fvarbitsSizePerEdge;
         }
     }
     for (i = 0; i < nv; ++i) {
-	vertices[i]->AddIncidentEdge(GetEdge(i));
+        vertices[i]->AddIncidentEdge(GetEdge(i));
     }
 }
 
@@ -493,80 +495,80 @@ template 
 void
 HbrFace::Destroy() {
     if (initialized && !destroyed) {
-	int i;
+        int i;
 #ifdef HBRSTITCH
         const int stitchCount = mesh->GetStitchCount();
-#endif        
+#endif
 
-	// Remove children's references to self
-	if (children) {
+        // Remove children's references to self
+        if (children) {
             int nchildren = mesh->GetSubdivision()->GetFaceChildrenCount(nvertices);
-	    for (i = 0; i < nchildren; ++i) {
-		if (children[i]) {
-		    children[i]->parent = 0;
-		    children[i] = 0;
-		}
-	    }
-	    delete[] children;
-	    children = 0;
-	}	
+            for (i = 0; i < nchildren; ++i) {
+                if (children[i]) {
+                    children[i]->parent = 0;
+                    children[i] = 0;
+            }
+            }
+            delete[] children;
+            children = 0;
+        }
 
-	// Deleting the incident edges from the vertices in this way is
-	// the safest way of doing things. Doing it in the halfedge
-	// destructor will not work well because it disrupts cycle
-	// finding/incident edge replacement in the vertex code.
+        // Deleting the incident edges from the vertices in this way is
+        // the safest way of doing things. Doing it in the halfedge
+        // destructor will not work well because it disrupts cycle
+        // finding/incident edge replacement in the vertex code.
         // We also take this time to clean up any orphaned stitches
         // still belonging to the edges.
-	for (i = 0; i < nvertices; ++i) {
+        for (i = 0; i < nvertices; ++i) {
             HbrHalfedge *edge = GetEdge(i);
 #ifdef HBRSTITCH
             edge->DestroyStitchEdges(stitchCount);
 #endif
-	    HbrVertex* vertex = edge->GetOrgVertex();
+            HbrVertex* vertex = edge->GetOrgVertex();
             if (fvarbits) {
                 HbrFVarData& fvt = vertex->GetFVarData(this);
                 if (fvt.GetFace() == this) {
                     fvt.SetFace(0);
                 }
             }
-	    vertex->RemoveIncidentEdge(edge);
-	    vertex->UnGuaranteeNeighbors();
-	}
-	if (extraedges) {
-	    delete[] extraedges;
-	    extraedges = 0;
-	}
+            vertex->RemoveIncidentEdge(edge);
+            vertex->UnGuaranteeNeighbors();
+        }
+        if (extraedges) {
+            delete[] extraedges;
+            extraedges = 0;
+        }
 
-	// Remove parent's reference to self
-	if (parent) {
-	    bool parentHasOtherKids = false;
-	    assert(parent->children);
+        // Remove parent's reference to self
+        if (parent) {
+            bool parentHasOtherKids = false;
+            assert(parent->children);
             int nchildren = mesh->GetSubdivision()->GetFaceChildrenCount(parent->nvertices);
-	    for (i = 0; i < nchildren; ++i) {
-		if (parent->children[i] == this) {
-		    parent->children[i] = 0;
-		} else if (parent->children[i]) parentHasOtherKids = true;
-	    }
-	    // After cleaning the parent's reference to self, the parent
-	    // may be able to clean itself up
-	    if (!parentHasOtherKids) {
-		delete[] parent->children;
-		parent->children = 0;
-		if (parent->GarbageCollectable()) {
-		    mesh->DeleteFace(parent);
-		}
-	    }
-	    parent = 0;
-	}
+            for (i = 0; i < nchildren; ++i) {
+                if (parent->children[i] == this) {
+                    parent->children[i] = 0;
+                } else if (parent->children[i]) parentHasOtherKids = true;
+            }
+            // After cleaning the parent's reference to self, the parent
+            // may be able to clean itself up
+            if (!parentHasOtherKids) {
+                delete[] parent->children;
+                parent->children = 0;
+                if (parent->GarbageCollectable()) {
+                    mesh->DeleteFace(parent);
+                }
+            }
+            parent = 0;
+        }
 
-	// Orphan the child vertex
-	if (vchild) {
-	    vchild->SetParent(static_cast(0));
-	    vchild = 0;
-	}
+        // Orphan the child vertex
+        if (vchild) {
+            vchild->SetParent(static_cast(0));
+            vchild = 0;
+        }
 
-	if (nvertices > 4 && fvarbits) {
-	    free(fvarbits);
+        if (nvertices > 4 && fvarbits) {
+            free(fvarbits);
 #ifdef HBRSTITCH
             if (stitchEdges) {
                 delete[] stitchEdges;
@@ -582,16 +584,16 @@ HbrFace::Destroy() {
         stitchDatas = 0;
 #endif
 
-	// Make sure the four edges intrinsic to face are properly cleared
+        // Make sure the four edges intrinsic to face are properly cleared
         // if they were used
         if (nvertices <= 4) {
             for (i = 0; i < nvertices; ++i) {
                 GetEdge(i)->Clear();
             }
         }
-	nvertices = 0;
-	initialized = 0;
-	destroyed = 1;
+        nvertices = 0;
+        initialized = 0;
+        destroyed = 1;
     }
 }
 
@@ -600,36 +602,36 @@ HbrHalfedge*
 HbrFace::GetEdge(int index) const {
     assert(index >= 0 && index < nvertices);
     if (nvertices > 4) {
-	return extraedges + index;
+        return extraedges + index;
     } else {
-	return const_cast*>(edges + index);
+        return const_cast*>(edges + index);
     }
 }
 
 template 
 HbrVertex*
 HbrFace::GetVertex(int index) const {
-    assert(index >= 0 && index < nvertices);    
+    assert(index >= 0 && index < nvertices);
     if (nvertices > 4) {
-	return extraedges[index].GetOrgVertex();
+        return extraedges[index].GetOrgVertex();
     } else {
-	return edges[index].GetOrgVertex();
+        return edges[index].GetOrgVertex();
     }
 }
 
 template 
 void
 HbrFace::SetChild(int index, HbrFace* face) {
-    // Construct the children array if it doesn't already exist
+        // Construct the children array if it doesn't already exist
     int i;
     if (!children) {
-        int nchildren = mesh->GetSubdivision()->GetFaceChildrenCount(nvertices);
-	children = new HbrFace*[nchildren];
-	for (i = 0; i < nchildren; ++i) {
-	    children[i] = 0;
-	}
-    }
-    children[index] = face;
+            int nchildren = mesh->GetSubdivision()->GetFaceChildrenCount(nvertices);
+        children = new HbrFace*[nchildren];
+        for (i = 0; i < nchildren; ++i) {
+                children[i] = 0;
+            }
+        }
+        children[index] = face;
     face->parent = this;
 }
 
@@ -655,11 +657,11 @@ HbrFace::Unrefine() {
     // references to the children)
     if (children) {
         int nchildren = mesh->GetSubdivision()->GetFaceChildrenCount(nvertices);
-	for (int i = 0; i < nchildren; ++i) {
-	    if (children[i]) mesh->DeleteFace(children[i]);
-	}
-	delete[] children;
-	children = 0;
+        for (int i = 0; i < nchildren; ++i) {
+            if (children[i]) mesh->DeleteFace(children[i]);
+        }
+        delete[] children;
+        children = 0;
     }
 }
 
@@ -680,24 +682,24 @@ void
 HbrFace::MarkUsage() {
     // Must increment the usage on all vertices which are in the
     // support for this face
-    HbrVertex* v;    
+    HbrVertex* v;
     HbrHalfedge* e = GetFirstEdge(), *ee, *eee, *start;
     for (int i = 0; i < nvertices; ++i) {
-	v = e->GetOrgVertex();
-	v->GuaranteeNeighbors();
-	start = v->GetIncidentEdge();
-	ee = start;
-	do {
-	    HbrFace* f = ee->GetLeftFace();
-	    eee = f->GetFirstEdge();
-	    for (int j = 0; j < f->GetNumVertices(); ++j) {
-		eee->GetOrgVertex()->IncrementUsage();
-		eee = eee->GetNext();
-	    }
-	    ee = v->GetNextEdge(ee);
-	    if (ee == start) break;
-	} while (ee);
-	e = e->GetNext();
+        v = e->GetOrgVertex();
+        v->GuaranteeNeighbors();
+        start = v->GetIncidentEdge();
+        ee = start;
+        do {
+            HbrFace* f = ee->GetLeftFace();
+            eee = f->GetFirstEdge();
+            for (int j = 0; j < f->GetNumVertices(); ++j) {
+                eee->GetOrgVertex()->IncrementUsage();
+                eee = eee->GetNext();
+            }
+            ee = v->GetNextEdge(ee);
+            if (ee == start) break;
+        } while (ee);
+        e = e->GetNext();
     }
 }
 
@@ -707,28 +709,28 @@ HbrFace::ClearUsage() {
     bool gc = false;
 
     // Must mark all vertices which may affect this face
-    HbrVertex* v, *vv;    
+    HbrVertex* v, *vv;
     HbrHalfedge* e = GetFirstEdge(), *ee, *eee, *start;
     for (int i = 0; i < nvertices; ++i) {
-	v = e->GetOrgVertex();
-	start = v->GetIncidentEdge();
-	ee = start;
-	do {
-	    HbrFace* f = ee->GetLeftFace();
-	    eee = f->GetFirstEdge();	    
-	    for (int j = 0; j < f->GetNumVertices(); ++j) {
-		vv = eee->GetOrgVertex();
-		vv->DecrementUsage();
-		if (!vv->IsUsed()) {
-		    mesh->AddGarbageCollectableVertex(vv);
-		    gc = true;
-		}
-		eee = eee->GetNext();
-	    }
-	    ee = v->GetNextEdge(ee);
-	    if (ee == start) break;
-	} while (ee);
-	e = e->GetNext();
+        v = e->GetOrgVertex();
+        start = v->GetIncidentEdge();
+        ee = start;
+        do {
+            HbrFace* f = ee->GetLeftFace();
+            eee = f->GetFirstEdge();
+            for (int j = 0; j < f->GetNumVertices(); ++j) {
+                vv = eee->GetOrgVertex();
+                vv->DecrementUsage();
+                if (!vv->IsUsed()) {
+                    mesh->AddGarbageCollectableVertex(vv);
+                    gc = true;
+                }
+                eee = eee->GetNext();
+            }
+            ee = v->GetNextEdge(ee);
+            if (ee == start) break;
+        } while (ee);
+        e = e->GetNext();
     }
     if (gc) mesh->GarbageCollect();
 }
@@ -738,12 +740,12 @@ bool
 HbrFace::GarbageCollectable() const {
     if (children || protect) return false;
     for (int i = 0; i < nvertices; ++i) {
-	HbrHalfedge* edge = GetEdge(i);
-	HbrVertex* vertex = edge->GetOrgVertex();
-	if (vertex->IsUsed()) return false;
-	if (!GetParent() && vertex->EdgeRemovalWillMakeSingular(edge)) {
-	    return false;
-	}
+        HbrHalfedge* edge = GetEdge(i);
+        HbrVertex* vertex = edge->GetOrgVertex();
+        if (vertex->IsUsed()) return false;
+        if (!GetParent() && vertex->EdgeRemovalWillMakeSingular(edge)) {
+            return false;
+        }
     }
     return true;
 }
@@ -755,9 +757,9 @@ HbrFace::SetHierarchicalEdits(HbrHierarchicalEdit** _edits) {
 
     // Walk the list of edits and look for any which apply locally.
     while (HbrHierarchicalEdit* edit = *_edits) {
-	if (!edit->IsRelevantToFace(this)) break;
-	edit->ApplyEditToFace(this);
-	_edits++;	
+        if (!edit->IsRelevantToFace(this)) break;
+        edit->ApplyEditToFace(this);
+        _edits++;
     }
 }
 
@@ -765,13 +767,13 @@ template 
 std::ostream& operator<<(std::ostream& out, const HbrFace& face) {
     out << "face " << face.GetID() << ", " << face.GetNumVertices() << " vertices (";
     for (int i = 0; i < face.GetNumVertices(); ++i) {
-	HbrHalfedge* e = face.GetEdge(i);
-	out << *(e->GetOrgVertex());
-	if (e->IsBoundary()) {
-	    out << " -/-> ";
-	} else {
-	    out << " ---> ";
-	}
+        HbrHalfedge* e = face.GetEdge(i);
+        out << *(e->GetOrgVertex());
+        if (e->IsBoundary()) {
+            out << " -/-> ";
+        } else {
+            out << " ---> ";
+        }
     }
     out << ")";
     return out;
diff --git a/opensubdiv/hbr/faceEdit.h b/opensubdiv/hbr/faceEdit.h
index d44cfd6f..43cff016 100644
--- a/opensubdiv/hbr/faceEdit.h
+++ b/opensubdiv/hbr/faceEdit.h
@@ -70,7 +70,7 @@ template 
 std::ostream& operator<<(std::ostream& out, const HbrFaceEdit& path) {
     out << "face path = (" << path.faceid << ' ';
     for (int i = 0; i < path.nsubfaces; ++i) {
-	out << static_cast(path.subfaces[i]) << ' ';
+        out << static_cast(path.subfaces[i]) << ' ';
     }
     return out << ")";
 }
@@ -81,35 +81,35 @@ class HbrFaceEdit : public HbrHierarchicalEdit {
 public:
 
     HbrFaceEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, int _index, int _width, typename HbrHierarchicalEdit::Operation _op, float *_edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
-	edit = new float[width];
-	memcpy(edit, _edit, width * sizeof(float));
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
+        edit = new float[width];
+        memcpy(edit, _edit, width * sizeof(float));
     }
-    
+
     HbrFaceEdit(int _faceid, int _nsubfaces, int *_subfaces, int _index, int _width, typename HbrHierarchicalEdit::Operation _op, float *_edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
-	edit = new float[width];
-	memcpy(edit, _edit, width * sizeof(float));
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
+        edit = new float[width];
+        memcpy(edit, _edit, width * sizeof(float));
     }
 
 #ifdef PRMAN
     HbrFaceEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, int _index, int _width, typename HbrHierarchicalEdit::Operation _op, RtToken _edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
-	edit = new float[width];
-	RtString* sedit = (RtString*) edit;
-	*sedit = _edit;
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
+        edit = new float[width];
+        RtString* sedit = (RtString*) edit;
+        *sedit = _edit;
     }
-    
+
     HbrFaceEdit(int _faceid, int _nsubfaces, int *_subfaces, int _index, int _width, typename HbrHierarchicalEdit::Operation _op, RtToken _edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
-	edit = new float[width];
-	RtString* sedit = (RtString*) edit;
-	*sedit = _edit;
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
+        edit = new float[width];
+        RtString* sedit = (RtString*) edit;
+        *sedit = _edit;
     }
 #endif
-    
+
     virtual ~HbrFaceEdit() {
-	delete[] edit;
+        delete[] edit;
     }
 
     friend std::ostream& operator<<  (std::ostream& out, const HbrFaceEdit& path);
@@ -125,17 +125,17 @@ public:
 
     // Get the type of operation
     typename HbrHierarchicalEdit::Operation GetOperation() const { return op; }
-    
+
     virtual void ApplyEditToFace(HbrFace* face) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
-	    int oldUniformIndex = face->GetUniformIndex();
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
+            int oldUniformIndex = face->GetUniformIndex();
 
-	    // Change the face's uniform index
-	    face->SetUniformIndex(face->GetMesh()->NewUniformIndex());
+            // Change the face's uniform index
+            face->SetUniformIndex(face->GetMesh()->NewUniformIndex());
 
-	    // Apply edit
-	    face->GetVertex(0)->GetData().ApplyFaceEdit(oldUniformIndex, face->GetUniformIndex(), *const_cast*>(this));
-	}
+            // Apply edit
+            face->GetVertex(0)->GetData().ApplyFaceEdit(oldUniformIndex, face->GetUniformIndex(), *const_cast*>(this));
+        }
     }
 
 private:
diff --git a/opensubdiv/hbr/fvarData.h b/opensubdiv/hbr/fvarData.h
index 93cd794c..bf3c07e2 100644
--- a/opensubdiv/hbr/fvarData.h
+++ b/opensubdiv/hbr/fvarData.h
@@ -76,7 +76,7 @@ template  class HbrFVarData {
 public:
 
     HbrFVarData(float *dataptr)
-	: initialized(false), face(0), data(dataptr) {
+        : initialized(false), face(0), data(dataptr) {
     }
 
     ~HbrFVarData() {
@@ -92,7 +92,7 @@ public:
     const HbrFace * GetFace() const {
         return face;
     }
-    
+
     // Clears the initialized flag
     void Uninitialize() {
         initialized = false;
@@ -108,7 +108,7 @@ public:
     void SetInitialized() {
         initialized = true;
     }
-    
+
     // Return the data from the NgpFVVector
     float* GetData(int item) const { return &data[item]; }
 
@@ -116,8 +116,8 @@ public:
     void Clear(int startindex, int width) {
         memset(data + startindex, 0, width * sizeof(float));
     }
-    
-    // Clears all values of this item    
+
+    // Clears all values of this item
     void ClearAll(int width) {
         initialized = true;
         memset(data, 0, width * sizeof(float));
@@ -127,27 +127,27 @@ public:
     // on this item
     void SetWithWeight(const HbrFVarData& fvvi, int startindex, int width, float weight) {
         float *dst = data + startindex, *src = fvvi.data + startindex;
-	for (int i = 0; i < width; ++i) {
-	    *dst++ = weight * *src++;
-	}
+        for (int i = 0; i < width; ++i) {
+            *dst++ = weight * *src++;
+        }
     }
-    
+
     // Add values of the indicated item (with the indicated weighing)
     // to this item
     void AddWithWeight(const HbrFVarData& fvvi, int startindex, int width, float weight) {
         float *dst = data + startindex, *src = fvvi.data + startindex;
-	for (int i = 0; i < width; ++i) {
-	    *dst++ += weight * *src++;
-	}
+        for (int i = 0; i < width; ++i) {
+            *dst++ += weight * *src++;
+        }
     }
 
     // Add all values of the indicated item (with the indicated
     // weighing) to this item
     void AddWithWeightAll(const HbrFVarData& fvvi, int width, float weight) {
-	float *dst = data, *src = fvvi.data;
-	for (int i = 0; i < width; ++i) {
-	    *dst++ += weight * *src++;
-	}
+        float *dst = data, *src = fvvi.data;
+        for (int i = 0; i < width; ++i) {
+            *dst++ += weight * *src++;
+        }
     }
 
     // Compare all values item against a float buffer. Returns true
@@ -165,7 +165,7 @@ public:
         initialized = true;
         memcpy(data, values, width * sizeof(float));
     }
-    
+
     // Compare this item against another item with tolerance.  Returns
     // true if it compares identical
     bool Compare(const HbrFVarData& fvvi, int startindex, int width, float tolerance=0.0f) const {
@@ -177,7 +177,7 @@ public:
 
     // Modify the data of the item with an edit
     void ApplyFVarEdit(const HbrFVarEdit& edit);
-    
+
 private:
     bool initialized;
     const HbrFace *face;
@@ -197,20 +197,20 @@ namespace OPENSUBDIV_VERSION {
 template 
 void
 HbrFVarData::ApplyFVarEdit(const HbrFVarEdit& edit) {
-	float *dst = data + edit.GetIndex() + edit.GetOffset();
-	const float *src = edit.GetEdit();
-	for (int i = 0; i < edit.GetWidth(); ++i) {
-	    switch(edit.GetOperation()) {
-		case HbrVertexEdit::Set:
-		    *dst++ = *src++;
-		    break;
-		case HbrVertexEdit::Add:
-		    *dst++ += *src++;
-		    break;
-		case HbrVertexEdit::Subtract:
-		    *dst++ -= *src++;
-	    }
-	}
+        float *dst = data + edit.GetIndex() + edit.GetOffset();
+        const float *src = edit.GetEdit();
+        for (int i = 0; i < edit.GetWidth(); ++i) {
+            switch(edit.GetOperation()) {
+                case HbrVertexEdit::Set:
+                    *dst++ = *src++;
+                    break;
+                case HbrVertexEdit::Add:
+                    *dst++ += *src++;
+                    break;
+                case HbrVertexEdit::Subtract:
+                    *dst++ -= *src++;
+            }
+        }
         initialized = true;
     }
 
diff --git a/opensubdiv/hbr/fvarEdit.h b/opensubdiv/hbr/fvarEdit.h
index c119d441..d1f6d369 100644
--- a/opensubdiv/hbr/fvarEdit.h
+++ b/opensubdiv/hbr/fvarEdit.h
@@ -71,7 +71,7 @@ template 
 std::ostream& operator<<(std::ostream& out, const HbrFVarEdit& path) {
     out << "vertex path = (" << path.faceid << ' ';
     for (int i = 0; i < path.nsubfaces; ++i) {
-	out << static_cast(path.subfaces[i]) << ' ';
+        out << static_cast(path.subfaces[i]) << ' ';
     }
     return out << static_cast(path.vertexid) << "), edit = (" << path.edit[0] << ',' << path.edit[1] << ',' << path.edit[2] << ')';
 }
@@ -82,24 +82,24 @@ class HbrFVarEdit : public HbrHierarchicalEdit {
 public:
 
     HbrFVarEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _vertexid, int _index, int _width, int _offset, typename HbrHierarchicalEdit::Operation _op, float *_edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), offset(_offset), op(_op) {
-	edit = new float[width];
-	memcpy(edit, _edit, width * sizeof(float));
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), offset(_offset), op(_op) {
+        edit = new float[width];
+        memcpy(edit, _edit, width * sizeof(float));
     }
-    
+
     HbrFVarEdit(int _faceid, int _nsubfaces, int *_subfaces, int _vertexid, int _index, int _width, int _offset, typename HbrHierarchicalEdit::Operation _op, float *_edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), offset(_offset), op(_op) {
-	edit = new float[width];
-	memcpy(edit, _edit, width * sizeof(float));
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), offset(_offset), op(_op) {
+        edit = new float[width];
+        memcpy(edit, _edit, width * sizeof(float));
     }
-    
+
     virtual ~HbrFVarEdit() {
-	delete[] edit;
+        delete[] edit;
     }
 
     // Return the vertex id (the last element in the path)
     unsigned char GetVertexID() const { return vertexid; }
-    
+
     friend std::ostream& operator<<  (std::ostream& out, const HbrFVarEdit& path);
 
     // Return index into the facevarying data
@@ -110,15 +110,15 @@ public:
 
     // Return offset of the data
     int GetOffset() const { return offset; }
-    
+
     // Get the numerical value of the edit
     const float* GetEdit() const { return edit; }
 
     // Get the type of operation
     typename HbrHierarchicalEdit::Operation GetOperation() const { return op; }
-    
+
     virtual void ApplyEditToFace(HbrFace* face) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
             // The edit will modify the data and almost certainly
             // create a discontinuity, so allocate storage for a new
             // copy of the existing data specific to the face (or use
@@ -132,7 +132,7 @@ public:
             } else {
                 fvt.ApplyFVarEdit(*const_cast*>(this));
             }
-	}
+        }
     }
 
 private:
diff --git a/opensubdiv/hbr/halfedge.h b/opensubdiv/hbr/halfedge.h
index 1ee4470e..bd6ef953 100644
--- a/opensubdiv/hbr/halfedge.h
+++ b/opensubdiv/hbr/halfedge.h
@@ -94,7 +94,7 @@ public:
     ~HbrHalfedge();
 
     void Clear();
-    
+
     // Finish the initialization of the halfedge. Should only be
     // called by HbrFace
     void Initialize(HbrHalfedge* opposite, int index, HbrVertex* origin, unsigned int *fvarbits, HbrFace* face);
@@ -136,7 +136,7 @@ public:
             return this - incidentFace->extraedges;
         }
     }
-    
+
     // Returns the incident vertex
     HbrVertex* GetVertex() const {
         return incidentVertex;
@@ -149,16 +149,16 @@ public:
 
     // Changes the origin vertex. Generally not a good idea to do
     void SetOrgVertex(HbrVertex* v) { incidentVertex = v; }
-    
+
     // Returns the destination vertex
     HbrVertex* GetDestVertex() const { return GetNext()->GetOrgVertex(); }
-    
+
     // Returns the incident facet
     HbrFace* GetFace() const { return incidentFace; }
 
     // Returns the mesh to which this edge belongs
     HbrMesh* GetMesh() const { return incidentFace->GetMesh(); }
-    
+
     // Returns the face on the right
     HbrFace* GetRightFace() const { return opposite ? opposite->GetLeftFace() : NULL; }
 
@@ -173,7 +173,7 @@ public:
         int intindex = datum >> 4;
         unsigned int bits = infsharp << ((datum & 15) * 2);
         getFVarInfSharp()[intindex] |= bits;
-	if (opposite) {
+        if (opposite) {
             opposite->getFVarInfSharp()[intindex] |= bits;
         }
     }
@@ -187,7 +187,7 @@ public:
             memcpy(fvarinfsharp, edge->getFVarInfSharp(), fvarbitsSizePerEdge * sizeof(unsigned int));
         }
     }
-    
+
     // Returns whether the edge is infinitely sharp in facevarying for
     // a particular facevarying datum
     bool GetFVarInfiniteSharp(int datum);
@@ -198,7 +198,7 @@ public:
 
     // Get the sharpness relative to facevarying data
     float GetFVarSharpness(int datum, bool ignoreGeometry=false);
-    
+
     // Returns the (raw) sharpness of the edge
     float GetSharpness() const { return sharpness; }
 
@@ -209,7 +209,7 @@ public:
     // subdivision (next = false) or at the next level of subdivision
     // (next = true).
     bool IsSharp(bool next) const { return (next ? (sharpness > 0.0f) : (sharpness >= 1.0f)); }
-    
+
     // Clears the masks of the adjacent edge vertices. Usually called
     // when a change in edge sharpness occurs.
     void ClearMask() { GetOrgVertex()->ClearMask(); GetDestVertex()->ClearMask(); }
@@ -219,38 +219,38 @@ public:
 
     // Make sure the edge has its opposite face
     void GuaranteeNeighbor();
-    
+
     // Remove the reference to subdivided vertex
     void RemoveChild() { vchild = 0; }
 
     // Sharpness constants
     enum Mask {
-	k_Smooth = 0,
-	k_Sharp = 1,
-	k_InfinitelySharp = 10
+        k_Smooth = 0,
+        k_Sharp = 1,
+        k_InfinitelySharp = 10
     };
 
 #ifdef HBRSTITCH
     StitchEdge* GetStitchEdge(int i) {
         StitchEdge **stitchEdge = getStitchEdges();
-	// If the stitch edge exists, the ownership is transferred to
-	// the caller. Make sure the opposite edge loses ownership as
-	// well.
-	if (stitchEdge[i]) {
-	    if (opposite) {
-		opposite->getStitchEdges()[i] = 0;
-	    }
-	    return StitchGetEdge(&stitchEdge[i]);
-	}
-	// If the stitch edge does not exist then we create one now.
-	// Make sure the opposite edge gets a copy of it too
-	else {
-	    StitchGetEdge(&stitchEdge[i]);
-	    if (opposite) {
-		opposite->getStitchEdges()[i] = stitchEdge[i];
-	    }
-	    return stitchEdge[i];
-	}
+        // If the stitch edge exists, the ownership is transferred to
+        // the caller. Make sure the opposite edge loses ownership as
+        // well.
+        if (stitchEdge[i]) {
+            if (opposite) {
+                opposite->getStitchEdges()[i] = 0;
+            }
+            return StitchGetEdge(&stitchEdge[i]);
+        }
+        // If the stitch edge does not exist then we create one now.
+        // Make sure the opposite edge gets a copy of it too
+        else {
+            StitchGetEdge(&stitchEdge[i]);
+            if (opposite) {
+                opposite->getStitchEdges()[i] = stitchEdge[i];
+            }
+            return stitchEdge[i];
+        }
     }
 
     // If stitch edge exists, and this edge has no opposite, destroy
@@ -266,70 +266,70 @@ public:
             }
         }
     }
-    
+
     StitchEdge* GetRayStitchEdge(int i) {
-	return GetStitchEdge(i + 2);
+        return GetStitchEdge(i + 2);
     }
 
     // Splits our split edge between our children. We'd better have
     // subdivided this edge by this point
     void SplitStitchEdge(int i) {
-	StitchEdge* se = GetStitchEdge(i);
-	HbrHalfedge* ea = GetOrgVertex()->Subdivide()->GetEdge(Subdivide());
-	HbrHalfedge* eb = Subdivide()->GetEdge(GetDestVertex()->Subdivide());
+        StitchEdge* se = GetStitchEdge(i);
+        HbrHalfedge* ea = GetOrgVertex()->Subdivide()->GetEdge(Subdivide());
+        HbrHalfedge* eb = Subdivide()->GetEdge(GetDestVertex()->Subdivide());
         StitchEdge **ease = ea->getStitchEdges();
         StitchEdge **ebse = eb->getStitchEdges();
-	if (i >= 2) { // ray tracing stitches
-	    if (!raystitchccw) {
-		StitchSplitEdge(se, &ease[i], &ebse[i], false, 0, 0, 0);
-	    } else {
-		StitchSplitEdge(se, &ebse[i], &ease[i], true, 0, 0, 0);
-	    }
-	    ea->raystitchccw = eb->raystitchccw = raystitchccw;
-	    if (eb->opposite) {
-		eb->opposite->getStitchEdges()[i] = ebse[i];
-		eb->opposite->raystitchccw = raystitchccw;
-	    }
-	    if (ea->opposite) {
-		ea->opposite->getStitchEdges()[i] = ease[i];
-		ea->opposite->raystitchccw = raystitchccw;
-	    }
-	} else {
-	    if (!stitchccw) {
-		StitchSplitEdge(se, &ease[i], &ebse[i], false, 0, 0, 0);
-	    } else {
-		StitchSplitEdge(se, &ebse[i], &ease[i], true, 0, 0, 0);
-	    }
-	    ea->stitchccw = eb->stitchccw = stitchccw;
-	    if (eb->opposite) {
-		eb->opposite->getStitchEdges()[i] = ebse[i];
-		eb->opposite->stitchccw = stitchccw;
-	    }
-	    if (ea->opposite) {
-		ea->opposite->getStitchEdges()[i] = ease[i];
-		ea->opposite->stitchccw = stitchccw;
-	    }
-	}
+        if (i >= 2) { // ray tracing stitches
+            if (!raystitchccw) {
+                StitchSplitEdge(se, &ease[i], &ebse[i], false, 0, 0, 0);
+            } else {
+                StitchSplitEdge(se, &ebse[i], &ease[i], true, 0, 0, 0);
+            }
+            ea->raystitchccw = eb->raystitchccw = raystitchccw;
+            if (eb->opposite) {
+                eb->opposite->getStitchEdges()[i] = ebse[i];
+                eb->opposite->raystitchccw = raystitchccw;
+            }
+            if (ea->opposite) {
+                ea->opposite->getStitchEdges()[i] = ease[i];
+                ea->opposite->raystitchccw = raystitchccw;
+            }
+        } else {
+            if (!stitchccw) {
+                StitchSplitEdge(se, &ease[i], &ebse[i], false, 0, 0, 0);
+            } else {
+                StitchSplitEdge(se, &ebse[i], &ease[i], true, 0, 0, 0);
+            }
+            ea->stitchccw = eb->stitchccw = stitchccw;
+            if (eb->opposite) {
+                eb->opposite->getStitchEdges()[i] = ebse[i];
+                eb->opposite->stitchccw = stitchccw;
+            }
+            if (ea->opposite) {
+                ea->opposite->getStitchEdges()[i] = ease[i];
+                ea->opposite->stitchccw = stitchccw;
+            }
+        }
     }
 
     void SplitRayStitchEdge(int i) {
-	SplitStitchEdge(i + 2);
+        SplitStitchEdge(i + 2);
     }
-    
+
     void SetStitchEdge(int i, StitchEdge* edge) {
         StitchEdge **stitchEdges = getStitchEdges();
-	stitchEdges[i] = edge;
-	if (opposite) {
-	    opposite->getStitchEdges()[i] = edge;
-	}
+        stitchEdges[i] = edge;
+        if (opposite) {
+            opposite->getStitchEdges()[i] = edge;
+        }
     }
 
     void SetRayStitchEdge(int i, StitchEdge* edge) {
         StitchEdge **stitchEdges = getStitchEdges();
-	stitchEdges[i+2] = edge;
-	if (opposite) {
-	    opposite->getStitchEdges()[i+2] = edge;
-	}
+        stitchEdges[i+2] = edge;
+        if (opposite) {
+            opposite->getStitchEdges()[i+2] = edge;
+        }
     }
 
     void* GetStitchData() const {
@@ -340,32 +340,32 @@ public:
     void SetStitchData(void* data) {
         *(incidentFace->stitchDatas + GetIndex()) = data;
         stitchdatavalid = data ? 1 : 0;
-	if (opposite) {
-	    *(opposite->incidentFace->stitchDatas + opposite->GetIndex()) = data;
+        if (opposite) {
+            *(opposite->incidentFace->stitchDatas + opposite->GetIndex()) = data;
             opposite->stitchdatavalid = stitchdatavalid;
-	}
+        }
     }
-    
+
     bool GetStitchCCW(bool raytraced) const { return raytraced ? raystitchccw : stitchccw; }
-    
+
     void ClearStitchCCW(bool raytraced) {
-	if (raytraced) {
-	    raystitchccw = 0;
-	    if (opposite) opposite->raystitchccw = 0;
-	} else {
-	    stitchccw = 0;
-	    if (opposite) opposite->stitchccw = 0;
-	}
+        if (raytraced) {
+            raystitchccw = 0;
+            if (opposite) opposite->raystitchccw = 0;
+        } else {
+            stitchccw = 0;
+            if (opposite) opposite->stitchccw = 0;
+        }
     }
 
     void ToggleStitchCCW(bool raytraced) {
-	if (raytraced) {
-	    raystitchccw = 1 - raystitchccw;
-	    if (opposite) opposite->raystitchccw = raystitchccw;
-	} else {
-	    stitchccw = 1 - stitchccw;
-	    if (opposite) opposite->stitchccw = stitchccw;
-	}
+        if (raytraced) {
+            raystitchccw = 1 - raystitchccw;
+            if (opposite) opposite->raystitchccw = raystitchccw;
+        } else {
+            stitchccw = 1 - stitchccw;
+            if (opposite) opposite->stitchccw = stitchccw;
+        }
     }
 
 #endif
@@ -395,7 +395,7 @@ private:
     unsigned char coarse:1;
     unsigned char lastedge:1;
     unsigned char firstedge:1;
-    
+
     // Returns bitmask indicating whether a given facevarying datum
     // for the edge is infinitely sharp. Each datum has two bits, and
     // if those two bits are set to 3, it means the status has not
@@ -426,14 +426,14 @@ HbrHalfedge::Initialize(HbrHalfedge* opposite, int index, HbrVertex* or
     lastedge = (index == face->GetNumVertices() - 1);
     firstedge = (index == 0);
     if (opposite) {
-	sharpness = opposite->sharpness;
+        sharpness = opposite->sharpness;
 #ifdef HBRSTITCH
         StitchEdge **stitchEdges = getStitchEdges();
-	for (int i = 0; i < face->GetMesh()->GetStitchCount(); ++i) {
-	    stitchEdges[i] = opposite->getStitchEdges()[i];
-	}
-	stitchccw = opposite->stitchccw;
-	raystitchccw = opposite->raystitchccw;
+        for (int i = 0; i < face->GetMesh()->GetStitchCount(); ++i) {
+            stitchEdges[i] = opposite->getStitchEdges()[i];
+        }
+        stitchccw = opposite->stitchccw;
+        raystitchccw = opposite->raystitchccw;
         stitchdatavalid = 0;
         if (stitchEdges && opposite->GetStitchData()) {
             *(incidentFace->stitchDatas + index) = opposite->GetStitchData();
@@ -449,9 +449,9 @@ HbrHalfedge::Initialize(HbrHalfedge* opposite, int index, HbrVertex* or
         sharpness = 0.0f;
 #ifdef HBRSTITCH
         StitchEdge **stitchEdges = getStitchEdges();
-	for (int i = 0; i < face->GetMesh()->GetStitchCount(); ++i) {
-	    stitchEdges[i] = 0;
-	}
+        for (int i = 0; i < face->GetMesh()->GetStitchCount(); ++i) {
+            stitchEdges[i] = 0;
+        }
         stitchccw = 1;
         raystitchccw = 1;
         stitchdatavalid = 0;
@@ -461,7 +461,7 @@ HbrHalfedge::Initialize(HbrHalfedge* opposite, int index, HbrVertex* or
             int fvarbitsSizePerEdge = ((fvarcount + 15) / 16);
             memset(fvarbits, 0xff, fvarbitsSizePerEdge * sizeof(unsigned int));
         }
-    }    
+    }
 }
 
 template 
@@ -473,21 +473,21 @@ template 
 void
 HbrHalfedge::Clear() {
     if (opposite) {
-	opposite->opposite = 0;
-	if (vchild) {
-	    // Transfer ownership of the vchild to the opposite ptr
-	    opposite->vchild = vchild;
+        opposite->opposite = 0;
+        if (vchild) {
+            // Transfer ownership of the vchild to the opposite ptr
+            opposite->vchild = vchild;
             // Done this way just for assertion sanity
             vchild->SetParent(static_cast(0));
             vchild->SetParent(opposite);
             vchild = 0;
-	}
-	opposite = 0;
+        }
+        opposite = 0;
     }
     // Orphan the child vertex
     else if (vchild) {
         vchild->SetParent(static_cast(0));
-	vchild = 0;
+        vchild = 0;
     }
 }
 
@@ -528,14 +528,14 @@ HbrHalfedge::GetFVarInfiniteSharp(int datum) {
         assert (bits != 2);
         return bits ? true : false;
     }
-    
+
     // If there is no face varying data it can't be infinitely sharp!
     const int fvarwidth = GetMesh()->GetTotalFVarWidth();
     if (!fvarwidth) {
         bits = ~(0x3 << shift);
-	fvarinfsharp[intindex] &= bits;
-	if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
-	return false;
+        fvarinfsharp[intindex] &= bits;
+        if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
+        return false;
     }
 
     // If either incident face is missing, it's a geometric boundary
@@ -543,9 +543,9 @@ HbrHalfedge::GetFVarInfiniteSharp(int datum) {
     HbrFace* left = GetLeftFace(), *right = GetRightFace();
     if (!left || !right) {
         bits = ~(0x2 << shift);
-	fvarinfsharp[intindex] &= bits;
-	if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
-	return true;
+        fvarinfsharp[intindex] &= bits;
+        if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
+        return true;
     }
 
     // Look for the indices on each face which correspond to the
@@ -555,17 +555,17 @@ HbrHalfedge::GetFVarInfiniteSharp(int datum) {
     e = left->GetFirstEdge();
     nv = left->GetNumVertices();
     for (i = 0; i < nv; ++i) {
-	if (e->GetOrgVertex() == GetOrgVertex()) lorg = i;
-	if (e->GetOrgVertex() == GetDestVertex()) ldst = i;
-	e = e->GetNext();
+        if (e->GetOrgVertex() == GetOrgVertex()) lorg = i;
+        if (e->GetOrgVertex() == GetDestVertex()) ldst = i;
+        e = e->GetNext();
     }
     e = right->GetFirstEdge();
     nv = right->GetNumVertices();
     for (i = 0; i < nv; ++i) {
-	if (e->GetOrgVertex() == GetOrgVertex()) rorg = i;
-	if (e->GetOrgVertex() == GetDestVertex()) rdst = i;
-	e = e->GetNext();	
-    }    
+        if (e->GetOrgVertex() == GetOrgVertex()) rorg = i;
+        if (e->GetOrgVertex() == GetDestVertex()) rdst = i;
+        e = e->GetNext();
+    }
     assert(lorg >= 0 && ldst >= 0 && rorg >= 0 && rdst >= 0);
     // Compare the facevarying data to some tolerance
     const int startindex = GetMesh()->GetFVarIndices()[datum];
@@ -573,8 +573,8 @@ HbrHalfedge::GetFVarInfiniteSharp(int datum) {
     if (!right->GetFVarData(rorg).Compare(left->GetFVarData(lorg), startindex, width, 0.001f) ||
         !right->GetFVarData(rdst).Compare(left->GetFVarData(ldst), startindex, width, 0.001f)) {
         bits = ~(0x2 << shift);
-	fvarinfsharp[intindex] &= bits;
-	if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
+        fvarinfsharp[intindex] &= bits;
+        if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
         return true;
     }
 
@@ -602,15 +602,15 @@ HbrHalfedge::GetFVarSharpness(int datum, bool ignoreGeometry) {
     if (infsharp) return k_InfinitelySharp;
 
     if (!ignoreGeometry) {
-	// If it's a geometrically sharp edge it's going to be a
-	// facevarying sharp edge too
-	if (sharpness > k_Smooth) {
-	    return k_InfinitelySharp;
-	}
+        // If it's a geometrically sharp edge it's going to be a
+        // facevarying sharp edge too
+        if (sharpness > k_Smooth) {
+            return k_InfinitelySharp;
+        }
     }
     return k_Smooth;
 }
-    
+
 
 template 
 std::ostream&
@@ -618,14 +618,14 @@ operator<<(std::ostream& out, const HbrHalfedge& edge) {
     if (edge.IsBoundary()) out << "boundary ";
     out << "edge connecting ";
     if (edge.GetOrgVertex())
-	out << *edge.GetOrgVertex();
+        out << *edge.GetOrgVertex();
     else
-	out << "(none)";
+        out << "(none)";
     out << " to ";
     if (edge.GetDestVertex()) {
-	out << *edge.GetDestVertex();
+        out << *edge.GetDestVertex();
     } else {
-	out << "(none)";
+        out << "(none)";
     }
     return out;
 }
@@ -636,7 +636,7 @@ template 
 class HbrHalfedgeCompare {
 public:
     bool operator() (const HbrHalfedge* a, HbrHalfedge* b) const {
-	return (a->GetFace()->GetPath() < b->GetFace()->GetPath());
+        return (a->GetFace()->GetPath() < b->GetFace()->GetPath());
     }
 };
 
diff --git a/opensubdiv/hbr/hierarchicalEdit.h b/opensubdiv/hbr/hierarchicalEdit.h
index 2c6709fa..1aa23266 100644
--- a/opensubdiv/hbr/hierarchicalEdit.h
+++ b/opensubdiv/hbr/hierarchicalEdit.h
@@ -71,44 +71,44 @@ class HbrHierarchicalEdit {
 
 public:
     typedef enum Operation {
-	Set,
-	Add,
-	Subtract
+        Set,
+        Add,
+        Subtract
     } Operation;
 
 protected:
 
     HbrHierarchicalEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces)
-	: faceid(_faceid), nsubfaces(_nsubfaces) {
-	subfaces = new unsigned char[_nsubfaces];
-	for (int i = 0; i < nsubfaces; ++i) {
-	    subfaces[i] = _subfaces[i];
-	}
+        : faceid(_faceid), nsubfaces(_nsubfaces) {
+        subfaces = new unsigned char[_nsubfaces];
+        for (int i = 0; i < nsubfaces; ++i) {
+            subfaces[i] = _subfaces[i];
+        }
     }
 
     HbrHierarchicalEdit(int _faceid, int _nsubfaces, int *_subfaces)
-	: faceid(_faceid), nsubfaces(_nsubfaces) {
-	subfaces = new unsigned char[_nsubfaces];
-	for (int i = 0; i < nsubfaces; ++i) {
-	    subfaces[i] = static_cast(_subfaces[i]);
-	}
+        : faceid(_faceid), nsubfaces(_nsubfaces) {
+        subfaces = new unsigned char[_nsubfaces];
+        for (int i = 0; i < nsubfaces; ++i) {
+            subfaces[i] = static_cast(_subfaces[i]);
+        }
     }
 
 public:
     virtual ~HbrHierarchicalEdit() {
-	delete[] subfaces;
+        delete[] subfaces;
     }
 
     bool operator<(const HbrHierarchicalEdit& p) const {
-	if (faceid < p.faceid) return true;
-	if (faceid > p.faceid) return false;
-	int minlength = nsubfaces;
-	if (minlength > p.nsubfaces) minlength = p.nsubfaces;
-	for (int i = 0; i < minlength; ++i) {
-	    if (subfaces[i] < p.subfaces[i]) return true;
-	    if (subfaces[i] > p.subfaces[i]) return false;	    
-	}
-	return (nsubfaces < p.nsubfaces);
+        if (faceid < p.faceid) return true;
+        if (faceid > p.faceid) return false;
+        int minlength = nsubfaces;
+        if (minlength > p.nsubfaces) minlength = p.nsubfaces;
+        for (int i = 0; i < minlength; ++i) {
+            if (subfaces[i] < p.subfaces[i]) return true;
+            if (subfaces[i] > p.subfaces[i]) return false;
+        }
+        return (nsubfaces < p.nsubfaces);
     }
 
     // Return the face id (the first element in the path)
@@ -119,7 +119,7 @@ public:
 
     // Return a subface element in the path
     unsigned char GetSubface(int index) const { return subfaces[index]; }
-    
+
     // Determines whether this hierarchical edit is relevant to the
     // face in question
     bool IsRelevantToFace(HbrFace* face) const;
@@ -128,12 +128,12 @@ public:
     virtual void ApplyEditToFace(HbrFace* /* face */) {}
 
     // Applys edit to vertex. Subclasses may override this method.
-    virtual void ApplyEditToVertex(HbrFace* /* face */, HbrVertex* /* vertex */) {} 
+    virtual void ApplyEditToVertex(HbrFace* /* face */, HbrVertex* /* vertex */) {}
 
 #ifdef PRMAN
     // Gets the effect of this hierarchical edit on the bounding box.
     // Subclasses may override this method
-    virtual void ApplyToBound(struct bbox& /* box */, RtMatrix * /* mx */) {}
+    virtual void ApplyToBound(struct bbox& /* box */, RtMatrix * /* mx */) const {}
 #endif
 
 protected:
@@ -150,7 +150,7 @@ protected:
 template 
 class HbrHierarchicalEditComparator {
 public:
-    bool operator() (const HbrHierarchicalEdit* path1, const HbrHierarchicalEdit* path2) const { 
+    bool operator() (const HbrHierarchicalEdit* path1, const HbrHierarchicalEdit* path2) const {
         return (*path1 < *path2);
     }
 };
@@ -181,7 +181,7 @@ HbrHierarchicalEdit::IsRelevantToFace(HbrFace* face) const {
     if (!p) return false;
 
     if (this == p) return true;
-    
+
     if (faceid != p->faceid) return false;
 
     // If our path length is less than the face depth, it should mean
@@ -190,7 +190,7 @@ HbrHierarchicalEdit::IsRelevantToFace(HbrFace* face) const {
     if (nsubfaces < face->GetDepth()) return false;
 
     if (memcmp(subfaces, p->subfaces, face->GetDepth() * sizeof(unsigned char)) != 0) {
-	return false;
+        return false;
     }
     return true;
 }
diff --git a/opensubdiv/hbr/holeEdit.h b/opensubdiv/hbr/holeEdit.h
index 9712c9f6..e4110713 100644
--- a/opensubdiv/hbr/holeEdit.h
+++ b/opensubdiv/hbr/holeEdit.h
@@ -68,7 +68,7 @@ template 
 std::ostream& operator<<(std::ostream& out, const HbrHoleEdit& path) {
     out << "edge path = (" << path.faceid << ' ';
     for (int i = 0; i < path.nsubfaces; ++i) {
-	out << static_cast(path.subfaces[i]) << ' ';
+        out << static_cast(path.subfaces[i]) << ' ';
     }
     return out << ")";
 }
@@ -79,22 +79,22 @@ class HbrHoleEdit : public HbrHierarchicalEdit {
 public:
 
     HbrHoleEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces) {
-    }	
-
-    HbrHoleEdit(int _faceid, int _nsubfaces, int *_subfaces) 
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces) {
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces) {
     }
-    
+
+    HbrHoleEdit(int _faceid, int _nsubfaces, int *_subfaces)
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces) {
+    }
+
     virtual ~HbrHoleEdit() {}
 
     friend std::ostream& operator<<  (std::ostream& out, const HbrHoleEdit& path);
 
     virtual void ApplyEditToFace(HbrFace* face) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {	
-	    face->SetHole();
-	}
-    }    
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
+            face->SetHole();
+        }
+    }
 };
 
 
diff --git a/opensubdiv/hbr/loop.h b/opensubdiv/hbr/loop.h
index 10588a56..4b621561 100644
--- a/opensubdiv/hbr/loop.h
+++ b/opensubdiv/hbr/loop.h
@@ -74,29 +74,30 @@ template 
 class HbrLoopSubdivision : public HbrSubdivision{
 public:
     HbrLoopSubdivision()
-	: HbrSubdivision() {}
+        : HbrSubdivision() {}
 
     virtual HbrSubdivision* Clone() const {
         return new HbrLoopSubdivision();
     }
-    
+
     virtual void Refine(HbrMesh* mesh, HbrFace* face);
-    virtual HbrFace* RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face, HbrVertex* vertex);    
+    virtual HbrFace* RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face, HbrVertex* vertex);
     virtual void GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge);
     virtual void GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* vertex);
 
     virtual bool HasLimit(HbrMesh* mesh, HbrFace* face);
     virtual bool HasLimit(HbrMesh* mesh, HbrHalfedge* edge);
     virtual bool HasLimit(HbrMesh* mesh, HbrVertex* vertex);
-    
+
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrFace* face);
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrHalfedge* edge);
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrVertex* vertex);
 
     virtual bool VertexIsExtraordinary(HbrMesh* mesh, HbrVertex* vertex) { return vertex->GetValence() != 6; }
+    virtual bool FaceIsExtraordinary(HbrMesh* /* mesh */, HbrFace* face) { return face->GetNumVertices() != 3; }
 
     virtual int GetFaceChildrenCount(int nvertices) const { return 4; }
-    
+
 private:
 
     // Transfers facevarying data from a parent face to a child face
@@ -121,7 +122,7 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
     // we need to do three edge subdivision rules
     if (index == 3) {
         const int fvarcount = mesh->GetFVarCount();
-	for (int i = 0; i < 3; ++i) {
+        for (int i = 0; i < 3; ++i) {
             HbrHalfedge *edge = face->GetEdge(i);
             GuaranteeNeighbor(mesh, edge);
             childVertex = child->GetVertex((i + 2) % 3);
@@ -129,11 +130,11 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
             if (!fvIsSmooth) {
                 childVertex->NewFVarData(child);
             }
-	    HbrFVarData& fv = childVertex->GetFVarData(child);
+            HbrFVarData& fv = childVertex->GetFVarData(child);
             int fvarindex = 0;
             for (int fvaritem = 0; fvaritem < fvarcount; ++fvaritem) {
                 const int fvarwidth = mesh->GetFVarWidths()[fvaritem];
-            
+
                 if (fvarinterp == HbrMesh::k_InterpolateBoundaryNone ||
                     face->GetEdge(i)->GetFVarSharpness(fvaritem) || face->GetEdge(i)->IsBoundary()) {
 
@@ -157,13 +158,13 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
                 fvarindex += fvarwidth;
             }
             fv.SetInitialized();
-	}
-	return;
+        }
+        return;
     }
 
     HbrHalfedge* edge;
     HbrVertex* v = face->GetVertex(index);
-    
+
     // Otherwise we proceed with one vertex and two edge subdivision
     // applications. First the vertex subdivision rule. Analyze
     // whether the vertex is on the boundary and whether it's an
@@ -189,14 +190,14 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
 
 
     bool fv0IsSmooth, fv1IsSmooth, fv2IsSmooth;
-    
+
     childVertex = child->GetVertex(index);
     fv0IsSmooth = v->IsFVarAllSmooth();
     if (!fv0IsSmooth) {
         childVertex->NewFVarData(child);
     }
     HbrFVarData& fv0 = childVertex->GetFVarData(child);
-    
+
     edge = face->GetEdge(index);
     GuaranteeNeighbor(mesh, edge);
     assert(edge->GetOrgVertex() == v);
@@ -208,7 +209,7 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
     HbrFVarData& fv1 = childVertex->GetFVarData(child);
 
     edge = edge->GetPrev();
-    GuaranteeNeighbor(mesh, edge);    
+    GuaranteeNeighbor(mesh, edge);
     assert(edge == face->GetEdge((index + 2) % 3));
     assert(edge->GetDestVertex() == v);
     childVertex = child->GetVertex((index + 2) % 3);
@@ -217,13 +218,13 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
         childVertex->NewFVarData(child);
     }
     HbrFVarData& fv2 = childVertex->GetFVarData(child);
-    
+
     const int fvarcount = mesh->GetFVarCount();
     int fvarindex = 0;
     for (int fvaritem = 0; fvaritem < fvarcount; ++fvaritem) {
         bool infcorner = false;
         const int fvarwidth = mesh->GetFVarWidths()[fvaritem];
-        const char fvarmask = v->GetFVarMask(fvaritem);        
+        const char fvarmask = v->GetFVarMask(fvaritem);
         if (fvarinterp == HbrMesh::k_InterpolateBoundaryEdgeAndCorner) {
             if (fvarmask >= HbrVertex::k_Corner) {
                 infcorner = true;
@@ -295,11 +296,11 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
                 if (bestface->GetVertex(j) == w) break;
             }
             assert(j != bestface->GetNumVertices());
-            fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);	
-        }    
+            fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
+        }
         // Boundary vertex rule (can use FVarSmooth, which is equivalent
         // to checking that it's sharper than a dart)
-        else if (fvarmask != 0) {    
+        else if (fvarmask != 0) {
 
             // Use 0.75 of the current vert
             fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.75f);
@@ -348,7 +349,7 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
             starte = bestedge;
             w = 0;
             if (HbrHalfedge* e = starte) {
-                assert(starte->GetOrgVertex() == v);	
+                assert(starte->GetOrgVertex() == v);
                 do {
                     if (e->GetFVarSharpness(fvaritem) || !e->GetRightFace()) {
                         bestface = e->GetLeftFace();
@@ -365,10 +366,10 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
             }
             assert(j != bestface->GetNumVertices());
             fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
-	
+
         }
         // Smooth rule
-        else if (!fv0IsSmooth || !fv0.IsInitialized()) {        
+        else if (!fv0IsSmooth || !fv0.IsInitialized()) {
             int valence = v->GetValence();
             float invvalence = 1.0f / valence;
             float beta = 0.25f * cosf((float)M_PI * 2.0f * invvalence) + 0.375f;
@@ -383,7 +384,7 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
             HbrHalfedge* start = v->GetIncidentEdge(), *edge;
             edge = start;
             while (edge) {
-                HbrFace* g = edge->GetLeftFace();	    
+                HbrFace* g = edge->GetLeftFace();
 
                 // .. and look for the edge on that face whose origin is
                 // the same as v, and add a contribution from its
@@ -423,23 +424,23 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
                 }
             }
         }
-		
+
 
         // Edge subdivision rule
         edge = edge->GetPrev();
-    
+
         if (fvarinterp == HbrMesh::k_InterpolateBoundaryNone ||
             edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {
 
-            // Sharp edge rule						 
+            // Sharp edge rule
             fv2.SetWithWeight(face->GetFVarData((index + 2) % 3), fvarindex, fvarwidth, 0.5f);
             fv2.AddWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.5f);
         } else if (!fv2IsSmooth || !fv2.IsInitialized()) {
-            // Smooth edge subdivision. Add 0.375 of adjacent vertices	
+            // Smooth edge subdivision. Add 0.375 of adjacent vertices
             fv2.SetWithWeight(face->GetFVarData((index + 2) % 3), fvarindex, fvarwidth, 0.375f);
             fv2.AddWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.375f);
             // Add 0.125 of opposite vertices
-            fv2.AddWithWeight(face->GetFVarData((index + 1) % 3), fvarindex, fvarwidth, 0.125f);	
+            fv2.AddWithWeight(face->GetFVarData((index + 1) % 3), fvarindex, fvarwidth, 0.125f);
 
             HbrFace* oppFace = edge->GetRightFace();
             for (int j = 0; j < oppFace->GetNumVertices(); ++j) {
@@ -447,7 +448,7 @@ HbrLoopSubdivision::transferFVarToChild(HbrMesh* mesh, HbrFace* face, H
                     fv2.AddWithWeight(oppFace->GetFVarData((j+2)%oppFace->GetNumVertices()), fvarindex, fvarwidth, 0.125f);
                     break;
                 }
-            }	
+            }
         }
 
         fvarindex += fvarwidth;
@@ -463,15 +464,15 @@ HbrLoopSubdivision::transferEditsToChild(HbrFace* face, HbrFace* child,
 
     // Hand down pointers to hierarchical edits
     if (HbrHierarchicalEdit** edits = face->GetHierarchicalEdits()) {
-	while (HbrHierarchicalEdit* edit = *edits) {
-	    if (!edit->IsRelevantToFace(face)) break;
-	    if (edit->GetNSubfaces() > face->GetDepth() &&
-		(edit->GetSubface(face->GetDepth()) == index)) {
-		child->SetHierarchicalEdits(edits);
-		break;
-	    }
-	    edits++;
-	}
+        while (HbrHierarchicalEdit* edit = *edits) {
+            if (!edit->IsRelevantToFace(face)) break;
+            if (edit->GetNSubfaces() > face->GetDepth() &&
+                (edit->GetSubface(face->GetDepth()) == index)) {
+                child->SetHierarchicalEdits(edits);
+                break;
+            }
+            edits++;
+        }
     }
 }
 
@@ -481,56 +482,56 @@ HbrLoopSubdivision::Refine(HbrMesh* mesh, HbrFace* face) {
 
 #ifdef HBR_DEBUG
     std::cerr << "\n\nRefining face " << *face << "\n";
-#endif	
-    
+#endif
+
     assert(face->GetNumVertices() == 3); // or triangulate it?
 
     HbrHalfedge* edge = face->GetFirstEdge();
     HbrHalfedge* prevedge = edge->GetPrev();
     for (int i = 0; i < 3; ++i) {
-	HbrVertex* vertex = edge->GetOrgVertex();
-	if (!face->GetChild(i)) {
+        HbrVertex* vertex = edge->GetOrgVertex();
+        if (!face->GetChild(i)) {
 #ifdef HBR_DEBUG
-	    std::cerr << "Kid " << i << "\n";
+            std::cerr << "Kid " << i << "\n";
 #endif
-	    HbrFace* child;
-	    HbrVertex* vertices[3];
+            HbrFace* child;
+            HbrVertex* vertices[3];
 
-	    vertices[i] = vertex->Subdivide();
-	    vertices[(i + 1) % 3] = edge->Subdivide();
-	    vertices[(i + 2) % 3] = prevedge->Subdivide();
-	    child = mesh->NewFace(3, vertices, face, i);
+            vertices[i] = vertex->Subdivide();
+            vertices[(i + 1) % 3] = edge->Subdivide();
+            vertices[(i + 2) % 3] = prevedge->Subdivide();
+            child = mesh->NewFace(3, vertices, face, i);
 #ifdef HBR_DEBUG
-	    std::cerr << "Creating face " << *child << " during refine\n";
-#endif		    
+            std::cerr << "Creating face " << *child << " during refine\n";
+#endif
 
-	    // Hand down edge sharpness		
-	    float sharpness;
-	    HbrHalfedge* childedge;
+            // Hand down edge sharpness
+            float sharpness;
+            HbrHalfedge* childedge;
 
             childedge = child->GetEdge(i);
-	    if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+            if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
                 HbrSubdivision::SubdivideCreaseWeight(
                     edge, edge->GetDestVertex(), childedge);
             }
             childedge->CopyFVarInfiniteSharpness(edge);
 
             childedge = child->GetEdge((i+2)%3);
-	    if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		HbrSubdivision::SubdivideCreaseWeight(
+            if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                HbrSubdivision::SubdivideCreaseWeight(
                     prevedge, prevedge->GetOrgVertex(), childedge);
-	    }
+            }
             childedge->CopyFVarInfiniteSharpness(prevedge);
 
-	    if (mesh->GetTotalFVarWidth()) {
-		transferFVarToChild(mesh, face, child, i);
-	    }	    
+            if (mesh->GetTotalFVarWidth()) {
+                transferFVarToChild(mesh, face, child, i);
+            }
 
-	    transferEditsToChild(face, child, i);
+            transferEditsToChild(face, child, i);
 
-	}
-	prevedge = edge;
-	edge = edge->GetNext();	
+        }
+        prevedge = edge;
+        edge = edge->GetNext();
     }
 
     refineFaceAtMiddle(mesh, face);
@@ -545,55 +546,55 @@ HbrLoopSubdivision::RefineFaceAtVertex(HbrMesh* mesh, HbrFace* face, Hb
 #endif
     HbrHalfedge* edge = face->GetFirstEdge();
     HbrHalfedge* prevedge = edge->GetPrev();
-    
+
     for (int i = 0; i < 3; ++i) {
-	if (edge->GetOrgVertex() == vertex) {	
-	    if (!face->GetChild(i)) {
+        if (edge->GetOrgVertex() == vertex) {
+            if (!face->GetChild(i)) {
 #ifdef HBR_DEBUG
-		std::cerr << "Kid " << i << "\n";
+                std::cerr << "Kid " << i << "\n";
 #endif
-		HbrFace* child;
-		HbrVertex* vertices[3];
+                HbrFace* child;
+                HbrVertex* vertices[3];
 
-		vertices[i] = vertex->Subdivide();
-		vertices[(i + 1) % 3] = edge->Subdivide();
-		vertices[(i + 2) % 3] = prevedge->Subdivide();
-		child = mesh->NewFace(3, vertices, face, i);
+                vertices[i] = vertex->Subdivide();
+                vertices[(i + 1) % 3] = edge->Subdivide();
+                vertices[(i + 2) % 3] = prevedge->Subdivide();
+                child = mesh->NewFace(3, vertices, face, i);
 #ifdef HBR_DEBUG
-		std::cerr << "Creating face " << *child << " during refine\n";
-#endif		    
+                std::cerr << "Creating face " << *child << " during refine\n";
+#endif
 
-		// Hand down edge sharpness		
-		float sharpness;
-		HbrHalfedge* childedge;
+                // Hand down edge sharpness
+                float sharpness;
+                HbrHalfedge* childedge;
 
                 childedge = child->GetEdge(i);
-		if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		    HbrSubdivision::SubdivideCreaseWeight(
+                if ((sharpness = edge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                    HbrSubdivision::SubdivideCreaseWeight(
                         edge, edge->GetDestVertex(), childedge);
-		}
+                }
                 childedge->CopyFVarInfiniteSharpness(edge);
 
                 childedge = child->GetEdge((i+2)%3);
-		if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
-		    HbrSubdivision::SubdivideCreaseWeight(
+                if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge::k_Smooth) {
+                    HbrSubdivision::SubdivideCreaseWeight(
                         prevedge, prevedge->GetOrgVertex(), childedge);
-		}
+                }
                 childedge->CopyFVarInfiniteSharpness(prevedge);
 
-		if (mesh->GetTotalFVarWidth()) {
-		    transferFVarToChild(mesh, face, child, i);
-		}
+                if (mesh->GetTotalFVarWidth()) {
+                    transferFVarToChild(mesh, face, child, i);
+                }
 
-		transferEditsToChild(face, child, i);
-	    
-		return child;
-	    } else {
-		return face->GetChild(i);
-	    }
-	}
-	prevedge = edge;
-	edge = edge->GetNext();	
+                transferEditsToChild(face, child, i);
+
+                return child;
+            } else {
+                return face->GetChild(i);
+            }
+        }
+        prevedge = edge;
+        edge = edge->GetNext();
     }
     return 0;
 }
@@ -602,7 +603,7 @@ template 
 void
 HbrLoopSubdivision::GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge) {
     if (edge->GetOpposite()) {
-	return;
+        return;
     }
 
 #ifdef HBR_DEBUG
@@ -611,7 +612,7 @@ HbrLoopSubdivision::GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge)
 
     /*
       Imagine the following:
-       
+
                       X
                      / \
                     /   \
@@ -622,7 +623,7 @@ HbrLoopSubdivision::GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge)
                /    \        \
               X------X--------X
                  1
-    
+
      If the parent of _both_ incident vertices are themselves edges,
      (like the edge marked 3 above), then this edge is in the center
      of the parent face. Refining the parent face in the middle or
@@ -633,41 +634,41 @@ HbrLoopSubdivision::GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge)
     HbrHalfedge* parentEdge2 = edge->GetDestVertex()->GetParentEdge();
     if (parentEdge1 && parentEdge2) {
 #ifdef HBR_DEBUG
-	std::cerr << "two parent edge situation\n";
+        std::cerr << "two parent edge situation\n";
 #endif
-	HbrFace* parentFace = parentEdge1->GetFace();
-	assert(parentFace == parentEdge2->GetFace());
-	if(parentEdge1->GetOrgVertex() == parentEdge2->GetDestVertex()) {
-	    refineFaceAtMiddle(mesh, parentFace);
-	} else {
-	    RefineFaceAtVertex(mesh, parentFace, parentEdge1->GetOrgVertex());
-	}
-	assert(edge->GetOpposite());
-	return;
+        HbrFace* parentFace = parentEdge1->GetFace();
+        assert(parentFace == parentEdge2->GetFace());
+        if(parentEdge1->GetOrgVertex() == parentEdge2->GetDestVertex()) {
+            refineFaceAtMiddle(mesh, parentFace);
+        } else {
+            RefineFaceAtVertex(mesh, parentFace, parentEdge1->GetOrgVertex());
+        }
+        assert(edge->GetOpposite());
+        return;
     }
 
     // Otherwise we're in the situation of edge 1 or edge 2 in the
     // diagram above.
     if (parentEdge1) {
 #ifdef HBR_DEBUG
-	std::cerr << "parent edge 1 " << *parentEdge1 << "\n";
+        std::cerr << "parent edge 1 " << *parentEdge1 << "\n";
 #endif
-	HbrVertex* parentVertex2 = edge->GetDestVertex()->GetParentVertex();
-	assert(parentVertex2);
-	RefineFaceAtVertex(mesh, parentEdge1->GetLeftFace(), parentVertex2);
-	if (parentEdge1->GetRightFace()) {
-	    RefineFaceAtVertex(mesh, parentEdge1->GetRightFace(), parentVertex2);
-	}
+        HbrVertex* parentVertex2 = edge->GetDestVertex()->GetParentVertex();
+        assert(parentVertex2);
+        RefineFaceAtVertex(mesh, parentEdge1->GetLeftFace(), parentVertex2);
+        if (parentEdge1->GetRightFace()) {
+            RefineFaceAtVertex(mesh, parentEdge1->GetRightFace(), parentVertex2);
+        }
     } else if (parentEdge2) {
 #ifdef HBR_DEBUG
-	std::cerr << "parent edge 2 " << *parentEdge2 << "\n";
+        std::cerr << "parent edge 2 " << *parentEdge2 << "\n";
 #endif
-	HbrVertex* parentVertex1 = edge->GetOrgVertex()->GetParentVertex();
-	assert(parentVertex1);
-	RefineFaceAtVertex(mesh, parentEdge2->GetLeftFace(), parentVertex1);
-	if (parentEdge2->GetRightFace()) {
-	    RefineFaceAtVertex(mesh, parentEdge2->GetRightFace(), parentVertex1);
-	}
+        HbrVertex* parentVertex1 = edge->GetOrgVertex()->GetParentVertex();
+        assert(parentVertex1);
+        RefineFaceAtVertex(mesh, parentEdge2->GetLeftFace(), parentVertex1);
+        if (parentEdge2->GetRightFace()) {
+            RefineFaceAtVertex(mesh, parentEdge2->GetRightFace(), parentVertex1);
+        }
     }
 }
 
@@ -686,50 +687,50 @@ HbrLoopSubdivision::GuaranteeNeighbors(HbrMesh* mesh, HbrVertex* vertex
     // and have 1) refined at both vertices of the parent edge, and 2)
     // have refined their "middle" face (which doesn't live at either
     // vertex).
-    
+
     HbrHalfedge* parentEdge = vertex->GetParentEdge();
     if (parentEdge) {
 #ifdef HBR_DEBUG
-	std::cerr << "parent edge situation " << *parentEdge << "\n";
+        std::cerr << "parent edge situation " << *parentEdge << "\n";
 #endif
-	HbrVertex* dest = parentEdge->GetDestVertex();
-	HbrVertex* org = parentEdge->GetOrgVertex();
-	GuaranteeNeighbor(mesh, parentEdge);
-	HbrFace* parentFace = parentEdge->GetLeftFace();
-	RefineFaceAtVertex(mesh, parentFace, dest);
-	RefineFaceAtVertex(mesh, parentFace, org);
-	refineFaceAtMiddle(mesh, parentFace);
-	parentFace = parentEdge->GetRightFace();
-	// The right face may not necessarily exist even after
-	// GuaranteeNeighbor
-	if (parentFace) {
-	    RefineFaceAtVertex(mesh, parentFace, dest);
-	    RefineFaceAtVertex(mesh, parentFace, org);
-	    refineFaceAtMiddle(mesh, parentFace);	    
-	}
-	return;
-    }    
-    
+        HbrVertex* dest = parentEdge->GetDestVertex();
+        HbrVertex* org = parentEdge->GetOrgVertex();
+        GuaranteeNeighbor(mesh, parentEdge);
+        HbrFace* parentFace = parentEdge->GetLeftFace();
+        RefineFaceAtVertex(mesh, parentFace, dest);
+        RefineFaceAtVertex(mesh, parentFace, org);
+        refineFaceAtMiddle(mesh, parentFace);
+        parentFace = parentEdge->GetRightFace();
+        // The right face may not necessarily exist even after
+        // GuaranteeNeighbor
+        if (parentFace) {
+            RefineFaceAtVertex(mesh, parentFace, dest);
+            RefineFaceAtVertex(mesh, parentFace, org);
+            refineFaceAtMiddle(mesh, parentFace);
+        }
+        return;
+    }
+
     // The second case: the vertex is a child of a vertex. In this case
     // we have to recursively guarantee that the parent's adjacent
     // faces also exist.
     HbrVertex* parentVertex = vertex->GetParentVertex();
     if (parentVertex) {
 #ifdef HBR_DEBUG
-	std::cerr << "parent vertex situation " << *parentVertex << "\n";
+        std::cerr << "parent vertex situation " << *parentVertex << "\n";
 #endif
-	parentVertex->GuaranteeNeighbors();
+        parentVertex->GuaranteeNeighbors();
 
-	// And then we refine all the face neighbors of the parent
-	// vertex
-	HbrHalfedge* start = parentVertex->GetIncidentEdge(), *edge;
-	edge = start;
-	while (edge) {
-	    HbrFace* f = edge->GetLeftFace();
-	    RefineFaceAtVertex(mesh, f, parentVertex);
-	    edge = parentVertex->GetNextEdge(edge);
-	    if (edge == start) break;	    
-	}
+        // And then we refine all the face neighbors of the parent
+        // vertex
+        HbrHalfedge* start = parentVertex->GetIncidentEdge(), *edge;
+        edge = start;
+        while (edge) {
+            HbrFace* f = edge->GetLeftFace();
+            RefineFaceAtVertex(mesh, f, parentVertex);
+            edge = parentVertex->GetNextEdge(edge);
+            if (edge == start) break;
+        }
     }
 }
 
@@ -740,9 +741,9 @@ HbrLoopSubdivision::HasLimit(HbrMesh* mesh, HbrFace* face) {
     if (face->IsHole()) return false;
     // A limit face exists if all the bounding edges have limit curves
     for (int i = 0; i < face->GetNumVertices(); ++i) {
-	if (!HasLimit(mesh, face->GetEdge(i))) {
-	    return false;
-	}
+        if (!HasLimit(mesh, face->GetEdge(i))) {
+            return false;
+        }
     }
     return true;
 }
@@ -750,14 +751,14 @@ HbrLoopSubdivision::HasLimit(HbrMesh* mesh, HbrFace* face) {
 template 
 bool
 HbrLoopSubdivision::HasLimit(HbrMesh* mesh, HbrHalfedge* edge) {
-    // 	A sharp edge has a limit curve if both endpoints have limits.
-    // 	A smooth edge has a limit if both endpoints have limits and
-    // 	the edge isn't on the boundary.
+    //  A sharp edge has a limit curve if both endpoints have limits.
+    //  A smooth edge has a limit if both endpoints have limits and
+    //  the edge isn't on the boundary.
 
     if (edge->GetSharpness() >= HbrHalfedge::k_InfinitelySharp) return true;
-    
+
     if (!HasLimit(mesh, edge->GetOrgVertex()) || !HasLimit(mesh, edge->GetDestVertex())) return false;
-    
+
     return !edge->IsBoundary();
 }
 
@@ -766,21 +767,21 @@ bool
 HbrLoopSubdivision::HasLimit(HbrMesh* mesh, HbrVertex* vertex) {
     vertex->GuaranteeNeighbors();
     switch (vertex->GetMask(false)) {
-	case HbrVertex::k_Smooth:
-	case HbrVertex::k_Dart:
-	    return !vertex->OnBoundary();
-	    break;
-	case HbrVertex::k_Crease:
-	case HbrVertex::k_Corner:
-	default:
-	    if (vertex->IsVolatile()) {
-		// Search for any incident semisharp boundary edge
+        case HbrVertex::k_Smooth:
+        case HbrVertex::k_Dart:
+            return !vertex->OnBoundary();
+            break;
+        case HbrVertex::k_Crease:
+        case HbrVertex::k_Corner:
+        default:
+            if (vertex->IsVolatile()) {
+                // Search for any incident semisharp boundary edge
                 HbrHalfedge* start = vertex->GetIncidentEdge(), *edge, *next;
                 edge = start;
                 while (edge) {
-		    if (edge->IsBoundary() && edge->GetSharpness() < HbrHalfedge::k_InfinitelySharp) {
-			return false;
-		    }
+                    if (edge->IsBoundary() && edge->GetSharpness() < HbrHalfedge::k_InfinitelySharp) {
+                        return false;
+                    }
                     next = vertex->GetNextEdge(edge);
                     if (next == start) {
                         break;
@@ -793,9 +794,9 @@ HbrLoopSubdivision::HasLimit(HbrMesh* mesh, HbrVertex* vertex) {
                     } else {
                         edge = next;
                     }
-		}
-	    }
-	    return true;
+                }
+            }
+            return true;
     }
 }
 
@@ -824,46 +825,46 @@ HbrLoopSubdivision::Subdivide(HbrMesh* mesh, HbrHalfedge* edge) {
     // If there's the possibility of vertex edits on either vertex, we
     // have to make sure the edit has been applied
     if (mesh->HasVertexEdits()) {
-	edge->GetOrgVertex()->GuaranteeNeighbors();
-	edge->GetDestVertex()->GuaranteeNeighbors();
+        edge->GetOrgVertex()->GuaranteeNeighbors();
+        edge->GetDestVertex()->GuaranteeNeighbors();
     }
 
     if (!edge->IsBoundary() && esharp <= 1.0f) {
 
-	// Of the two half-edges, pick one of them consistently such
-	// that the org and dest vertices are also consistent through
-	// multi-threading. It doesn't matter as far as the
-	// theoretical calculation is concerned, but it is desirable
-	// to be consistent about it in the face of the limitations of
-	// floating point commutativity. So we always pick the
-	// half-edge such that its incident face is the smallest of
-	// the two faces, as far as the face paths are concerned.
-	if (edge->GetOpposite() && edge->GetOpposite()->GetFace()->GetPath() < edge->GetFace()->GetPath()) {
-	    edge = edge->GetOpposite();
-	}
-	
-	// Handle both the smooth and fractional sharpness cases.  We
-	// lerp between the sharp case (average of the two end points)
-	// and the unsharp case (3/8 of each of the two end points
-	// plus 1/8 of the two opposite face averages).
+        // Of the two half-edges, pick one of them consistently such
+        // that the org and dest vertices are also consistent through
+        // multi-threading. It doesn't matter as far as the
+        // theoretical calculation is concerned, but it is desirable
+        // to be consistent about it in the face of the limitations of
+        // floating point commutativity. So we always pick the
+        // half-edge such that its incident face is the smallest of
+        // the two faces, as far as the face paths are concerned.
+        if (edge->GetOpposite() && edge->GetOpposite()->GetFace()->GetPath() < edge->GetFace()->GetPath()) {
+            edge = edge->GetOpposite();
+        }
 
-	// Lerp end point weight between non sharp contribution of
-	// 3/8 and the sharp contribution of 0.5.
-	float endPtWeight = 0.375f + esharp * (0.5f - 0.375f);
-	data.AddWithWeight(edge->GetOrgVertex()->GetData(), endPtWeight);
-	data.AddWithWeight(edge->GetDestVertex()->GetData(), endPtWeight);
+        // Handle both the smooth and fractional sharpness cases.  We
+        // lerp between the sharp case (average of the two end points)
+        // and the unsharp case (3/8 of each of the two end points
+        // plus 1/8 of the two opposite face averages).
 
-	// Lerp the opposite pt weights between non sharp contribution
-	// of 1/8 and the sharp contribution of 0.
-	float oppPtWeight = 0.125f * (1 - esharp);
-	HbrHalfedge* ee = edge->GetNext();
-	data.AddWithWeight(ee->GetDestVertex()->GetData(), oppPtWeight);
-	ee = edge->GetOpposite()->GetNext();
-	data.AddWithWeight(ee->GetDestVertex()->GetData(), oppPtWeight);
+        // Lerp end point weight between non sharp contribution of
+        // 3/8 and the sharp contribution of 0.5.
+        float endPtWeight = 0.375f + esharp * (0.5f - 0.375f);
+        data.AddWithWeight(edge->GetOrgVertex()->GetData(), endPtWeight);
+        data.AddWithWeight(edge->GetDestVertex()->GetData(), endPtWeight);
+
+        // Lerp the opposite pt weights between non sharp contribution
+        // of 1/8 and the sharp contribution of 0.
+        float oppPtWeight = 0.125f * (1 - esharp);
+        HbrHalfedge* ee = edge->GetNext();
+        data.AddWithWeight(ee->GetDestVertex()->GetData(), oppPtWeight);
+        ee = edge->GetOpposite()->GetNext();
+        data.AddWithWeight(ee->GetDestVertex()->GetData(), oppPtWeight);
     } else {
-	// Fully sharp edge, just average the two end points
-	data.AddWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
-	data.AddWithWeight(edge->GetDestVertex()->GetData(), 0.5f);
+        // Fully sharp edge, just average the two end points
+        data.AddWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
+        data.AddWithWeight(edge->GetDestVertex()->GetData(), 0.5f);
     }
 
     // Varying data is always the average of two end points
@@ -876,7 +877,7 @@ HbrLoopSubdivision::Subdivide(HbrMesh* mesh, HbrHalfedge* edge) {
 
     // Only boundary edges will create extraordinary vertices
     if (edge->IsBoundary()) {
-	v->SetExtraordinary();
+        v->SetExtraordinary();
     }
     return v;
 }
@@ -888,7 +889,7 @@ HbrLoopSubdivision::Subdivide(HbrMesh* mesh, HbrVertex* vertex) {
     // Ensure the ring of faces around this vertex exists before
     // we compute the valence
     vertex->GuaranteeNeighbors();
-    
+
     float valence = static_cast(vertex->GetValence());
     float invvalence = 1.0f / valence;
 
@@ -907,63 +908,63 @@ HbrLoopSubdivision::Subdivide(HbrMesh* mesh, HbrVertex* vertex) {
     // subdivision, then use fractional mask weights to weigh
     // each weighing
     if (masks[0] != masks[1]) {
-	weights[1] = vertex->GetFractionalMask();
-	weights[0] = 1.0f - weights[1];
-	passes = 2;
+        weights[1] = vertex->GetFractionalMask();
+        weights[0] = 1.0f - weights[1];
+        passes = 2;
     } else {
-	weights[0] = 1.0f;
-	weights[1] = 0.0f;
-	passes = 1;
+        weights[0] = 1.0f;
+        weights[1] = 0.0f;
+        passes = 1;
     }
     for (int i = 0; i < passes; ++i) {
-	switch (masks[i]) {
-	    case HbrVertex::k_Smooth:
-	    case HbrVertex::k_Dart: {		
-		float beta = 0.25f * cosf((float)M_PI * 2.0f * invvalence) + 0.375f;
-		beta = beta * beta;
-		beta = (0.625f - beta) * invvalence;
+        switch (masks[i]) {
+            case HbrVertex::k_Smooth:
+            case HbrVertex::k_Dart: {
+                float beta = 0.25f * cosf((float)M_PI * 2.0f * invvalence) + 0.375f;
+                beta = beta * beta;
+                beta = (0.625f - beta) * invvalence;
 
-		data.AddWithWeight(vertex->GetData(), weights[i] * (1 - (beta * valence)));
+                data.AddWithWeight(vertex->GetData(), weights[i] * (1 - (beta * valence)));
 
                 HbrSubdivision::AddSurroundingVerticesWithWeight(
                     mesh, vertex, weights[i] * beta, &data);
                 break;
-	    }
-	    case HbrVertex::k_Crease: {
-		// Compute 3/4 of old vertex value
-		data.AddWithWeight(vertex->GetData(), weights[i] * 0.75f);
+            }
+            case HbrVertex::k_Crease: {
+                // Compute 3/4 of old vertex value
+                data.AddWithWeight(vertex->GetData(), weights[i] * 0.75f);
 
-		// Add 0.125f of the (hopefully only two!) neighbouring
-		// sharp edges
+                // Add 0.125f of the (hopefully only two!) neighbouring
+                // sharp edges
                 HbrSubdivision::AddCreaseEdgesWithWeight(
                     mesh, vertex, i == 1, weights[i] * 0.125f, &data);
-		break;
-	    }
-	    case HbrVertex::k_Corner:
-	    default: {
-		// Just copy the old value
-		data.AddWithWeight(vertex->GetData(), weights[i]);
-		break;
-	    }
-	}
+                break;
+            }
+            case HbrVertex::k_Corner:
+            default: {
+                // Just copy the old value
+                data.AddWithWeight(vertex->GetData(), weights[i]);
+                break;
+            }
+        }
     }
 
-    // Varying data is always just propogated down
+    // Varying data is always just propagated down
     data.AddVaryingWithWeight(vertex->GetData(), 1.0f);
-    
+
 #ifdef HBR_DEBUG
-    std::cerr << "Subdividing at " << *vertex << "\n";    
+    std::cerr << "Subdividing at " << *vertex << "\n";
     std::cerr << "  created " << *v << "\n";
 #endif
     // Inherit extraordinary flag and sharpness
     if (vertex->IsExtraordinary()) v->SetExtraordinary();
     float sharp = vertex->GetSharpness();
     if (sharp >= HbrVertex::k_InfinitelySharp) {
-	v->SetSharpness(HbrVertex::k_InfinitelySharp);
+        v->SetSharpness(HbrVertex::k_InfinitelySharp);
     } else if (sharp > HbrVertex::k_Smooth) {
-	v->SetSharpness(std::max((float) HbrVertex::k_Smooth, sharp - 1.0f));
+        v->SetSharpness(std::max((float) HbrVertex::k_Smooth, sharp - 1.0f));
     } else {
-	v->SetSharpness(HbrVertex::k_Smooth);
+        v->SetSharpness(HbrVertex::k_Smooth);
     }
     return v;
 }
@@ -974,28 +975,28 @@ HbrLoopSubdivision::refineFaceAtMiddle(HbrMesh* mesh, HbrFace* face) {
 
 #ifdef HBR_DEBUG
     std::cerr << "Refining middle face of " << *face << "\n";
-#endif	
-    
-    if (!face->GetChild(3)) {
-	HbrFace* child;
-	HbrVertex* vertices[3];
-
-	// The fourth face is not an obvious child of any vertex. We
-	// assign it index 3 despite there being no fourth vertex in
-	// the triangle. The ordering of vertices here is done to
-	// preserve parametric space as best we can
-	vertices[0] = face->GetEdge(1)->Subdivide();
-	vertices[1] = face->GetEdge(2)->Subdivide();
-	vertices[2] = face->GetEdge(0)->Subdivide();
-	child = mesh->NewFace(3, vertices, face, 3);
-#ifdef HBR_DEBUG
-	std::cerr << "Creating face " << *child << "\n";
 #endif
-	if (mesh->GetTotalFVarWidth()) {
-	    transferFVarToChild(mesh, face, child, 3);
-	}
 
-	transferEditsToChild(face, child, 3);
+    if (!face->GetChild(3)) {
+        HbrFace* child;
+        HbrVertex* vertices[3];
+
+        // The fourth face is not an obvious child of any vertex. We
+        // assign it index 3 despite there being no fourth vertex in
+        // the triangle. The ordering of vertices here is done to
+        // preserve parametric space as best we can
+        vertices[0] = face->GetEdge(1)->Subdivide();
+        vertices[1] = face->GetEdge(2)->Subdivide();
+        vertices[2] = face->GetEdge(0)->Subdivide();
+        child = mesh->NewFace(3, vertices, face, 3);
+#ifdef HBR_DEBUG
+        std::cerr << "Creating face " << *child << "\n";
+#endif
+        if (mesh->GetTotalFVarWidth()) {
+            transferFVarToChild(mesh, face, child, 3);
+        }
+
+        transferEditsToChild(face, child, 3);
     }
 }
 
diff --git a/opensubdiv/hbr/mesh.h b/opensubdiv/hbr/mesh.h
index 8a2db9fe..6db44394 100644
--- a/opensubdiv/hbr/mesh.h
+++ b/opensubdiv/hbr/mesh.h
@@ -59,8 +59,7 @@
 
 #include 
 #include 
-#include 
-#include 
+#include 
 #include 
 #include 
 
@@ -94,14 +93,14 @@ public:
 #endif
     );
     ~HbrMesh();
-    
+
     // Create vertex with the indicated ID and data
     HbrVertex* NewVertex(int id, const T &data);
 
     // Create vertex with the indicated data. The ID will be assigned
     // by the mesh.
     HbrVertex* NewVertex(const T &data);
-    
+
     // Create vertex without an ID - one will be assigned by the mesh,
     // and the data implicitly created will share the same id
     HbrVertex* NewVertex();
@@ -120,36 +119,36 @@ public:
 
     // Finishes initialization of the mesh
     void Finish();
-    
+
     // Remove the indicated face from the mesh
     void DeleteFace(HbrFace* face);
 
     // Remove the indicated vertex from the mesh
     void DeleteVertex(HbrVertex* vertex);
-    
+
     // Returns number of vertices in the mesh
     int GetNumVertices() const;
 
     // Returns number of disconnected vertices in the mesh
     int GetNumDisconnectedVertices() const;
 
-    // Returns number of faces in the mesh    
+    // Returns number of faces in the mesh
     int GetNumFaces() const;
 
-    // Returns number of coarse faces in the mesh    
+    // Returns number of coarse faces in the mesh
     int GetNumCoarseFaces() const;
 
     // Ask for face with the indicated ID
     HbrFace* GetFace(int id) const;
-    
+
     // Returns a collection of all vertices in the mesh
-    void GetVertices(std::list*>& vertices) const;
+    void GetVertices(std::vector*>& vertices) const;
 
     // Applies operator to all vertices
     void ApplyOperatorAllVertices(HbrVertexOperator &op) const;
-    
+
     // Returns a collection of all faces in the mesh
-    void GetFaces(std::list*>& faces) const;
+    void GetFaces(std::vector*>& faces) const;
 
     // Returns the subdivision method
     HbrSubdivision* GetSubdivision() const { return subdivision; }
@@ -159,7 +158,7 @@ public:
 
     // Return a table of the start index of each facevarying variable
     const int *GetFVarIndices() const { return fvarindices; }
-    
+
     // Return a table of the size of each facevarying variable
     const int *GetFVarWidths() const { return fvarwidths; }
 
@@ -177,9 +176,9 @@ public:
 
     // Interpolate boundary management
     enum InterpolateBoundaryMethod {
-	k_InterpolateBoundaryNone,
-	k_InterpolateBoundaryEdgeOnly,
-	k_InterpolateBoundaryEdgeAndCorner,
+        k_InterpolateBoundaryNone,
+        k_InterpolateBoundaryEdgeOnly,
+        k_InterpolateBoundaryEdgeAndCorner,
         k_InterpolateBoundaryAlwaysSharp
     };
 
@@ -190,15 +189,15 @@ public:
 
     bool GetFVarPropagateCorners() const { return fvarpropagatecorners; }
     void SetFVarPropagateCorners(bool p) { fvarpropagatecorners = p; }
-    
+
     // Register routines for keeping track of memory usage
     void RegisterMemoryRoutines(void (*increment)(unsigned long bytes), void (*decrement)(unsigned long bytes)) {
-	m_faceAllocator.SetMemStatsIncrement(increment);
-	m_faceAllocator.SetMemStatsDecrement(decrement);
-	m_vertexAllocator.SetMemStatsIncrement(increment);
-	m_vertexAllocator.SetMemStatsDecrement(decrement);
-	s_memStatsIncrement = increment;
-	s_memStatsDecrement = decrement;
+        m_faceAllocator.SetMemStatsIncrement(increment);
+        m_faceAllocator.SetMemStatsDecrement(decrement);
+        m_vertexAllocator.SetMemStatsIncrement(increment);
+        m_vertexAllocator.SetMemStatsDecrement(decrement);
+        s_memStatsIncrement = increment;
+        s_memStatsDecrement = decrement;
     }
 
     // Add a vertex to consider for garbage collection. All
@@ -219,37 +218,39 @@ public:
     // Add a new hierarchical edit to the mesh
     void AddHierarchicalEdit(HbrHierarchicalEdit* edit);
 
-    // Return a pointer to the beginning of the list of hierarchical edits
-    HbrHierarchicalEdit** GetHierarchicalEdits() const { return hierarchicalEditArray; }
+    // Return the hierarchical edits associated with the mesh
+    const std::vector*> &GetHierarchicalEdits() const {
+        return hierarchicalEdits;
+    }
 
     // Whether the mesh has certain types of edits
     bool HasVertexEdits() const { return hasVertexEdits; }
     bool HasCreaseEdits() const { return hasCreaseEdits; }
-    
+
     void Unrefine(int numCoarseVerts, int numCoarseFaces) {
-	static int oldMaxFaceID = 0;
-	if(oldMaxFaceID == 0) {
-	    oldMaxFaceID = numCoarseFaces;
-	}
-	for (int i = numCoarseFaces; i < maxFaceID; ++i) {
-	    if (faces[i]) {
-		HbrFace* f = faces[i];
-		if(f && not f->IsCoarse())
-		    DeleteFace(f);
-	    }
-	}
-	//oldMaxFaceID = maxFaceID;
-	maxFaceID = numCoarseFaces;
+        static int oldMaxFaceID = 0;
+        if(oldMaxFaceID == 0) {
+            oldMaxFaceID = numCoarseFaces;
+        }
+        for (int i = numCoarseFaces; i < maxFaceID; ++i) {
+            if (faces[i]) {
+                HbrFace* f = faces[i];
+                if(f && not f->IsCoarse())
+                    DeleteFace(f);
+            }
+        }
+        //oldMaxFaceID = maxFaceID;
+        maxFaceID = numCoarseFaces;
 
         int vert = numCoarseVerts % vsetsize;
-        for( int set=(numCoarseVerts/vsetsize); set* v = vertices[set][vert];
-		if(v && not v->IsReferenced()) 
-		    DeleteVertex(v);
-	    }
+        for( int set=(numCoarseVerts/vsetsize); set* v = vertices[set][vert];
+                if(v && not v->IsReferenced())
+                    DeleteVertex(v);
+            }
             vert = 0;
-	}
+        }
     }
 
     // Whether the mesh is in "transient" mode, i.e. all
@@ -257,9 +258,9 @@ public:
     void SetTransientMode(bool mode) {
         m_transientMode = mode;
     }
-    
+
     void FreeTransientData();
-    
+
 private:
     // The mutex type depends on where hbr is being used.
     #if PRMAN
@@ -277,8 +278,8 @@ private:
     public:
          ScopedLock(Mutex *mutex) : _mutex(mutex) {
              mutex->Lock();
-	 }
-	
+         }
+
         ~ScopedLock() {
             Release();
         }
@@ -292,12 +293,12 @@ private:
         Mutex *_mutex;
     };
     #endif
-    
+
     // Mutex used to lock access to the "vertices" data member.
     mutable Mutex m_verticesMutex;
 
 private:
-    
+
     // Subdivision method used in this mesh
     HbrSubdivision* subdivision;
 
@@ -316,7 +317,7 @@ private:
 #ifdef HBRSTITCH
     const int stitchCount;
 #endif
-    
+
     // Vertices which comprise this mesh
     HbrVertex*** vertices;
     int nvsets;
@@ -344,28 +345,22 @@ private:
 
     // Whether facevarying corners propagate their sharpness
     bool fvarpropagatecorners;
-    
+
     // Memory statistics tracking routines
     HbrMemStatFunction s_memStatsIncrement;
     HbrMemStatFunction s_memStatsDecrement;
 
     // Vertices which may be garbage collected
-    std::deque*> gcVertices;
+    std::vector*> gcVertices;
 
     // List of vertex IDs which may be recycled
     std::set recycleIDs;
 
-    // Sorted hierarchical edits. This set is valid only until
-    // Finish() is called, at which point the mesh should switch over
-    // to using hierarchicalEditArray
-    std::multiset*, HbrHierarchicalEditComparator > hierarchicalEditSet;
-
-    // Sorted array of hierarchical edits. This array is valid only
-    // after Finish() has been called. Note that HbrFaces have
-    // pointers directly into this array so manipulation of it should
-    // be avoided
-    int nHierarchicalEdits;
-    HbrHierarchicalEdit** hierarchicalEditArray;
+    // Hierarchical edits. This vector is left unsorted until Finish()
+    // is called, at which point it is sorted. After that point,
+    // HbrFaces have pointers directly into this array so manipulation
+    // of it should be avoided.
+    std::vector*> hierarchicalEdits;
 
     // Size of faces (including 4 facevarying bits and stitch edges)
     const size_t m_faceSize;
@@ -373,14 +368,14 @@ private:
 
     // Size of vertices (includes storage for one piece of facevarying data)
     const size_t m_vertexSize;
-    HbrAllocator > m_vertexAllocator;    
+    HbrAllocator > m_vertexAllocator;
 
     // Memory used by this mesh alone, plus all its faces and vertices
     size_t m_memory;
 
     // Number of coarse faces. Initialized at Finish()
     int m_numCoarseFaces;
-    
+
     // Flags which indicate whether the mesh has certain types of
     // edits
     unsigned hasVertexEdits:1;
@@ -392,11 +387,11 @@ private:
     bool m_transientMode;
 
     // Vertices which are transient
-    std::deque*> m_transientVertices;
+    std::vector*> m_transientVertices;
 
     // Faces which are transient
-    std::deque*> m_transientFaces;
-    
+    std::vector*> m_transientFaces;
+
 };
 
 } // end namespace OPENSUBDIV_VERSION
@@ -429,8 +424,6 @@ HbrMesh::HbrMesh(HbrSubdivision* s, int _fvarcount, const int *_fvarindice
       fvarinterpboundarymethod(k_InterpolateBoundaryNone),
       fvarpropagatecorners(false),
       s_memStatsIncrement(0), s_memStatsDecrement(0),
-      nHierarchicalEdits(0),
-      hierarchicalEditArray(0),
       m_faceSize(sizeof(HbrFace) + 4 *
                  ((fvarcount + 15) / 16 * sizeof(unsigned int)
 #ifdef HBRSTITCH
@@ -438,11 +431,11 @@ HbrMesh::HbrMesh(HbrSubdivision* s, int _fvarcount, const int *_fvarindice
                  + sizeof(void*) // for stitch data
 #endif
                   )),
-      m_faceAllocator(&m_memory, 64, 0, 0, m_faceSize),
+      m_faceAllocator(&m_memory, 512, 0, 0, m_faceSize),
       m_vertexSize(sizeof(HbrVertex) +
                    sizeof(HbrHalfedge*) + // for incidentEdges[1]
                    totalfvarwidth * sizeof(float) + sizeof(HbrFVarData)),
-      m_vertexAllocator(&m_memory, 64, 0, 0, m_vertexSize),
+      m_vertexAllocator(&m_memory, 512, 0, 0, m_vertexSize),
       m_memory(0),
       m_numCoarseFaces(-1),
       hasVertexEdits(0),
@@ -456,17 +449,17 @@ HbrMesh::~HbrMesh() {
 
     int i;
     if (faces) {
-	for (i = 0; i < nfaces; ++i) {	
-	    if (faces[i]) {
-		faces[i]->Destroy();
-		m_faceAllocator.Deallocate(faces[i]);
-	    }
-	}
-	if (s_memStatsDecrement) {
-	    s_memStatsDecrement(nfaces * sizeof(HbrFace*));
-	}
+        for (i = 0; i < nfaces; ++i) {
+            if (faces[i]) {
+                faces[i]->Destroy();
+                m_faceAllocator.Deallocate(faces[i]);
+            }
+        }
+        if (s_memStatsDecrement) {
+            s_memStatsDecrement(nfaces * sizeof(HbrFace*));
+        }
         m_memory -= nfaces * sizeof(HbrFace*);
-	delete[] faces;
+        delete[] faces;
     }
 
     if (nvsets) {
@@ -483,14 +476,12 @@ HbrMesh::~HbrMesh() {
                 s_memStatsDecrement(vsetsize * sizeof(HbrVertex*));
             }
             m_memory -= vsetsize * sizeof(HbrVertex*);
-	}
+        }
         delete[] vertices;
     }
-    if (hierarchicalEditArray) {
-	for (i = 0; i < nHierarchicalEdits; ++i) {
-	    delete hierarchicalEditArray[i];
-	}
-	delete[] hierarchicalEditArray;
+    for (typename std::vector* >::iterator hi =
+             hierarchicalEdits.begin(); hi != hierarchicalEdits.end(); ++hi) {
+        delete *hi;
     }
 }
 
@@ -501,11 +492,11 @@ HbrMesh::NewVertex(int id, const T &data) {
 
     int arrayindex = id / vsetsize;
     int vertindex = id % vsetsize;
-#if PRMAN or MENV    
+#if PRMAN or MENV
     ScopedLock lock(&m_verticesMutex);
 #else
     IlmThread::Lock lock(m_verticesMutex);
-#endif    
+#endif
     HbrVertex** vset = 0;
     if (arrayindex >= nvsets) {
         HbrVertex*** nvertices = new HbrVertex**[arrayindex + 1];
@@ -526,22 +517,22 @@ HbrMesh::NewVertex(int id, const T &data) {
         vertices = nvertices;
     }
     vset = vertices[arrayindex];
-#if PRMAN or MENV    
+#if PRMAN or MENV
     lock.Release();
 #else
     lock.release();
-#endif    
+#endif
     v = vset[vertindex];
     if (v) {
-	v->Destroy();
+        v->Destroy();
     } else {
-	v = m_vertexAllocator.Allocate();
+        v = m_vertexAllocator.Allocate();
     }
     v->Initialize(id, data, GetTotalFVarWidth());
     vset[vertindex] = v;
-    
+
     if (id >= maxVertexID) {
-	maxVertexID = id + 1;
+        maxVertexID = id + 1;
     }
 
     // Newly created vertices are always candidates for garbage
@@ -563,11 +554,11 @@ HbrMesh::NewVertex(const T &data) {
     // we can
     int id = maxVertexID;
     if (!recycleIDs.empty()) {
-	id = *recycleIDs.begin();
+        id = *recycleIDs.begin();
         recycleIDs.erase(recycleIDs.begin());
     }
     if (id >= maxVertexID) {
-	maxVertexID = id + 1;
+        maxVertexID = id + 1;
     }
     return NewVertex(id, data);
 }
@@ -579,11 +570,11 @@ HbrMesh::NewVertex() {
     // we can
     int id = maxVertexID;
     if (!recycleIDs.empty()) {
-	id = *recycleIDs.begin();
-        recycleIDs.erase(recycleIDs.begin());        
+        id = *recycleIDs.begin();
+        recycleIDs.erase(recycleIDs.begin());
     }
     if (id >= maxVertexID) {
-	maxVertexID = id + 1;
+        maxVertexID = id + 1;
     }
     T data(id);
     data.Clear();
@@ -596,11 +587,11 @@ HbrMesh::GetVertex(int id) const {
     int arrayindex = id / vsetsize;
     int vertindex = id % vsetsize;
 
-#if PRMAN or MENV    
+#if PRMAN or MENV
     ScopedLock lock(&m_verticesMutex);
 #else
     IlmThread::Lock lock(m_verticesMutex);
-#endif    
+#endif
     if (arrayindex >= nvsets) {
         return 0;
     }
@@ -614,45 +605,45 @@ HbrMesh::NewFace(int nv, int *vtx, int uindex) {
     HbrVertex** facevertices = reinterpret_cast**>(alloca(sizeof(HbrVertex*) * nv));
     int i;
     for (i = 0; i < nv; ++i) {
-	facevertices[i] = GetVertex(vtx[i]);
-	if (!facevertices[i]) {
-	    return 0;
-	}
+        facevertices[i] = GetVertex(vtx[i]);
+        if (!facevertices[i]) {
+            return 0;
+        }
     }
     HbrFace *f = 0;
     // Resize if needed
     if (nfaces <= maxFaceID) {
-	int nnfaces = nfaces;
-	while (nnfaces <= maxFaceID) {
+        int nnfaces = nfaces;
+        while (nnfaces <= maxFaceID) {
             nnfaces *= 2;
             if (nnfaces < 1) nnfaces = 1;
-	}
-	HbrFace** newfaces = new HbrFace*[nnfaces];
-	if (s_memStatsIncrement) {
-	    s_memStatsIncrement(nnfaces * sizeof(HbrFace*));
-	}
+        }
+        HbrFace** newfaces = new HbrFace*[nnfaces];
+        if (s_memStatsIncrement) {
+            s_memStatsIncrement(nnfaces * sizeof(HbrFace*));
+        }
         m_memory += nnfaces * sizeof(HbrFace*);
-	if (faces) {
-	    for (i = 0; i < nfaces; ++i) {
-		newfaces[i] = faces[i];
-	    }
-	    if (s_memStatsDecrement) {
-		s_memStatsDecrement(nfaces * sizeof(HbrFace*));
-	    }
+        if (faces) {
+            for (i = 0; i < nfaces; ++i) {
+                newfaces[i] = faces[i];
+            }
+            if (s_memStatsDecrement) {
+                s_memStatsDecrement(nfaces * sizeof(HbrFace*));
+            }
             m_memory -= nfaces * sizeof(HbrFace*);
-	    delete[] faces;
-	}
-	for (i = nfaces; i < nnfaces; ++i) {
-	    newfaces[i] = 0;
-	}
-	faces = newfaces;
-	nfaces = nnfaces;
+            delete[] faces;
+        }
+        for (i = nfaces; i < nnfaces; ++i) {
+            newfaces[i] = 0;
+        }
+        faces = newfaces;
+        nfaces = nnfaces;
     }
     f = faces[maxFaceID];
     if (f) {
-	f->Destroy();
+        f->Destroy();
     } else {
-	f = m_faceAllocator.Allocate();
+        f = m_faceAllocator.Allocate();
     }
     f->Initialize(this, NULL, -1, maxFaceID, uindex, nv, facevertices, totalfvarwidth, 0);
     faces[maxFaceID] = f;
@@ -673,37 +664,37 @@ HbrMesh::NewFace(int nv, HbrVertex **vtx, HbrFace* parent, int childind
     HbrFace *f = 0;
     // Resize if needed
     if (nfaces <= maxFaceID) {
-	int nnfaces = nfaces;
-	while (nnfaces <= maxFaceID) {
+        int nnfaces = nfaces;
+        while (nnfaces <= maxFaceID) {
             nnfaces *= 2;
             if (nnfaces < 1) nnfaces = 1;
-	}
-	HbrFace** newfaces = new HbrFace*[nnfaces];
-	if (s_memStatsIncrement) {
-	    s_memStatsIncrement(nnfaces * sizeof(HbrFace*));
-	}
+        }
+        HbrFace** newfaces = new HbrFace*[nnfaces];
+        if (s_memStatsIncrement) {
+            s_memStatsIncrement(nnfaces * sizeof(HbrFace*));
+        }
         m_memory += nnfaces * sizeof(HbrFace*);
-	if (faces) {
-	    for (int i = 0; i < nfaces; ++i) {
-		newfaces[i] = faces[i];
-	    }
-	    if (s_memStatsDecrement) {
-		s_memStatsDecrement(nfaces * sizeof(HbrFace*));
-	    }
+        if (faces) {
+            for (int i = 0; i < nfaces; ++i) {
+                newfaces[i] = faces[i];
+            }
+            if (s_memStatsDecrement) {
+                s_memStatsDecrement(nfaces * sizeof(HbrFace*));
+            }
             m_memory -= nfaces * sizeof(HbrFace*);
-	    delete[] faces;
-	}
-	for (int i = nfaces; i < nnfaces; ++i) {
-	    newfaces[i] = 0;
-	}
-	faces = newfaces;
-	nfaces = nnfaces;
-    }    
+            delete[] faces;
+        }
+        for (int i = nfaces; i < nnfaces; ++i) {
+            newfaces[i] = 0;
+        }
+        faces = newfaces;
+        nfaces = nnfaces;
+    }
     f = faces[maxFaceID];
     if (f) {
-	f->Destroy();
+        f->Destroy();
     } else {
-	f = m_faceAllocator.Allocate();
+        f = m_faceAllocator.Allocate();
     }
     f->Initialize(this, parent, childindex, maxFaceID, parent ? parent->GetUniformIndex() : 0, nv, vtx, totalfvarwidth, parent ? parent->GetDepth() + 1 : 0);
     if (parent) {
@@ -725,36 +716,36 @@ HbrMesh::Finish() {
     int i, j;
     m_numCoarseFaces = 0;
     for (i = 0; i < nfaces; ++i) {
-	if (faces[i]) {
+        if (faces[i]) {
             faces[i]->SetCoarse();
             m_numCoarseFaces++;
         }
     }
 
-    std::list*> vertexlist;
+    std::vector*> vertexlist;
     GetVertices(vertexlist);
-    for (typename std::list*>::iterator vi = vertexlist.begin();
+    for (typename std::vector*>::iterator vi = vertexlist.begin();
          vi != vertexlist.end(); ++vi) {
         HbrVertex* vertex = *vi;
         if (vertex->IsConnected()) vertex->Finish();
     }
     // If interpolateboundary is on, process boundary edges
     if (interpboundarymethod == k_InterpolateBoundaryEdgeOnly || interpboundarymethod == k_InterpolateBoundaryEdgeAndCorner) {
-	for (i = 0; i < nfaces; ++i) {
-	    if (HbrFace* face = faces[i]) {
-		int nv = face->GetNumVertices();
-		for (int k = 0; k < nv; ++k) {
-		    HbrHalfedge* edge = face->GetEdge(k);
-		    if (edge->IsBoundary()) {
-			edge->SetSharpness(HbrHalfedge::k_InfinitelySharp);
-		    }
-		}
-	    }
-	}
+        for (i = 0; i < nfaces; ++i) {
+            if (HbrFace* face = faces[i]) {
+                int nv = face->GetNumVertices();
+                for (int k = 0; k < nv; ++k) {
+                    HbrHalfedge* edge = face->GetEdge(k);
+                    if (edge->IsBoundary()) {
+                        edge->SetSharpness(HbrHalfedge::k_InfinitelySharp);
+                    }
+                }
+            }
+        }
     }
     // Process corners
     if (interpboundarymethod == k_InterpolateBoundaryEdgeAndCorner) {
-        for (typename std::list*>::iterator vi = vertexlist.begin();
+        for (typename std::vector*>::iterator vi = vertexlist.begin();
              vi != vertexlist.end(); ++vi) {
             HbrVertex* vertex = *vi;
             if (vertex && vertex->IsConnected() && vertex->OnBoundary() && vertex->GetCoarseValence() == 2) {
@@ -762,31 +753,27 @@ HbrMesh::Finish() {
             }
         }
     }
-    // Convert the sorted set of hierarchical edits to an array
-    nHierarchicalEdits = hierarchicalEditSet.size();
-    if (nHierarchicalEdits) {
-	// Size the array by one extra; the last will be a sentinel
-	// value (null)
-	hierarchicalEditArray = new HbrHierarchicalEdit*[nHierarchicalEdits + 1];
-	i = 0;
-	for (typename std::multiset*, HbrHierarchicalEditComparator >::iterator pi = hierarchicalEditSet.begin(); pi != hierarchicalEditSet.end(); ++pi) {
-	    hierarchicalEditArray[i++] = *pi;
-	}
-	assert(i == nHierarchicalEdits);
-	hierarchicalEditArray[i] = 0;
+
+    // Sort the hierarchical edits
+    if (!hierarchicalEdits.empty()) {
+        HbrHierarchicalEditComparator cmp;
+        int nHierarchicalEdits = (int)hierarchicalEdits.size();
+        std::sort(hierarchicalEdits.begin(), hierarchicalEdits.end(), cmp);
+        // Push a sentinel null value - we rely upon this sentinel to
+        // ensure face->GetHierarchicalEdits knows when to terminate
+        hierarchicalEdits.push_back(0);
 	j = 0;
 	// Link faces to hierarchical edits
 	for (i = 0; i < nfaces; ++i) {
 	    if (faces[i]) {
-		while (j < nHierarchicalEdits && hierarchicalEditArray[j]->GetFaceID() < i) {
+		while (j < nHierarchicalEdits && hierarchicalEdits[j]->GetFaceID() < i) {
 		    ++j;
 		}
-		if (j < nHierarchicalEdits && hierarchicalEditArray[j]->GetFaceID() == i) {
-		    faces[i]->SetHierarchicalEdits(&hierarchicalEditArray[j]);
+		if (j < nHierarchicalEdits && hierarchicalEdits[j]->GetFaceID() == i) {
+		    faces[i]->SetHierarchicalEdits(&hierarchicalEdits[j]);
 		}
 	    }
 	}
-	hierarchicalEditSet.clear();
     }
 }
 
@@ -794,12 +781,12 @@ template 
 void
 HbrMesh::DeleteFace(HbrFace* face) {
     if (face->GetID() < nfaces) {
-	HbrFace* f = faces[face->GetID()];
-	if (f == face) {
-	    faces[face->GetID()] = 0;
-	    face->Destroy();
-	    m_faceAllocator.Deallocate(face);
-	}
+        HbrFace* f = faces[face->GetID()];
+        if (f == face) {
+            faces[face->GetID()] = 0;
+            face->Destroy();
+            m_faceAllocator.Deallocate(face);
+        }
     }
 }
 
@@ -811,18 +798,18 @@ HbrMesh::DeleteVertex(HbrVertex* vertex) {
         recycleIDs.insert(vertex->GetID());
         int id = vertex->GetID();
         int arrayindex = id / vsetsize;
-#if PRMAN or MENV    
+#if PRMAN or MENV
     ScopedLock lock(&m_verticesMutex);
 #else
     IlmThread::Lock lock(m_verticesMutex);
-#endif    
+#endif
         int vertindex = id % vsetsize;
         HbrVertex** vset = vertices[arrayindex];
-#if PRMAN or MENV    
+#if PRMAN or MENV
     lock.Release();
 #else
     lock.release();
-#endif    
+#endif
         vset[vertindex] = 0;
         vertex->Destroy();
         m_vertexAllocator.Deallocate(vertex);
@@ -833,11 +820,11 @@ template 
 int
 HbrMesh::GetNumVertices() const {
     int count = 0;
-#if PRMAN or MENV    
+#if PRMAN or MENV
     ScopedLock lock(&m_verticesMutex);
 #else
     IlmThread::Lock lock(m_verticesMutex);
-#endif    
+#endif
     for (int vi = 0; vi < nvsets; ++vi) {
         HbrVertex** vset = vertices[vi];
         for (int i = 0; i < vsetsize; ++i) {
@@ -851,11 +838,11 @@ template 
 int
 HbrMesh::GetNumDisconnectedVertices() const {
     int disconnected = 0;
-#if PRMAN or MENV    
+#if PRMAN or MENV
     ScopedLock lock(&m_verticesMutex);
 #else
     IlmThread::Lock lock(m_verticesMutex);
-#endif    
+#endif
     for (int vi = 0; vi < nvsets; ++vi) {
         HbrVertex** vset = vertices[vi];
         for (int i = 0; i < vsetsize; ++i) {
@@ -874,7 +861,7 @@ int
 HbrMesh::GetNumFaces() const {
     int count = 0;
     for (int i = 0; i < nfaces; ++i) {
-	if (faces[i]) count++;
+        if (faces[i]) count++;
     }
     return count;
 }
@@ -887,7 +874,7 @@ HbrMesh::GetNumCoarseFaces() const {
     // Otherwise we have to just count it up now
     int count = 0;
     for (int i = 0; i < nfaces; ++i) {
-	if (faces[i] && faces[i]->IsCoarse()) count++;
+        if (faces[i] && faces[i]->IsCoarse()) count++;
     }
     return count;
 }
@@ -896,19 +883,19 @@ template 
 HbrFace*
 HbrMesh::GetFace(int id) const {
     if (id < nfaces) {
-	return faces[id];
+        return faces[id];
     }
     return 0;
 }
 
 template 
 void
-HbrMesh::GetVertices(std::list*>& lvertices) const {
-#if PRMAN or MENV    
+HbrMesh::GetVertices(std::vector*>& lvertices) const {
+#if PRMAN or MENV
     ScopedLock lock(&m_verticesMutex);
 #else
     IlmThread::Lock lock(m_verticesMutex);
-#endif    
+#endif
     for (int vi = 0; vi < nvsets; ++vi) {
         HbrVertex** vset = vertices[vi];
         for (int i = 0; i < vsetsize; ++i) {
@@ -920,11 +907,11 @@ HbrMesh::GetVertices(std::list*>& lvertices) const {
 template 
 void
 HbrMesh::ApplyOperatorAllVertices(HbrVertexOperator &op) const {
-#if PRMAN or MENV    
+#if PRMAN or MENV
     ScopedLock lock(&m_verticesMutex);
 #else
     IlmThread::Lock lock(m_verticesMutex);
-#endif    
+#endif
     for (int vi = 0; vi < nvsets; ++vi) {
         HbrVertex** vset = vertices[vi];
         for (int i = 0; i < vsetsize; ++i) {
@@ -935,9 +922,9 @@ HbrMesh::ApplyOperatorAllVertices(HbrVertexOperator &op) const {
 
 template 
 void
-HbrMesh::GetFaces(std::list*>& lfaces) const {
+HbrMesh::GetFaces(std::vector*>& lfaces) const {
     for (int i = 0; i < nfaces; ++i) {
-	if (faces[i]) lfaces.push_back(faces[i]);
+        if (faces[i]) lfaces.push_back(faces[i]);
     }
 }
 
@@ -959,7 +946,7 @@ HbrMesh::PrintStats(std::ostream &out) {
                     singular++;
                 }
                 else if (!v->IsConnected()) {
-                    out << "  disconnected: " << *v << "\n";		
+                    out << "  disconnected: " << *v << "\n";
                     disconnected++;
                 } else {
                     if (v->IsExtraordinary()) {
@@ -979,74 +966,80 @@ HbrMesh::PrintStats(std::ostream &out) {
     int sumsides = 0;
     int numfaces = 0;
     for (i = 0; i < nfaces; ++i) {
-	if (HbrFace* f = faces[i]) {
-	    numfaces++;
-	    sumsides += f->GetNumVertices();
-	}
+        if (HbrFace* f = faces[i]) {
+            numfaces++;
+            sumsides += f->GetNumVertices();
+        }
     }
     out << "Mesh has " << nfaces << " faces\n";
     out << "Average sidedness " << (float) sumsides / nfaces << "\n";
 }
 
-#define HBR_MESH_BUFFERSIZE 4096
-
 template 
 void
 HbrMesh::GarbageCollect() {
     if (gcVertices.empty()) return;
 
-    if (gcVertices.size() <= HBR_MESH_BUFFERSIZE) return;
+    static const size_t gcthreshold = 4096;
+
+    if (gcVertices.size() <= gcthreshold) return;
     // Go through the list of garbage collectable vertices and gather
     // up the neighboring faces of those vertices which can be garbage
     // collected.
-    std::list*> killlist;
-    std::list*> vlist;
+    std::vector*> killlist;
+    std::vector*> vlist;
 
-    while (gcVertices.size() > HBR_MESH_BUFFERSIZE / 2) {
-	HbrVertex* v = gcVertices.front(); gcVertices.pop_front();
-	v->ClearCollected();
-	if (v->IsUsed()) continue;
-	vlist.push_back(v);
-	HbrHalfedge* start = v->GetIncidentEdge(), *edge;
-	edge = start;
-	while (edge) {
-	    HbrFace* f = edge->GetLeftFace();
-	    if (!f->IsCollected()) {
+    // Process the vertices in the same order as they were collected
+    // (gcVertices used to be declared as a std::deque, but that was
+    // causing unnecessary heap traffic).
+    int numprocessed = gcVertices.size() - gcthreshold / 2;
+    for (int i = 0; i < numprocessed; ++i) {
+        HbrVertex* v = gcVertices[i];
+        v->ClearCollected();
+        if (v->IsUsed()) continue;
+        vlist.push_back(v);
+        HbrHalfedge* start = v->GetIncidentEdge(), *edge;
+        edge = start;
+        while (edge) {
+            HbrFace* f = edge->GetLeftFace();
+            if (!f->IsCollected()) {
                 f->SetCollected();
-		killlist.push_back(f);
-	    }
-	    edge = v->GetNextEdge(edge);
-	    if (edge == start) break;
-	}
+                killlist.push_back(f);
+            }
+            edge = v->GetNextEdge(edge);
+            if (edge == start) break;
+        }
     }
 
+    gcVertices.erase(gcVertices.begin(), gcVertices.begin() + numprocessed);
+
     // Delete those faces
-    for (typename std::list*>::iterator fi = killlist.begin(); fi != killlist.end(); ++fi) {
-	if ((*fi)->GarbageCollectable()) {
-	    DeleteFace(*fi);
-	} else {
-	    (*fi)->ClearCollected();
+    for (typename std::vector*>::iterator fi = killlist.begin(); fi != killlist.end(); ++fi) {
+        if ((*fi)->GarbageCollectable()) {
+            DeleteFace(*fi);
+        } else {
+            (*fi)->ClearCollected();
         }
     }
 
     // Delete as many vertices as we can
-    for (typename std::list*>::iterator vi = vlist.begin(); vi != vlist.end(); ++vi) {
-	HbrVertex* v = *vi;
-	if (!v->IsReferenced()) {
-	    DeleteVertex(v);
-	}
+    for (typename std::vector*>::iterator vi = vlist.begin(); vi != vlist.end(); ++vi) {
+        HbrVertex* v = *vi;
+        if (!v->IsReferenced()) {
+            DeleteVertex(v);
+        }
     }
 }
 
 template 
 void
 HbrMesh::AddHierarchicalEdit(HbrHierarchicalEdit* edit) {
-    hierarchicalEditSet.insert(edit);
+    hierarchicalEdits.push_back(edit);
     if (dynamic_cast*>(edit) ||
-	dynamic_cast*>(edit)) {
-	hasVertexEdits = 1;
+        dynamic_cast*>(edit)) {
+        hasVertexEdits = 1;
     } else if (dynamic_cast*>(edit)) {
-	hasCreaseEdits = 1;
+        hasCreaseEdits = 1;
     }
 }
 
@@ -1054,13 +1047,13 @@ template 
 void
 HbrMesh::FreeTransientData() {
     // When purging transient data, we must clear the faces first
-    for (typename std::deque*>::iterator fi = m_transientFaces.begin();
+    for (typename std::vector*>::iterator fi = m_transientFaces.begin();
          fi != m_transientFaces.end(); ++fi) {
         DeleteFace(*fi);
     }
     // The vertices should now be trivial to purge after the transient
     // faces have been cleared
-    for (typename std::deque*>::iterator vi = m_transientVertices.begin();
+    for (typename std::vector*>::iterator vi = m_transientVertices.begin();
          vi != m_transientVertices.end(); ++vi) {
         DeleteVertex(*vi);
     }
@@ -1075,11 +1068,11 @@ HbrMesh::FreeTransientData() {
         }
     }
     // Reset max vertex ID. Slightly more complicated
-#if PRMAN or MENV    
+#if PRMAN or MENV
     ScopedLock lock(&m_verticesMutex);
 #else
     IlmThread::Lock lock(m_verticesMutex);
-#endif    
+#endif
     for (i = (nvsets * vsetsize) - 1; i >= 0; --i) {
         int arrayindex = i / vsetsize;
         int vertindex = i % vsetsize;
diff --git a/opensubdiv/hbr/subdivision.h b/opensubdiv/hbr/subdivision.h
index eaddec17..e6c3afe1 100644
--- a/opensubdiv/hbr/subdivision.h
+++ b/opensubdiv/hbr/subdivision.h
@@ -69,12 +69,12 @@ template  class HbrMesh;
 template  class HbrSubdivision {
 public:
     HbrSubdivision()
-	: creaseSubdivision(k_CreaseNormal) {}
+        : creaseSubdivision(k_CreaseNormal) {}
 
     virtual ~HbrSubdivision() {}
 
     virtual HbrSubdivision* Clone() const = 0;
-    
+
     // How to subdivide a face
     virtual void Refine(HbrMesh* mesh, HbrFace* face) = 0;
 
@@ -83,7 +83,7 @@ public:
 
     // Refine all faces around a particular vertex
     virtual void RefineAtVertex(HbrMesh* mesh, HbrVertex* vertex);
-    
+
     // Given an edge, try to ensure the edge's opposite exists by
     // forcing refinement up the hierarchy
     virtual void GuaranteeNeighbor(HbrMesh* mesh, HbrHalfedge* edge) = 0;
@@ -97,7 +97,7 @@ public:
     virtual bool HasLimit(HbrMesh* /* mesh */, HbrFace* /* face */) { return true; }
     virtual bool HasLimit(HbrMesh* /* mesh */, HbrHalfedge* /* edge */) { return true; }
     virtual bool HasLimit(HbrMesh* /* mesh */, HbrVertex* /* vertex */) { return true; }
-    
+
     // How to turn faces, edges, and vertices into vertices
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrFace* face) = 0;
     virtual HbrVertex* Subdivide(HbrMesh* mesh, HbrHalfedge* edge) = 0;
@@ -106,6 +106,9 @@ public:
     // Returns true if the vertex is extraordinary in the subdivision scheme
     virtual bool VertexIsExtraordinary(HbrMesh* /* mesh */, HbrVertex* /* vertex */) { return false; }
 
+    // Returns true if the face is extraordinary in the subdivision scheme
+    virtual bool FaceIsExtraordinary(HbrMesh* /* mesh */, HbrFace* /* face */) { return false; }
+
     // Crease subdivision rules. When subdividing a edge with a crease
     // strength, we get two child subedges, and we need to determine
     // what weights to assign these subedges. The "normal" rule
@@ -115,8 +118,8 @@ public:
     // vertices, and weighs them; for more information consult
     // the Geri's Game paper.
     enum CreaseSubdivision {
-	k_CreaseNormal,
-	k_CreaseChaikin
+        k_CreaseNormal,
+        k_CreaseChaikin
     };
     CreaseSubdivision GetCreaseSubdivisionMethod() const { return creaseSubdivision; }
     void SetCreaseSubdivisionMethod(CreaseSubdivision method) { creaseSubdivision = method; }
@@ -132,7 +135,7 @@ public:
     // Returns the expected number of children faces after subdivision
     // for a face with the given number of vertices.
     virtual int GetFaceChildrenCount(int nvertices) const = 0;
-    
+
 protected:
     CreaseSubdivision creaseSubdivision;
 
@@ -143,7 +146,7 @@ protected:
     // Helper routine for subclasses: for a given vertex with a crease
     // mask, adds contributions from the two crease edges
     void AddCreaseEdgesWithWeight(HbrMesh* mesh, HbrVertex* vertex, bool next, float weight, T* data);
-    
+
 private:
     // Helper class used by AddSurroundingVerticesWithWeight
     class SmoothSubdivisionVertexOperator : public HbrVertexOperator {
@@ -155,7 +158,7 @@ private:
         {
         }
         virtual void operator() (HbrVertex &vertex) {
-            // Must ensure vertex edits have been applied        
+            // Must ensure vertex edits have been applied
             if (m_meshHasEdits) {
                 vertex.GuaranteeNeighbors();
             }
@@ -186,7 +189,7 @@ private:
                 // Must ensure vertex edits have been applied
                 if (m_meshHasEdits) {
                     a->GuaranteeNeighbors();
-                }			
+                }
                 m_data->AddWithWeight(a->GetData(), m_weight);
                 m_count++;
             }
@@ -218,7 +221,7 @@ private:
         HbrMesh* const m_mesh;
         HbrVertex* const m_vertex;
     };
-    
+
 };
 
 template 
@@ -238,48 +241,48 @@ HbrSubdivision::SubdivideCreaseWeight(HbrHalfedge* edge, HbrVertex* ver
     // In all methods, if the parent edge is infinitely sharp, the
     // child edge is also infinitely sharp
     if (sharpness >= HbrHalfedge::k_InfinitelySharp) {
-	subedge->SetSharpness(HbrHalfedge::k_InfinitelySharp);
+        subedge->SetSharpness(HbrHalfedge::k_InfinitelySharp);
     }
 
     // Chaikin's curve subdivision: use 3/4 of the parent sharpness,
     // plus 1/4 of crease sharpnesses incident to vertex
     else if (creaseSubdivision == HbrSubdivision::k_CreaseChaikin) {
 
-	float childsharp = 0.0f;
+        float childsharp = 0.0f;
 
-	// Add 1/4 of the sharpness of all crease edges incident to
-	// the vertex (other than this crease edge)
-	std::list*> edges;
-	vertex->GuaranteeNeighbors();
-	vertex->GetSurroundingEdges(edges);
+        // Add 1/4 of the sharpness of all crease edges incident to
+        // the vertex (other than this crease edge)
+        std::vector*> edges;
+        vertex->GuaranteeNeighbors();
+        vertex->GetSurroundingEdges(edges);
 
-	int n = 0;
-	for (typename std::list*>::iterator ei = edges.begin(); ei != edges.end(); ++ei) {
-	    if (*ei == edge) continue;
-	    if ((*ei)->GetSharpness() > HbrHalfedge::k_Smooth) {
-		childsharp += (*ei)->GetSharpness();
-		n++;
-	    }
-	}
+        int n = 0;
+        for (typename std::vector*>::iterator ei = edges.begin(); ei != edges.end(); ++ei) {
+            if (*ei == edge) continue;
+            if ((*ei)->GetSharpness() > HbrHalfedge::k_Smooth) {
+                childsharp += (*ei)->GetSharpness();
+                n++;
+            }
+        }
+
+        if (n) {
+            childsharp = childsharp * 0.25f / n;
+        }
+
+        // Add 3/4 of the sharpness of this crease edge
+        childsharp += sharpness * 0.75f;
+        childsharp -= 1.0f;
+        if (childsharp < (float) HbrHalfedge::k_Smooth) {
+            childsharp = (float) HbrHalfedge::k_Smooth;
+        }
+        subedge->SetSharpness(childsharp);
 
-	if (n) {
-	    childsharp = childsharp * 0.25f / n;
-	}
-		    
-	// Add 3/4 of the sharpness of this crease edge
-	childsharp += sharpness * 0.75f;
-	childsharp -= 1.0f;
-	if (childsharp < (float) HbrHalfedge::k_Smooth) {
-	    childsharp = (float) HbrHalfedge::k_Smooth;
-	}
-	subedge->SetSharpness(childsharp);
-	
     } else {
-	sharpness -= 1.0f;
-	if (sharpness < (float) HbrHalfedge::k_Smooth) {
-	    sharpness = (float) HbrHalfedge::k_Smooth;
-	}
-	subedge->SetSharpness(sharpness);
+        sharpness -= 1.0f;
+        if (sharpness < (float) HbrHalfedge::k_Smooth) {
+            sharpness = (float) HbrHalfedge::k_Smooth;
+        }
+        subedge->SetSharpness(sharpness);
     }
 }
 
diff --git a/opensubdiv/hbr/vertex.h b/opensubdiv/hbr/vertex.h
index 7949248c..dd126fc6 100644
--- a/opensubdiv/hbr/vertex.h
+++ b/opensubdiv/hbr/vertex.h
@@ -60,7 +60,7 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 #include "../hbr/fvarData.h"
 #include "../hbr/face.h"
 
@@ -81,7 +81,7 @@ template  class HbrVertex {
 public:
     HbrVertex();
     HbrVertex(int vid, const T &data, int fvarwidth) {
-	Initialize(vid, data, fvarwidth);
+        Initialize(vid, data, fvarwidth);
     }
     void Initialize(int vid, const T &data, int fvarwidth);
     ~HbrVertex();
@@ -96,7 +96,7 @@ public:
     // Checks if removal of the indicated incident edge will result
     // in a singular vertex
     bool EdgeRemovalWillMakeSingular(HbrHalfedge* edge) const;
-    
+
     // Sets up vertex flags after the vertex has been bound to a mesh
     void Finish();
 
@@ -106,7 +106,7 @@ public:
     // Compute the valence of this vertex including only edges which
     // are "coarse" (highest level edges)
     int GetCoarseValence() const;
-    
+
     // Return vertex ID
     int GetID() const { return id; }
 
@@ -127,16 +127,16 @@ public:
 
     // Returns new facevarying data matched to the face
     HbrFVarData& NewFVarData(const HbrFace* face);
-    
+
     // Return any incident face attached to the vertex
     HbrFace* GetFace() const;
 
     // Return the mesh to which this vertex belongs
     HbrMesh* GetMesh() const;
-    
+
     // Return an edge connected to dest
     HbrHalfedge* GetEdge(const HbrVertex* dest) const;
-    
+
     // Given an edge, returns the next edge in counterclockwise order
     // around this vertex. Note well: this is only the next halfedge,
     // which means that all edges returned by this function are
@@ -158,8 +158,8 @@ public:
     // currently they are potentially very inefficient and should be
     // avoided.
     HbrVertex* GetQEONext(const HbrVertex* dest) const;
-    HbrVertex* GetQEONext(const HbrHalfedge* edge) const;    
-    HbrVertex* GetQEOPrev(const HbrHalfedge* edge) const;    
+    HbrVertex* GetQEONext(const HbrHalfedge* edge) const;
+    HbrVertex* GetQEOPrev(const HbrHalfedge* edge) const;
     HbrVertex* GetQEOPrev(const HbrVertex* dest) const;
     HbrVertex* GetQELNext(const HbrVertex* dest) const;
 
@@ -189,12 +189,12 @@ public:
 
     // Sets the sharpness of the vertex
     void SetSharpness(float sharp) { sharpness = sharp; ClearMask(); }
-    
+
     // Returns whether the corner is sharp at the current level of
     // subdivision (next = false) or at the next level of subdivision
     // (next = true).
     bool IsSharp(bool next) const { return (next ? (sharpness > 0.0f) : (sharpness >= 1.0f)); }
-    
+
     // Sets the vertex mask if the vertex is sharp to reflect that
     // it's a corner
     void ClearMask() {
@@ -214,7 +214,7 @@ public:
     // adjacent sharp edges. The fractional mask is a value between 0
     // and 1
     float GetFractionalMask() const;
-    
+
     // Returns whether the vertex is singular (has two separate
     // incident halfedge cycles)
     bool IsSingular() const { return nIncidentEdges > 1; }
@@ -222,27 +222,27 @@ public:
     // Collect the ring of edges around this vertex. Note well:
     // not all edges in this list will have an orientation where
     // the origin of the edge is this vertex!
-    void GetSurroundingEdges(std::list*>& edges) const;
+    void GetSurroundingEdges(std::vector*>& edges) const;
 
     // Apply an edge operator to each edge in the ring of edges
     // around this vertex
     void ApplyOperatorSurroundingEdges(HbrHalfedgeOperator &op) const;
-    
+
     // Collect the ring of vertices around this vertex (the ones
-    // that share an edge with this vertex) 
-    void GetSurroundingVertices(std::list*>& vertices) const;
+    // that share an edge with this vertex)
+    void GetSurroundingVertices(std::vector*>& vertices) const;
 
     // Apply a vertex operator to each vertex in the ring of vertices
     // around this vertex
     void ApplyOperatorSurroundingVertices(HbrVertexOperator &op) const;
-    
+
     // Applys an operator to the ring of faces around this vertex
     void ApplyOperatorSurroundingFaces(HbrFaceOperator &op) const;
-    
+
     // Returns the parent, which can be a edge, face, or vertex
     HbrHalfedge* GetParentEdge() const { return (parentType == k_ParentEdge ? parent.edge : 0); }
     HbrFace* GetParentFace() const { return (parentType == k_ParentFace ? parent.face : 0); }
-    HbrVertex* GetParentVertex() const { return (parentType == k_ParentVertex ? parent.vertex : 0); }    
+    HbrVertex* GetParentVertex() const { return (parentType == k_ParentVertex ? parent.vertex : 0); }
 
     // Set the parent pointer
     void SetParent(HbrHalfedge* edge) { assert(!edge || !parent.vertex); parentType = k_ParentEdge; parent.edge = edge; }
@@ -254,18 +254,18 @@ public:
 
     // Refines the ring of faces around this vertex
     void Refine();
-    
+
     // Make sure the vertex has all faces in the ring around it
     void GuaranteeNeighbors();
 
     // Indicates that the vertex may have a missing face neighbor and
     // may need to guarantee its neighbors in the future
     void UnGuaranteeNeighbors() {
-	neighborsguaranteed = 0;
-	// Its mask is also invalidated
-	validmask = 0;
+        neighborsguaranteed = 0;
+        // Its mask is also invalidated
+        validmask = 0;
     }
-    
+
     // Remove the reference to subdivided vertex
     void RemoveChild() { vchild = 0; }
 
@@ -277,13 +277,13 @@ public:
     bool IsExtraordinary() const { return extraordinary; }
 
     // Tag the vertex as being extraordinary
-    void SetExtraordinary() { extraordinary = 1; }    
+    void SetExtraordinary() { extraordinary = 1; }
 
     // Returns whether the vertex is volatile (incident to a semisharp
     // edge or semisharp corner)
     bool IsVolatile() { if (!validmask) GetMask(false); return volatil; }
 
-    // Simple bookkeeping needed for garbage collection by HbrMesh    
+    // Simple bookkeeping needed for garbage collection by HbrMesh
     bool IsCollected() const { return collected; }
     void SetCollected() { collected = 1; }
     void ClearCollected() { collected = 0; }
@@ -299,18 +299,18 @@ public:
     // Returns true if the vertex is connected. This means that it has
     // an incident edge
     bool IsConnected() const { return nIncidentEdges > 0; }
-    
+
     // Return an incident edge to this vertex, which happens to be the
     // first halfedge of the cycles.
     HbrHalfedge* GetIncidentEdge() const { return nIncidentEdges ? incidentEdges[0] : 0; }
 
     // Sharpness and mask constants
     enum Mask {
-	k_Smooth = 0,
-	k_Dart = 1,
-	k_Crease = 2,
-	k_Corner = 3,
-	k_InfinitelySharp = 10
+        k_Smooth = 0,
+        k_Dart = 1,
+        k_Crease = 2,
+        k_Corner = 3,
+        k_InfinitelySharp = 10
     };
 
     // Increment the usage counter on the vertex
@@ -325,7 +325,7 @@ public:
     // Used by block allocator
     HbrVertex*& GetNext() { return vchild; }
 
-    // Returns the blind pointer to client data    
+    // Returns the blind pointer to client data
     void *GetClientData() const {
         return clientData;
     }
@@ -336,26 +336,26 @@ public:
     }
 
     enum ParentType {
-	k_ParentNone, k_ParentFace, k_ParentEdge, k_ParentVertex
+        k_ParentNone, k_ParentFace, k_ParentEdge, k_ParentVertex
     };
-    
+
 private:
     // Splits a singular vertex into multiple nonsingular vertices
     void splitSingular();
-        
+
     // Data
     T data;
 
     // Pointer to extra facevarying data. Space for this is allocated
     // by NewFVarData
     HbrFVarData *morefvardata;
-    
+
     // Unique ID of this vertex
     int id;
 
     // Size of incident array
     int nIncidentEdges;
-    
+
     // The number of halfedges which have this vertex as the incident
     // edge. When references == 0, the vertex is safe to delete
     int references;
@@ -367,7 +367,7 @@ private:
     // Number of facevarying data allocated to this vertex in
     // morefvardata
     int nfvardata;
-    
+
     // Sharpness
     float sharpness;
 
@@ -388,7 +388,7 @@ private:
     unsigned short neighborsguaranteed:1;
     // Bookkeeping for HbrMesh
     unsigned short collected:1;
-    
+
     // Whether the vertex has an edit. The edit is owned by a face
     // so this is just a tag that indicates we need to search the
     // vertex's neighboring faces for an edit
@@ -400,7 +400,7 @@ private:
 
     // Parent type - can be face, edge, or vertex
     unsigned short parentType:2;
-    
+
     // List of edge cycles. For "singular" vertices, the corresponding
     // set of adjacent halfedges may consist of several cycles, and we
     // need to account for all of them here. In cases where
@@ -414,9 +414,9 @@ private:
     HbrVertex* vchild;
 
     union {
-	HbrFace* face;
-	HbrHalfedge* edge;
-	HbrVertex* vertex;
+        HbrFace* face;
+        HbrHalfedge* edge;
+        HbrVertex* vertex;
     } parent;
 
     // Blind client data pointer
@@ -458,7 +458,7 @@ HbrVertex::Initialize(int vid, const T &vdata, int fvarwidth) {
     char *buffer = ((char*) this + sizeof(*this));
     incidentEdges = (HbrHalfedge**) buffer;
     buffer += sizeof(HbrHalfedge*);
-    
+
     if (fvarwidth) {
         // Upstream allocator ensured the class was padded by the
         // appropriate size. GetFVarData will return a pointer to this
@@ -477,27 +477,27 @@ template 
 void
 HbrVertex::Destroy() {
     if (!destroyed) {
-	// Vertices are only safe for deletion if the number of incident
-	// edges is exactly zero.
-	assert(references == 0);
+        // Vertices are only safe for deletion if the number of incident
+        // edges is exactly zero.
+        assert(references == 0);
 
-	// Delete parent reference to self
-	if (parentType == k_ParentEdge && parent.edge) {
-	    parent.edge->RemoveChild();
-	    parent.edge = 0;
-	} else if (parentType == k_ParentFace && parent.face) {
-	    parent.face->RemoveChild();
-	    parent.face = 0;
-	} else if (parentType == k_ParentVertex && parent.vertex) {
-	    parent.vertex->RemoveChild();
-	    parent.vertex = 0;
-	}
-    
-	// Orphan the child vertex
-	if (vchild) {
-	    vchild->SetParent(static_cast(0));
-	    vchild = 0;
-	}
+        // Delete parent reference to self
+        if (parentType == k_ParentEdge && parent.edge) {
+            parent.edge->RemoveChild();
+            parent.edge = 0;
+        } else if (parentType == k_ParentFace && parent.face) {
+            parent.face->RemoveChild();
+            parent.face = 0;
+        } else if (parentType == k_ParentVertex && parent.vertex) {
+            parent.vertex->RemoveChild();
+            parent.vertex = 0;
+        }
+
+        // Orphan the child vertex
+        if (vchild) {
+            vchild->SetParent(static_cast(0));
+            vchild = 0;
+        }
         // We're skipping the placement destructors here, in the
         // assumption that HbrFVarData's destructor doesn't actually do
         // anything much
@@ -505,7 +505,7 @@ HbrVertex::Destroy() {
             free(morefvardata);
             nfvardata = 0;
         }
-	destroyed = 1;
+        destroyed = 1;
     }
 }
 
@@ -513,7 +513,7 @@ template 
 void
 HbrVertex::AddIncidentEdge(HbrHalfedge* edge) {
     assert(edge->GetOrgVertex() == this);
-    
+
     // First, maintain the property that all of the incident edges
     // will always be a boundary edge if possible. If any of the
     // incident edges are no longer boundaries at this point then they
@@ -521,119 +521,119 @@ HbrVertex::AddIncidentEdge(HbrHalfedge* edge) {
     int i, newEdgeCount = 0;
     bool edgeFound = false;
     for (i = 0; i < nIncidentEdges; ++i) {
-	if (incidentEdges[i] == edge) {
-	    edgeFound = true;
-	}
-	if (incidentEdges[i]->IsBoundary()) {
-	    incidentEdges[newEdgeCount++] = incidentEdges[i];
-	} else {
-	    // Did this edge suddenly stop being a boundary because
-	    // the newly introduced edge (or something close to it)
-	    // closed a cycle? If so, we don't want to lose a pointer
-	    // to this edge cycle!  So check to see if this cycle is
-	    // complete, and if so, keep it.
-	    HbrHalfedge* start = incidentEdges[i];
-	    HbrHalfedge* edge = start;
-	    bool prevmatch = false;
-	    do {
-		edge = GetNextEdge(edge);
-		// Check all previous incident edges, if already
-		// encountered then we have an edge to this cycle and
-		// don't need to proceed further with this check
-		for (int j = 0; j < i; ++j) {
-		    if (incidentEdges[j] == edge) {
-			prevmatch = true;
-			break;
-		    }
-		}
-	    } while (!prevmatch && edge && edge != start);
-	    if (!prevmatch && edge && edge == start) {
-		
-		incidentEdges[newEdgeCount++] = incidentEdges[i];
-	    }
-	}
+        if (incidentEdges[i] == edge) {
+            edgeFound = true;
+        }
+        if (incidentEdges[i]->IsBoundary()) {
+            incidentEdges[newEdgeCount++] = incidentEdges[i];
+        } else {
+            // Did this edge suddenly stop being a boundary because
+            // the newly introduced edge (or something close to it)
+            // closed a cycle? If so, we don't want to lose a pointer
+            // to this edge cycle!  So check to see if this cycle is
+            // complete, and if so, keep it.
+            HbrHalfedge* start = incidentEdges[i];
+            HbrHalfedge* edge = start;
+            bool prevmatch = false;
+            do {
+                edge = GetNextEdge(edge);
+                // Check all previous incident edges, if already
+                // encountered then we have an edge to this cycle and
+                // don't need to proceed further with this check
+                for (int j = 0; j < i; ++j) {
+                    if (incidentEdges[j] == edge) {
+                        prevmatch = true;
+                        break;
+                    }
+                }
+            } while (!prevmatch && edge && edge != start);
+            if (!prevmatch && edge && edge == start) {
+
+                incidentEdges[newEdgeCount++] = incidentEdges[i];
+            }
+        }
     }
-    
+
     // If we are now left with no incident edges, then this edge
     // becomes the sole incident edge (since we always need somewhere
     // to start, even if it's a uninterrupted cycle [ie it doesn't
     // matter whether the edge is a boundary]). Restore incidentEdges
     // array to point to the end of the object.
     if (newEdgeCount == 0) {
-	if (!(edgeFound && nIncidentEdges == 1)) {
-	    if (nIncidentEdges > 1) {
-		delete [] incidentEdges;
-	    }
+        if (!(edgeFound && nIncidentEdges == 1)) {
+            if (nIncidentEdges > 1) {
+                delete [] incidentEdges;
+            }
             incidentEdges = (HbrHalfedge**) ((char*) this + sizeof(*this));
             incidentEdges[0] = edge;
-	    nIncidentEdges = 1;
-	}
+            nIncidentEdges = 1;
+        }
     }
 
     // Otherwise, we already have a set of incident edges - we only
     // add this edge if it's a boundary edge, which would begin a new
     // cycle.
     else if (edge->IsBoundary()) {
-	if (!edgeFound) {
-	    // Must add the new edge. May need to reallocate here.
-	    if (newEdgeCount + 1 != nIncidentEdges) {
+        if (!edgeFound) {
+            // Must add the new edge. May need to reallocate here.
+            if (newEdgeCount + 1 != nIncidentEdges) {
                 HbrHalfedge** newIncidentEdges = 0;
                 if (newEdgeCount + 1 > 1) {
                     newIncidentEdges = new HbrHalfedge*[newEdgeCount + 1];
                 } else {
                     newIncidentEdges = (HbrHalfedge**) ((char*) this + sizeof(*this));
                 }
-		for (i = 0; i < newEdgeCount; ++i) {
-		    newIncidentEdges[i] = incidentEdges[i];
-		}
+                for (i = 0; i < newEdgeCount; ++i) {
+                    newIncidentEdges[i] = incidentEdges[i];
+                }
                 if (nIncidentEdges > 1) {
                     delete[] incidentEdges;
                 }
-		nIncidentEdges = newEdgeCount + 1;
-		incidentEdges = newIncidentEdges;
-	    }
-	    incidentEdges[newEdgeCount] = edge;
-	} else {
-	    // Edge is already in our list, so we don't need to add it
-	    // again. However, we may need to reallocate due to above
-	    // cleaning of nonboundary edges
-	    if (newEdgeCount != nIncidentEdges) {
+                nIncidentEdges = newEdgeCount + 1;
+                incidentEdges = newIncidentEdges;
+            }
+            incidentEdges[newEdgeCount] = edge;
+        } else {
+            // Edge is already in our list, so we don't need to add it
+            // again. However, we may need to reallocate due to above
+            // cleaning of nonboundary edges
+            if (newEdgeCount != nIncidentEdges) {
                 HbrHalfedge** newIncidentEdges = 0;
                 if (newEdgeCount > 1) {
                     newIncidentEdges = new HbrHalfedge*[newEdgeCount];
                 } else {
                     newIncidentEdges = (HbrHalfedge**) ((char*) this + sizeof(*this));
                 }
-		for (i = 0; i < newEdgeCount; ++i) {
-		    newIncidentEdges[i] = incidentEdges[i];
-		}
+                for (i = 0; i < newEdgeCount; ++i) {
+                    newIncidentEdges[i] = incidentEdges[i];
+                }
                 if (nIncidentEdges > 1) {
                     delete[] incidentEdges;
                 }
-		nIncidentEdges = newEdgeCount;
-		incidentEdges = newIncidentEdges;
-	    }
-	}
+                nIncidentEdges = newEdgeCount;
+                incidentEdges = newIncidentEdges;
+            }
+        }
     }
     else {
-	// Again, we may need to reallocate due to above cleaning of
-	// nonboundary edges
-	if (newEdgeCount != nIncidentEdges) {
+        // Again, we may need to reallocate due to above cleaning of
+        // nonboundary edges
+        if (newEdgeCount != nIncidentEdges) {
             HbrHalfedge** newIncidentEdges = 0;
             if (newEdgeCount > 1) {
                 newIncidentEdges = new HbrHalfedge*[newEdgeCount];
             } else {
                 newIncidentEdges = (HbrHalfedge**) ((char*) this + sizeof(*this));
             }
-	    for (i = 0; i < newEdgeCount; ++i) {
-		newIncidentEdges[i] = incidentEdges[i];
-	    }
+            for (i = 0; i < newEdgeCount; ++i) {
+                newIncidentEdges[i] = incidentEdges[i];
+            }
             if (nIncidentEdges > 1) {
                 delete[] incidentEdges;
             }
-	    nIncidentEdges = newEdgeCount;
-	    incidentEdges = newIncidentEdges;
-	}
+            nIncidentEdges = newEdgeCount;
+            incidentEdges = newIncidentEdges;
+        }
     }
 
     // For non-boundary edges, ensure that the incident edge starting
@@ -664,7 +664,7 @@ HbrVertex::AddIncidentEdge(HbrHalfedge* edge) {
             }
         }
     }
-    
+
     references++;
 }
 
@@ -677,34 +677,34 @@ HbrVertex::RemoveIncidentEdge(HbrHalfedge* edge) {
     references--;
     if (references) {
 
-	HbrHalfedge* next;
+        HbrHalfedge* next;
 
-	// We may need to shuffle our halfedge cycles. First we check
-	// whether the edge being erased begins any edge cycles
-	bool edgeFound = false;
-	next = GetNextEdge(edge);
-	
-	for (i = 0; i < nIncidentEdges; ++i) {
-	    if (incidentEdges[i] == edge) {
+        // We may need to shuffle our halfedge cycles. First we check
+        // whether the edge being erased begins any edge cycles
+        bool edgeFound = false;
+        next = GetNextEdge(edge);
 
-		// Edge cycle found. Replace the edge with the next edge
-		// in the cycle if possible.
-		if (next) {
-		    incidentEdges[i] = next;
-		    // We are done.
-		    return;
-		}
+        for (i = 0; i < nIncidentEdges; ++i) {
+            if (incidentEdges[i] == edge) {
 
-		// If no next edge is found it means the entire cycle
-		// has gone away.
-		edgeFound = true;
-		break;
-	    }
-	}
+                // Edge cycle found. Replace the edge with the next edge
+                // in the cycle if possible.
+                if (next) {
+                    incidentEdges[i] = next;
+                    // We are done.
+                    return;
+                }
 
-	// The edge cycle needs to disappear
-	if (edgeFound) {
-	    assert(nIncidentEdges > 1);
+                // If no next edge is found it means the entire cycle
+                // has gone away.
+                edgeFound = true;
+                break;
+            }
+        }
+
+        // The edge cycle needs to disappear
+        if (edgeFound) {
+            assert(nIncidentEdges > 1);
 
             HbrHalfedge** newIncidentEdges = 0;
             if (nIncidentEdges - 1 > 1) {
@@ -712,69 +712,69 @@ HbrVertex::RemoveIncidentEdge(HbrHalfedge* edge) {
             } else {
                 newIncidentEdges = (HbrHalfedge**) ((char*) this + sizeof(*this));
             }
-	    j = 0;
-	    for (i = 0; i < nIncidentEdges; ++i) {
-		if (incidentEdges[i] != edge) {
-		    newIncidentEdges[j++] = incidentEdges[i];
-		}
-	    }
-	    assert(j == nIncidentEdges - 1);
+            j = 0;
+            for (i = 0; i < nIncidentEdges; ++i) {
+                if (incidentEdges[i] != edge) {
+                    newIncidentEdges[j++] = incidentEdges[i];
+                }
+            }
+            assert(j == nIncidentEdges - 1);
             if (nIncidentEdges > 1) {
                 delete[] incidentEdges;
             }
-	    nIncidentEdges--;
-	    incidentEdges = newIncidentEdges;
-	    return;
-	}
-	// Now deal with the case where we remove an edge
-	// which did not begin a boundary edge cycle. If this
-	// happens then the resulting unbroken cycle does
-	// get broken; in that case we replace the incident
-	// edge with the next one after this.
-	else if (nIncidentEdges == 1 && !incidentEdges[0]->IsBoundary()) {
-	    if (next) {
-		incidentEdges[0] = next;
-	    } else {
-		// hm, what does this mean for us? Not sure at the
-		// moment.
-		std::cout << "Could not split cycle!\n";
-		assert(0);
-	    }
-	}
+            nIncidentEdges--;
+            incidentEdges = newIncidentEdges;
+            return;
+        }
+        // Now deal with the case where we remove an edge
+        // which did not begin a boundary edge cycle. If this
+        // happens then the resulting unbroken cycle does
+        // get broken; in that case we replace the incident
+        // edge with the next one after this.
+        else if (nIncidentEdges == 1 && !incidentEdges[0]->IsBoundary()) {
+            if (next) {
+                incidentEdges[0] = next;
+            } else {
+                // hm, what does this mean for us? Not sure at the
+                // moment.
+                std::cout << "Could not split cycle!\n";
+                assert(0);
+            }
+        }
 
-	// (Is this another case or a specialization of the above?)
-	// When an edge in the middle of a boundary cycle goes away we
-	// need to mark a new cycle.
-	//
-	// If there is no next edge, it means that we didn't
-	// actually split the cycle, we just deleted the last edge
-	// in the cycle. As such nothing needs to occur because
-	// the "split" is already present.
-	
-	else if (!edge->IsBoundary() && next) {
+        // (Is this another case or a specialization of the above?)
+        // When an edge in the middle of a boundary cycle goes away we
+        // need to mark a new cycle.
+        //
+        // If there is no next edge, it means that we didn't
+        // actually split the cycle, we just deleted the last edge
+        // in the cycle. As such nothing needs to occur because
+        // the "split" is already present.
+
+        else if (!edge->IsBoundary() && next) {
             HbrHalfedge** newIncidentEdges = 0;
             if (nIncidentEdges + 1 > 1) {
                 newIncidentEdges = new HbrHalfedge*[nIncidentEdges + 1];
             } else {
                 newIncidentEdges = (HbrHalfedge**) ((char*) this + sizeof(*this));
             }
-	    for (i = 0; i < nIncidentEdges; ++i) {
-		newIncidentEdges[i] = incidentEdges[i];
-	    }
-	    newIncidentEdges[nIncidentEdges] = next;
+            for (i = 0; i < nIncidentEdges; ++i) {
+                newIncidentEdges[i] = incidentEdges[i];
+            }
+            newIncidentEdges[nIncidentEdges] = next;
             if (nIncidentEdges > 1) {
                 delete[] incidentEdges;
             }
-	    nIncidentEdges++;
-	    incidentEdges = newIncidentEdges;
-	}
+            nIncidentEdges++;
+            incidentEdges = newIncidentEdges;
+        }
     } else {
-	// No references left, we can just clear all the cycles
-	if (nIncidentEdges > 1) {
-	    delete[] incidentEdges;
+        // No references left, we can just clear all the cycles
+        if (nIncidentEdges > 1) {
+            delete[] incidentEdges;
             incidentEdges = (HbrHalfedge**) ((char*) this + sizeof(*this));
-	}
-	nIncidentEdges = 0;
+        }
+        nIncidentEdges = 0;
     }
 }
 
@@ -783,20 +783,20 @@ bool
 HbrVertex::EdgeRemovalWillMakeSingular(HbrHalfedge* edge) const {
     // Only edge left, or no incident edges at all (how?)
     if (references <= 1 || nIncidentEdges <= 0) {
-	return false;
+        return false;
     }
     // There are at least two existing cycles. We could maybe consider
     // the case where removal of this edge will actually make one of
     // the edge cycles go away, possibly leaving behind just one, but
     // we'll ignore that possibility for now
     else if (nIncidentEdges > 1) {
-	return true;
+        return true;
     }
      // This is the incident edge starting a single cycle. Removal of
      // the edge will replace the start of the cycle with the next
      // edge, and we keep a single cycle.
     else if (nIncidentEdges == 1 && incidentEdges[0] == edge) {
-	return false;
+        return false;
     }
     // Check the single cycle: was it interrupted? (i.e. a
     // boundary). If not interrupted, then deletion of any edge still
@@ -805,11 +805,11 @@ HbrVertex::EdgeRemovalWillMakeSingular(HbrHalfedge* edge) const {
     // cycle. Otherwise we must split the cycle, which would result in
     // a singular vertex
     else if (!incidentEdges[0]->IsBoundary()) {
-	return false;
+        return false;
     } else if (GetNextEdge(edge)) {
-	return true;
+        return true;
     } else {
-	return false;
+        return false;
     }
 }
 
@@ -818,11 +818,11 @@ void
 HbrVertex::Finish() {
     extraordinary = false;
     if (HbrMesh* mesh = GetMesh()) {
-	if (IsSingular()) splitSingular();
-	assert(!IsSingular());
-	if (mesh->GetSubdivision()) {
-	    extraordinary = mesh->GetSubdivision()->VertexIsExtraordinary(mesh, this);
-	}
+        if (IsSingular()) splitSingular();
+        assert(!IsSingular());
+        if (mesh->GetSubdivision()) {
+            extraordinary = mesh->GetSubdivision()->VertexIsExtraordinary(mesh, this);
+        }
     }
 }
 
@@ -834,8 +834,8 @@ HbrVertex::GetValence() const {
     HbrHalfedge* start = incidentEdges[0];
     HbrHalfedge* edge = start;
     if (edge) do {
-	valence++;
-	edge = GetNextEdge(edge);
+        valence++;
+        edge = GetNextEdge(edge);
     } while (edge && edge != start);
     // In boundary cases, we increment the valence count by
     // one more
@@ -851,10 +851,10 @@ HbrVertex::GetCoarseValence() const {
     HbrHalfedge* start = incidentEdges[0];
     HbrHalfedge* edge = start;
     if (edge) do {
-	if (edge->IsCoarse()) {
-	    valence++;
-	}
-	edge = GetNextEdge(edge);
+        if (edge->IsCoarse()) {
+            valence++;
+        }
+        edge = GetNextEdge(edge);
     } while (edge && edge != start);
     // In boundary cases, we increment the valence count by one more
     // (this assumes the last edge is coarse, which it had better be
@@ -933,14 +933,14 @@ HbrHalfedge*
 HbrVertex::GetEdge(const HbrVertex* dest) const {
     // Here, we generally want to go through all halfedge cycles
     for (int i = 0; i < nIncidentEdges; ++i) {
-	HbrHalfedge* cycle = incidentEdges[i];
-	HbrHalfedge* edge = cycle;	
-	if (edge) do {
-	    if (edge->GetDestVertex() == dest) {
-		return edge;
-	    }
-	    edge = GetNextEdge(edge);
-	} while (edge && edge != cycle);
+        HbrHalfedge* cycle = incidentEdges[i];
+        HbrHalfedge* edge = cycle;
+        if (edge) do {
+            if (edge->GetDestVertex() == dest) {
+                return edge;
+            }
+            edge = GetNextEdge(edge);
+        } while (edge && edge != cycle);
     }
     return 0;
 }
@@ -966,12 +966,12 @@ HbrVertex*
 HbrVertex::GetQEONext(const HbrVertex* dest) const {
     HbrHalfedge* edge = GetEdge(dest);
     if (edge) {
-	return edge->GetPrev()->GetOrgVertex();
+        return edge->GetPrev()->GetOrgVertex();
     }
     HbrHalfedge* start = GetIncidentEdge(), *next;
     edge = start;
     while (edge) {
-	next = GetNextEdge(edge);
+        next = GetNextEdge(edge);
         if (edge->GetDestVertex() == dest) {
             if (!next) {
                 return edge->GetPrev()->GetOrgVertex();
@@ -987,9 +987,9 @@ HbrVertex::GetQEONext(const HbrVertex* dest) const {
             } else {
                 return 0;
             }
-	} else {
-	    edge = next;
-	}
+        } else {
+            edge = next;
+        }
     }
     // Shouldn't get here
     return 0;
@@ -1007,9 +1007,9 @@ HbrVertex*
 HbrVertex::GetQEOPrev(const HbrVertex* dest) const {
     HbrHalfedge* edge = GetEdge(dest);
     if (edge) {
-	if (edge->GetOpposite()) {
-	    return edge->GetOpposite()->GetNext()->GetDestVertex();
-	} else {
+        if (edge->GetOpposite()) {
+            return edge->GetOpposite()->GetNext()->GetDestVertex();
+        } else {
             HbrHalfedge* start = GetIncidentEdge(), *next;
             edge = start;
             while (edge) {
@@ -1034,12 +1034,12 @@ HbrVertex::GetQEOPrev(const HbrVertex* dest) const {
                     edge = next;
                 }
             }
-	    return 0;
-	}
+            return 0;
+        }
     }
     edge = dest->GetEdge(this);
     if (edge) {
-	return edge->GetNext()->GetDestVertex();
+        return edge->GetNext()->GetDestVertex();
     }
     return 0;
 }
@@ -1060,11 +1060,11 @@ HbrVertex*
 HbrVertex::GetQELNext(const HbrVertex* dest) const {
     HbrHalfedge* edge = GetEdge(dest);
     if (edge) {
-	return edge->GetNext()->GetDestVertex();
-    }	
+        return edge->GetNext()->GetDestVertex();
+    }
     edge = dest->GetEdge(this);
     if (edge) {
-	return edge->GetPrev()->GetOrgVertex();
+        return edge->GetPrev()->GetOrgVertex();
     }
     return 0;
 }
@@ -1111,25 +1111,25 @@ HbrVertex::IsFVarCorner(int datum) {
     edge = start;
     bool lastedgewassharp = false;
     while (edge) {
-	if (edge->GetFVarSharpness(datum)) {
-	    if (lastedgewassharp) {
-		return true;
-	    } else {
-		lastedgewassharp = true;
-	    }
-	} else {
-	    lastedgewassharp = false;
-	}
-	nextedge = GetNextEdge(edge);
-	if (nextedge == start) {
-	    return start->GetFVarSharpness(datum) && lastedgewassharp;
-	} else if (!nextedge) {
-	    // Special case for the last edge in a cycle.
-	    edge = edge->GetPrev();
-	    return edge->GetFVarSharpness(datum) && lastedgewassharp;	    
-	} else {
-	    edge = nextedge;
-	}
+        if (edge->GetFVarSharpness(datum)) {
+            if (lastedgewassharp) {
+                return true;
+            } else {
+                lastedgewassharp = true;
+            }
+        } else {
+            lastedgewassharp = false;
+        }
+        nextedge = GetNextEdge(edge);
+        if (nextedge == start) {
+            return start->GetFVarSharpness(datum) && lastedgewassharp;
+        } else if (!nextedge) {
+            // Special case for the last edge in a cycle.
+            edge = edge->GetPrev();
+            return edge->GetFVarSharpness(datum) && lastedgewassharp;
+        } else {
+            edge = nextedge;
+        }
     }
     return false;
 }
@@ -1139,15 +1139,15 @@ unsigned char
 HbrVertex::GetMask(bool next) {
 
     if (validmask) {
-	return (unsigned char)(next ? mask1 : mask0);
+        return (unsigned char)(next ? mask1 : mask0);
     }
 
     mask0 = mask1 = 0;
 
     // Mark volatility
     if (sharpness > k_Smooth && sharpness < k_InfinitelySharp)
-	volatil = 1;
-    
+        volatil = 1;
+
     // If the vertex is tagged as sharp immediately promote its mask
     // to corner
     if (IsSharp(false)) {
@@ -1161,46 +1161,46 @@ HbrVertex::GetMask(bool next) {
     HbrHalfedge* start = GetIncidentEdge(), *edge, *nextedge;
     edge = start;
     while (edge) {
-	float esharp = edge->GetSharpness();
+        float esharp = edge->GetSharpness();
 
-	if (edge->IsSharp(false)) {
+        if (edge->IsSharp(false)) {
             if (mask0 < k_Corner) {
                 mask0++;
             }
-	}
-	if (edge->IsSharp(true)) {
+        }
+        if (edge->IsSharp(true)) {
             if (mask1 < k_Corner) {
                 mask1++;
             }
-	}
-	// If any incident edge is semisharp, mark the vertex as volatile
-	if (esharp > HbrHalfedge::k_Smooth && esharp < HbrHalfedge::k_InfinitelySharp) {
-	    volatil = 1;
-	}	
-	nextedge = GetNextEdge(edge);
-	if (nextedge == start) {
-	    break;
-	} else if (!nextedge) {
-	    // Special case for the last edge in a cycle.
-	    edge = edge->GetPrev();
-	    esharp = edge->GetSharpness();	    
-	    if (edge->IsSharp(false)) {
+        }
+        // If any incident edge is semisharp, mark the vertex as volatile
+        if (esharp > HbrHalfedge::k_Smooth && esharp < HbrHalfedge::k_InfinitelySharp) {
+            volatil = 1;
+        }
+        nextedge = GetNextEdge(edge);
+        if (nextedge == start) {
+            break;
+        } else if (!nextedge) {
+            // Special case for the last edge in a cycle.
+            edge = edge->GetPrev();
+            esharp = edge->GetSharpness();
+            if (edge->IsSharp(false)) {
                 if (mask0 < k_Corner) {
                     mask0++;
                 }
-	    }
-	    if (edge->IsSharp(true)) {
+            }
+            if (edge->IsSharp(true)) {
                 if (mask1 < k_Corner) {
                     mask1++;
                 }
-	    }
-	    if (esharp > HbrHalfedge::k_Smooth && esharp < HbrHalfedge::k_InfinitelySharp) {
-		volatil = 1;
-	    }
-	    break;
-	} else {
-	    edge = nextedge;
-	}
+            }
+            if (esharp > HbrHalfedge::k_Smooth && esharp < HbrHalfedge::k_InfinitelySharp) {
+                volatil = 1;
+            }
+            break;
+        } else {
+            edge = nextedge;
+        }
     }
     validmask = 1;
     return (unsigned char)(next ? mask1 : mask0);
@@ -1215,36 +1215,36 @@ HbrVertex::GetFVarMask(int datum) {
     // If the vertex is tagged as sharp immediately promote its mask
     // to corner
     if (IsSharp(false)) {
-	mask += k_Corner;
+        mask += k_Corner;
     }
 
     // Count the number of surrounding facevarying boundary edges
     HbrHalfedge* start = GetIncidentEdge(), *edge, *nextedge;
     edge = start;
     while (edge) {
-	if (edge->GetFVarSharpness(datum)) {
+        if (edge->GetFVarSharpness(datum)) {
             if (mask < k_Corner) {
                 mask++;
             } else {
                 // Can't get any sharper, so give up early
                 break;
             }
-	}
-	nextedge = GetNextEdge(edge);
-	if (nextedge == start) {
-	    break;
-	} else if (!nextedge) {
-	    // Special case for the last edge in a cycle.
-	    edge = edge->GetPrev();
-	    if (edge->GetFVarSharpness(datum)) {
+        }
+        nextedge = GetNextEdge(edge);
+        if (nextedge == start) {
+            break;
+        } else if (!nextedge) {
+            // Special case for the last edge in a cycle.
+            edge = edge->GetPrev();
+            if (edge->GetFVarSharpness(datum)) {
                 if (mask < k_Corner) {
                     mask++;
                 }
-	    }
-	    break;
-	} else {
-	    edge = nextedge;
-	}
+            }
+            break;
+        } else {
+            edge = nextedge;
+        }
     }
     return mask;
 }
@@ -1256,7 +1256,7 @@ HbrVertex::GetFractionalMask() const {
     float n = 0;
 
     if (sharpness > k_Smooth && sharpness < k_Dart) {
-	mask += sharpness; ++n;
+        mask += sharpness; ++n;
     }
 
     // Add up the strengths of surrounding fractional sharp edges
@@ -1264,22 +1264,22 @@ HbrVertex::GetFractionalMask() const {
     edge = start;
     while (edge) {
         float esharp = edge->GetSharpness();
-	if (esharp > HbrHalfedge::k_Smooth && esharp < HbrHalfedge::k_Sharp) {
-	    mask += esharp; ++n;
-	}
-	next = GetNextEdge(edge);
-	if (next == start) {
-	    break;
-	} else if (!next) {
-	    // Special case for the last edge in a cycle.
+        if (esharp > HbrHalfedge::k_Smooth && esharp < HbrHalfedge::k_Sharp) {
+            mask += esharp; ++n;
+        }
+        next = GetNextEdge(edge);
+        if (next == start) {
+            break;
+        } else if (!next) {
+            // Special case for the last edge in a cycle.
             esharp = edge->GetPrev()->GetSharpness();
             if (esharp > HbrHalfedge::k_Smooth && esharp < HbrHalfedge::k_Sharp) {
                 mask += esharp; ++n;
             }
-	    break;
-	} else {
-	    edge = next;
-	}
+            break;
+        } else {
+            edge = next;
+        }
     }
     assert (n > 0.0f && mask < n);
     return (mask / n);
@@ -1287,21 +1287,21 @@ HbrVertex::GetFractionalMask() const {
 
 template 
 void
-HbrVertex::GetSurroundingEdges(std::list*>& edges) const {
+HbrVertex::GetSurroundingEdges(std::vector*>& edges) const {
     HbrHalfedge* start = GetIncidentEdge(), *edge, *next;
     edge = start;
     while (edge) {
-	edges.push_back(edge);
-	next = GetNextEdge(edge);
-	if (next == start) {
-	    break;
-	} else if (!next) {
-	    // Special case for the last edge in a cycle.
-	    edges.push_back(edge->GetPrev());
-	    break;
-	} else {
-	    edge = next;
-	}
+        edges.push_back(edge);
+        next = GetNextEdge(edge);
+        if (next == start) {
+            break;
+        } else if (!next) {
+            // Special case for the last edge in a cycle.
+            edges.push_back(edge->GetPrev());
+            break;
+        } else {
+            edge = next;
+        }
     }
 }
 
@@ -1312,7 +1312,7 @@ HbrVertex::ApplyOperatorSurroundingEdges(HbrHalfedgeOperator &op) const {
     edge = start;
     while (edge) {
         op(*edge);
-	next = GetNextEdge(edge);
+        next = GetNextEdge(edge);
         if (next == start) {
             break;
         } else if (!next) {
@@ -1326,23 +1326,23 @@ HbrVertex::ApplyOperatorSurroundingEdges(HbrHalfedgeOperator &op) const {
 
 template 
 void
-HbrVertex::GetSurroundingVertices(std::list*>& vertices) const {
+HbrVertex::GetSurroundingVertices(std::vector*>& vertices) const {
     HbrHalfedge* start = GetIncidentEdge(), *edge, *next;
     edge = start;
     while (edge) {
-	vertices.push_back(edge->GetDestVertex());
-	next = GetNextEdge(edge);
-	if (next == start) {
-	    break;
-	} else if (!next) {
-	    // Special case for the last edge in a cycle: the last
-	    // vertex on that cycle is not the destination of an
-	    // outgoing halfedge
-	    vertices.push_back(edge->GetPrev()->GetOrgVertex());
-	    break;
-	} else {
-	    edge = next;
-	}
+        vertices.push_back(edge->GetDestVertex());
+        next = GetNextEdge(edge);
+        if (next == start) {
+            break;
+        } else if (!next) {
+            // Special case for the last edge in a cycle: the last
+            // vertex on that cycle is not the destination of an
+            // outgoing halfedge
+            vertices.push_back(edge->GetPrev()->GetOrgVertex());
+            break;
+        } else {
+            edge = next;
+        }
     }
 }
 
@@ -1353,7 +1353,7 @@ HbrVertex::ApplyOperatorSurroundingVertices(HbrVertexOperator &op) const {
     edge = start;
     while (edge) {
         op(*edge->GetDestVertex());
-	next = GetNextEdge(edge);
+        next = GetNextEdge(edge);
         if (next == start) return;
         else if (!next) {
             op(*edge->GetPrev()->GetOrgVertex());
@@ -1371,8 +1371,8 @@ HbrVertex::ApplyOperatorSurroundingFaces(HbrFaceOperator &op) const {
     edge = start;
     while (edge) {
         op(*edge->GetLeftFace());
-	edge = GetNextEdge(edge);
-	if (edge == start) break;
+        edge = GetNextEdge(edge);
+        if (edge == start) break;
     }
 }
 
@@ -1397,31 +1397,31 @@ template 
 void
 HbrVertex::GuaranteeNeighbors() {
     if (!neighborsguaranteed) {
-	HbrMesh* mesh = GetMesh();
-	mesh->GetSubdivision()->GuaranteeNeighbors(mesh, this);
-	neighborsguaranteed = 1;    
+        HbrMesh* mesh = GetMesh();
+        mesh->GetSubdivision()->GuaranteeNeighbors(mesh, this);
+        neighborsguaranteed = 1;
 
-	// At this point we can apply vertex edits because we have all
-	// surrounding faces, and know whether any of them has
-	// necessary edit information (they would have set our
-	// hasvertexedit bit)
-	if (hasvertexedit && !editsapplied) { 
+        // At this point we can apply vertex edits because we have all
+        // surrounding faces, and know whether any of them has
+        // necessary edit information (they would have set our
+        // hasvertexedit bit)
+        if (hasvertexedit && !editsapplied) {
             HbrHalfedge* start = GetIncidentEdge(), *edge;
             edge = start;
             while (edge) {
                 HbrFace* face = edge->GetLeftFace();
-		if (HbrHierarchicalEdit** edits = face->GetHierarchicalEdits()) {
-		    while (HbrHierarchicalEdit* edit = *edits) {
-			if (!edit->IsRelevantToFace(face)) break;
-			edit->ApplyEditToVertex(face, this);
-			edits++;
-		    }
-		}
+                if (HbrHierarchicalEdit** edits = face->GetHierarchicalEdits()) {
+                    while (HbrHierarchicalEdit* edit = *edits) {
+                        if (!edit->IsRelevantToFace(face)) break;
+                        edit->ApplyEditToVertex(face, this);
+                        edits++;
+                    }
+                }
                 edge = GetNextEdge(edge);
                 if (edge == start) break;
             }
-	    editsapplied = 1;
-	}
+            editsapplied = 1;
+        }
     }
 }
 
@@ -1439,43 +1439,43 @@ HbrVertex::splitSingular() {
     HbrHalfedge* e;
 
     // Go through each edge cycle after the first
-    std::list*> edges;
+    std::vector*> edges;
     for (int i = 1; i < nIncidentEdges; ++i) {
 
-	// Create duplicate vertex
-	HbrVertex* w = mesh->NewVertex();
-	w->GetData().AddWithWeight(GetData(), 1.0);
-	w->SetSharpness(GetSharpness());
-	
-	// Walk all edges in this cycle and reattach them to duplicate
-	// vertex
-	HbrHalfedge* start = incidentEdges[i];
-	e = start;
-	edges.clear();
-	do {
-	    edges.push_back(e);
-	    e = GetNextEdge(e);
-	} while (e && e != start);
+        // Create duplicate vertex
+        HbrVertex* w = mesh->NewVertex();
+        w->GetData().AddWithWeight(GetData(), 1.0);
+        w->SetSharpness(GetSharpness());
 
-	for (typename std::list*>::iterator ei = edges.begin(); ei != edges.end(); ++ei) {
-	    e = *ei;
-	    if (e->GetOpposite()) {
-		HbrHalfedge* next = e->GetOpposite()->GetNext();
-		if (next->GetOrgVertex() == this) {
-		    references--;
-		    next->SetOrgVertex(w);
-		    w->AddIncidentEdge(next);
-		}
-	    }
-	    // Check again, because sometimes it's been relinked by
-	    // previous clause already
-	    if (e->GetOrgVertex() == this) {
-		references--;
-		e->SetOrgVertex(w);
-		w->AddIncidentEdge(e);
-	    }
-	}
-	w->Finish();
+        // Walk all edges in this cycle and reattach them to duplicate
+        // vertex
+        HbrHalfedge* start = incidentEdges[i];
+        e = start;
+        edges.clear();
+        do {
+            edges.push_back(e);
+            e = GetNextEdge(e);
+        } while (e && e != start);
+
+        for (typename std::vector*>::iterator ei = edges.begin(); ei != edges.end(); ++ei) {
+            e = *ei;
+            if (e->GetOpposite()) {
+                HbrHalfedge* next = e->GetOpposite()->GetNext();
+                if (next->GetOrgVertex() == this) {
+                    references--;
+                    next->SetOrgVertex(w);
+                    w->AddIncidentEdge(next);
+                }
+            }
+            // Check again, because sometimes it's been relinked by
+            // previous clause already
+            if (e->GetOrgVertex() == this) {
+                references--;
+                e->SetOrgVertex(w);
+                w->AddIncidentEdge(e);
+            }
+        }
+        w->Finish();
     }
 
     e = incidentEdges[0];
diff --git a/opensubdiv/hbr/vertexEdit.h b/opensubdiv/hbr/vertexEdit.h
index b7d15a72..285b9849 100644
--- a/opensubdiv/hbr/vertexEdit.h
+++ b/opensubdiv/hbr/vertexEdit.h
@@ -71,7 +71,7 @@ template 
 std::ostream& operator<<(std::ostream& out, const HbrVertexEdit& path) {
     out << "vertex path = (" << path.faceid << ' ';
     for (int i = 0; i < path.nsubfaces; ++i) {
-	out << static_cast(path.subfaces[i]) << ' ';
+        out << static_cast(path.subfaces[i]) << ' ';
     }
     return out << static_cast(path.vertexid) << "), edit = (" << path.edit[0] << ',' << path.edit[1] << ',' << path.edit[2] << ')';
 }
@@ -82,24 +82,24 @@ class HbrVertexEdit : public HbrHierarchicalEdit {
 public:
 
     HbrVertexEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _vertexid, int _index, int _width, bool _isP, typename HbrHierarchicalEdit::Operation _op, float *_edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), isP(_isP), op(_op) {
-	edit = new float[width];
-	memcpy(edit, _edit, width * sizeof(float));
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), isP(_isP), op(_op) {
+        edit = new float[width];
+        memcpy(edit, _edit, width * sizeof(float));
     }
-    
+
     HbrVertexEdit(int _faceid, int _nsubfaces, int *_subfaces, int _vertexid, int _index, int _width, bool _isP, typename HbrHierarchicalEdit::Operation _op, float *_edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), isP(_isP), op(_op) {
-	edit = new float[width];
-	memcpy(edit, _edit, width * sizeof(float));
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), isP(_isP), op(_op) {
+        edit = new float[width];
+        memcpy(edit, _edit, width * sizeof(float));
     }
-    
+
     virtual ~HbrVertexEdit() {
-	delete[] edit;
+        delete[] edit;
     }
 
     // Return the vertex id (the last element in the path)
     unsigned char GetVertexID() const { return vertexid; }
-    
+
     friend std::ostream& operator<<  (std::ostream& out, const HbrVertexEdit& path);
 
     // Return index of variable this edit applies to
@@ -113,48 +113,48 @@ public:
 
     // Get the type of operation
     typename HbrHierarchicalEdit::Operation GetOperation() const { return op; }
-    
+
     virtual void ApplyEditToFace(HbrFace* face) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
-	    // Tags the vertex as being edited; it'll figure out what to
-	    // when GuaranteeNeighbor is called
-	    face->GetVertex(vertexid)->SetVertexEdit();
-	}
-	// In any event, mark the face as having a vertex edit (which
-	// may only be applied on subfaces)
-	face->MarkVertexEdits();
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
+            // Tags the vertex as being edited; it'll figure out what to
+            // when GuaranteeNeighbor is called
+            face->GetVertex(vertexid)->SetVertexEdit();
+        }
+        // In any event, mark the face as having a vertex edit (which
+        // may only be applied on subfaces)
+        face->MarkVertexEdits();
     }
 
     virtual void ApplyEditToVertex(HbrFace* face, HbrVertex* vertex) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth() && 
-		face->GetVertex(vertexid) == vertex) {
-	    vertex->GetData().ApplyVertexEdit(*const_cast*>(this));
-	}
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth() &&
+                face->GetVertex(vertexid) == vertex) {
+            vertex->GetData().ApplyVertexEdit(*const_cast*>(this));
+        }
     }
 
 #ifdef PRMAN
-    virtual void ApplyToBound(struct bbox& bbox, RtMatrix *mx) {
-	if (isP) {
-	    struct xyz p = *(struct xyz*)edit;
-	    if (mx)
-		MxTransformByMatrix(&p, &p, *mx, 1);
-	    if (op == HbrHierarchicalEdit::Set) {
-		bbox.min.x = std::min(bbox.min.x, p.x);
-		bbox.min.y = std::min(bbox.min.y, p.y);
-		bbox.min.z = std::min(bbox.min.z, p.z);
-		bbox.max.x = std::max(bbox.max.x, p.x);
-		bbox.max.y = std::max(bbox.max.y, p.y);
-		bbox.max.z = std::max(bbox.max.z, p.z);
-	    } else if (op == HbrHierarchicalEdit::Add || 
-		       op == HbrHierarchicalEdit::Subtract) {
-		bbox.min.x -= fabsf(p.x);
-		bbox.min.y -= fabsf(p.y);
-		bbox.min.z -= fabsf(p.z);
-		bbox.max.x += fabsf(p.x);
-		bbox.max.y += fabsf(p.y);
-		bbox.max.z += fabsf(p.z);
-	    }
-	}
+    virtual void ApplyToBound(struct bbox& bbox, RtMatrix *mx) const {
+        if (isP) {
+            struct xyz p = *(struct xyz*)edit;
+            if (mx)
+                MxTransformByMatrix(&p, &p, *mx, 1);
+            if (op == HbrHierarchicalEdit::Set) {
+                bbox.min.x = std::min(bbox.min.x, p.x);
+                bbox.min.y = std::min(bbox.min.y, p.y);
+                bbox.min.z = std::min(bbox.min.z, p.z);
+                bbox.max.x = std::max(bbox.max.x, p.x);
+                bbox.max.y = std::max(bbox.max.y, p.y);
+                bbox.max.z = std::max(bbox.max.z, p.z);
+            } else if (op == HbrHierarchicalEdit::Add ||
+                       op == HbrHierarchicalEdit::Subtract) {
+                bbox.min.x -= fabsf(p.x);
+                bbox.min.y -= fabsf(p.y);
+                bbox.min.z -= fabsf(p.z);
+                bbox.max.x += fabsf(p.x);
+                bbox.max.y += fabsf(p.y);
+                bbox.max.z += fabsf(p.z);
+            }
+        }
     }
 #endif
 
@@ -173,24 +173,24 @@ class HbrMovingVertexEdit : public HbrHierarchicalEdit {
 public:
 
     HbrMovingVertexEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _vertexid, int _index, int _width, bool _isP, typename HbrHierarchicalEdit::Operation _op, float *_edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), isP(_isP), op(_op) {
-	edit = new float[width * 2];
-	memcpy(edit, _edit, 2 * width * sizeof(float));
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), isP(_isP), op(_op) {
+        edit = new float[width * 2];
+        memcpy(edit, _edit, 2 * width * sizeof(float));
     }
-    
+
     HbrMovingVertexEdit(int _faceid, int _nsubfaces, int *_subfaces, int _vertexid, int _index, int _width, bool _isP, typename HbrHierarchicalEdit::Operation _op, float *_edit)
-	: HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), isP(_isP), op(_op) {
-	edit = new float[width * 2];
-	memcpy(edit, _edit, 2 * width * sizeof(float));
+        : HbrHierarchicalEdit(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), index(_index), width(_width), isP(_isP), op(_op) {
+        edit = new float[width * 2];
+        memcpy(edit, _edit, 2 * width * sizeof(float));
     }
-    
+
     virtual ~HbrMovingVertexEdit() {
-	delete[] edit;
+        delete[] edit;
     }
 
     // Return the vertex id (the last element in the path)
     unsigned char GetVertexID() const { return vertexid; }
-    
+
     friend std::ostream& operator<<  (std::ostream& out, const HbrVertexEdit& path);
 
     // Return index of variable this edit applies to
@@ -198,60 +198,60 @@ public:
 
     // Return width of the variable
     int GetWidth() const { return width; }
-    
+
     // Get the numerical value of the edit
     const float* GetEdit() const { return edit; }
 
     // Get the type of operation
     typename HbrHierarchicalEdit::Operation GetOperation() const { return op; }
-    
+
     virtual void ApplyEditToFace(HbrFace* face) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
-	    // Tags the vertex as being edited; it'll figure out what to
-	    // when GuaranteeNeighbor is called
-	    face->GetVertex(vertexid)->SetVertexEdit();
-	}
-	// In any event, mark the face as having a vertex edit (which
-	// may only be applied on subfaces)
-	face->MarkVertexEdits();
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth()) {
+            // Tags the vertex as being edited; it'll figure out what to
+            // when GuaranteeNeighbor is called
+            face->GetVertex(vertexid)->SetVertexEdit();
+        }
+        // In any event, mark the face as having a vertex edit (which
+        // may only be applied on subfaces)
+        face->MarkVertexEdits();
     }
 
     virtual void ApplyEditToVertex(HbrFace* face, HbrVertex* vertex) {
-	if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth() && 
-		face->GetVertex(vertexid) == vertex) {
-	    vertex->GetData().ApplyMovingVertexEdit(*const_cast*>(this));
-	}
+        if (HbrHierarchicalEdit::GetNSubfaces() == face->GetDepth() &&
+                face->GetVertex(vertexid) == vertex) {
+            vertex->GetData().ApplyMovingVertexEdit(*const_cast*>(this));
+        }
     }
 
 #ifdef PRMAN
-    virtual void ApplyToBound(struct bbox& bbox, RtMatrix *mx) {
-	if (isP) {
-	    struct xyz p1 = *(struct xyz*)edit;
-	    struct xyz p2 = *(struct xyz*)&edit[3];
-	    if (mx) {
-		MxTransformByMatrix(&p1, &p1, *mx, 1);
-		MxTransformByMatrix(&p2, &p2, *mx, 1);
-	    }
-	    if (op == HbrVertexEdit::Set) {
-		bbox.min.x = std::min(std::min(bbox.min.x, p1.x), p2.x);
-		bbox.min.y = std::min(std::min(bbox.min.y, p1.y), p2.y);
-		bbox.min.z = std::min(std::min(bbox.min.z, p1.z), p2.z);
-		bbox.max.x = std::max(std::max(bbox.max.x, p1.x), p2.x);
-		bbox.max.y = std::max(std::max(bbox.max.y, p1.y), p2.y);
-		bbox.max.z = std::max(std::max(bbox.max.z, p1.z), p2.z);
-	    } else if (op == HbrVertexEdit::Add ||
-		       op == HbrVertexEdit::Subtract) {
-		float maxx = std::max(fabsf(p1.x), fabsf(p2.x));
-		float maxy = std::max(fabsf(p1.y), fabsf(p2.y));
-		float maxz = std::max(fabsf(p1.z), fabsf(p2.z));
-		bbox.min.x -= maxx;
-		bbox.min.y -= maxy;
-		bbox.min.z -= maxz;
-		bbox.max.x += maxx;
-		bbox.max.y += maxy;
-		bbox.max.z += maxz;
-	    }
-	}
+    virtual void ApplyToBound(struct bbox& bbox, RtMatrix *mx) const {
+        if (isP) {
+            struct xyz p1 = *(struct xyz*)edit;
+            struct xyz p2 = *(struct xyz*)&edit[3];
+            if (mx) {
+                MxTransformByMatrix(&p1, &p1, *mx, 1);
+                MxTransformByMatrix(&p2, &p2, *mx, 1);
+            }
+            if (op == HbrVertexEdit::Set) {
+                bbox.min.x = std::min(std::min(bbox.min.x, p1.x), p2.x);
+                bbox.min.y = std::min(std::min(bbox.min.y, p1.y), p2.y);
+                bbox.min.z = std::min(std::min(bbox.min.z, p1.z), p2.z);
+                bbox.max.x = std::max(std::max(bbox.max.x, p1.x), p2.x);
+                bbox.max.y = std::max(std::max(bbox.max.y, p1.y), p2.y);
+                bbox.max.z = std::max(std::max(bbox.max.z, p1.z), p2.z);
+            } else if (op == HbrVertexEdit::Add ||
+                       op == HbrVertexEdit::Subtract) {
+                float maxx = std::max(fabsf(p1.x), fabsf(p2.x));
+                float maxy = std::max(fabsf(p1.y), fabsf(p2.y));
+                float maxz = std::max(fabsf(p1.z), fabsf(p2.z));
+                bbox.min.x -= maxx;
+                bbox.min.y -= maxy;
+                bbox.min.z -= maxz;
+                bbox.max.x += maxx;
+                bbox.max.y += maxy;
+                bbox.max.z += maxz;
+            }
+        }
     }
 #endif
 
diff --git a/opensubdiv/osd/CMakeLists.txt b/opensubdiv/osd/CMakeLists.txt
index 88f4f5ce..2ad7a0ef 100644
--- a/opensubdiv/osd/CMakeLists.txt
+++ b/opensubdiv/osd/CMakeLists.txt
@@ -71,7 +71,7 @@ set(SOURCE_FILES
     cpuKernel.cpp
     kernelDispatcher.cpp
     mesh.cpp
-    vertexBuffer.cpp    
+    vertexBuffer.cpp
 )
 
 set(KERNEL_FILES
@@ -96,7 +96,9 @@ set(PUBLIC_HEADER_FILES
 #-------------------------------------------------------------------------------
 # platform dependent tweaks
 if(APPLE)
-
+    set(PLATFORM_COMPILE_FLAGS
+        -fPIC
+    )
 elseif(UNIX)
     set(PLATFORM_COMPILE_FLAGS
         -fPIC
@@ -110,26 +112,31 @@ add_definitions(
 )
 
 #-------------------------------------------------------------------------------
-if( OPENMP_FOUND )
-    add_definitions(
-        -DOPENSUBDIV_HAS_OPENMP
-        ${OpenMP_CXX_FLAGS}
+if( PTEX_FOUND )
+    list(APPEND SOURCE_FILES
+        pTexture.cpp
     )
+    list(APPEND PUBLIC_HEADER_FILES
+        pTexture.h
+    )
+    include_directories( ${PTEX_INCLUDE_DIR} )
+    list(APPEND PLATFORM_LIBRARIES
+        ${PTEX_LIBRARY}
+    )
+endif()
+
+#-------------------------------------------------------------------------------
+if( OPENMP_FOUND )
     if (CMAKE_COMPILER_IS_GNUCXX)
         list(APPEND PLATFORM_LIBRARIES
             gomp
         )
     endif()
-else()
-    message(STATUS
-        "* OpenMP was not found : support for OMP parallel compute kernels will be diabled "
-        "in Osd. If your compiler supports OpenMP directives, please refer to the "
-        "FindOpenMP.cmake shared module in your cmake installation.")
 endif()
 
 #-------------------------------------------------------------------------------
-# GL code & dependencies 
-# note : (GLSL compute kernels require GL 4.2, which excludes APPLE) 
+# GL code & dependencies
+# note : (GLSL compute kernels require GL 4.2, which excludes APPLE)
 if( OPENGL_FOUND AND GLEW_FOUND AND (NOT APPLE) )
     list(APPEND SOURCE_FILES
         glslDispatcher.cpp
@@ -143,19 +150,16 @@ if( OPENGL_FOUND AND GLEW_FOUND AND (NOT APPLE) )
     list(APPEND PLATFORM_LIBRARIES
         ${OPENGL_LIBRARY}
         ${GLEW_LIBRARY}
-    )	
-    add_definitions(
-        -DOPENSUBDIV_HAS_GLSL
     )
-else()
-    message(STATUS
-        "* OpenGL was not found : support for GLSL parallel compute kernels will be disabled "
-        "in Osd. If you have an OpenGL SDK installed (version 4.2 or above), please refer to "
-        "the FindOpenGL.cmake shared module in your cmake installation.")
+else( OPENGL_FOUND AND APPLE )
+    list(APPEND PLATFORM_LIBRARIES
+        ${OPENGL_LIBRARY}
+        ${ILMBASE_LIBRARIES}
+    )
 endif()
 
 #-------------------------------------------------------------------------------
-# OpenCL code & dependencies 
+# OpenCL code & dependencies
 if ( OPENCL_FOUND )
     list(APPEND SOURCE_FILES
         clDispatcher.cpp
@@ -168,19 +172,11 @@ if ( OPENCL_FOUND )
     )
     list(APPEND PLATFORM_LIBRARIES
         ${OPENCL_LIBRARIES}
-    )	
-    add_definitions(
-        -DOPENSUBDIV_HAS_OPENCL
     )
-else()
-    message(STATUS
-        "* OpenCL was not found : support for OpenCL parallel compute kernels will be disabled "
-        "in Osd. If you have the OpenCL SDK installed, please refer to the FindOpenCL.cmake "
-        "in ${PROJECT_SOURCE_DIR}/cmake.")
 endif()
 
 #-------------------------------------------------------------------------------
-# CUDA code & dependencies 
+# CUDA code & dependencies
 if( CUDA_FOUND )
     list(APPEND SOURCE_FILES
         cudaDispatcher.cpp
@@ -191,17 +187,9 @@ if( CUDA_FOUND )
     list(APPEND KERNEL_FILES
         cudaKernel.cu
     )
-    add_definitions(
-        -DOPENSUBDIV_HAS_CUDA
-    )
     if (UNIX)
         list( APPEND CUDA_NVCC_FLAGS -Xcompiler -fPIC )
     endif()
-else()
-    message(STATUS
-        "* CUDA was not found : support for CUDA parallel compute kernels will be disabled "
-        "in Osd. If you have the CUDA SDK installed, please refer to the FindCUDA.cmake "
-        "shared module in your cmake installation.")
 endif()
 
 
@@ -220,15 +208,15 @@ foreach(kernel_file ${KERNEL_FILES})
 
         string(REGEX REPLACE "(.*)[.].*" "\\1.inc" inc_file ${kernel_file})
         list(APPEND INC_FILES ${inc_file})
-        
+
         add_custom_command(
             OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${inc_file}
             COMMAND stringify ${CMAKE_CURRENT_SOURCE_DIR}/${kernel_file}
                 ${CMAKE_CURRENT_SOURCE_DIR}/${inc_file}
             DEPENDS stringify ${CMAKE_CURRENT_SOURCE_DIR}/${kernel_file}
         )
-        
-    endif()    
+
+    endif()
 endforeach()
 
 #-------------------------------------------------------------------------------
@@ -236,7 +224,7 @@ source_group("Kernels" FILES ${KERNEL_FILES})
 
 source_group("Inc" FILES ${INC_FILES})
 
-_add_library(osd_static STATIC
+_add_possibly_cuda_library(osd_static STATIC
     ${SOURCE_FILES}
     ${PRIVATE_HEADER_FILES}
     ${PUBLIC_HEADER_FILES}
@@ -250,7 +238,7 @@ target_link_libraries(osd_static
 )
 
 if (NOT WIN32)
-    _add_library(osd_dynamic SHARED
+    _add_possibly_cuda_library(osd_dynamic SHARED
         ${SOURCE_FILES}
         ${PRIVATE_HEADER_FILES}
         ${PUBLIC_HEADER_FILES}
diff --git a/opensubdiv/osd/clDispatcher.cpp b/opensubdiv/osd/clDispatcher.cpp
index 665c0574..7ffd90b3 100644
--- a/opensubdiv/osd/clDispatcher.cpp
+++ b/opensubdiv/osd/clDispatcher.cpp
@@ -61,6 +61,7 @@
 #if defined(_WIN32)
     #include 
 #elif defined(__APPLE__)
+    #include 
     #include 
 #else
     #include 
@@ -85,9 +86,9 @@ static const char *clSource =
 
 std::vector OsdClKernelDispatcher::kernelRegistry;
 
-// XXX: context and queue should be moved to client code
 cl_context OsdClKernelDispatcher::_clContext = NULL;
 cl_command_queue OsdClKernelDispatcher::_clQueue = NULL;
+cl_device_id OsdClKernelDispatcher::_clDevice=NULL;
 
 OsdClVertexBuffer::OsdClVertexBuffer(int numElements, int numVertices,
                                      cl_context clContext, cl_command_queue clQueue) :
@@ -143,7 +144,7 @@ OsdClKernelDispatcher::DeviceTable::Copy(cl_context context, int size, const voi
             clReleaseMemObject(devicePtr);
         devicePtr = clCreateBuffer(context, CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR, size,
                                    const_cast(table), &ciErrNum);
-        
+
         CL_CHECK_ERROR(ciErrNum, "Table copy %p\n", table);
     }
 }
@@ -229,6 +230,52 @@ OsdClKernelDispatcher::Synchronize() {
     clFinish(_clQueue);
 }
 
+void
+OsdClKernelDispatcher::ApplyBilinearFaceVerticesKernel(FarMesh * mesh, int offset,
+                                                    int level, int start, int end, void * data) const {
+
+    ApplyCatmarkFaceVerticesKernel(mesh, offset, level, start, end, data);
+}
+
+void
+OsdClKernelDispatcher::ApplyBilinearEdgeVerticesKernel(FarMesh * mesh, int offset,
+                                                    int level, int start, int end, void * data) const {
+
+    cl_int ciErrNum;
+    size_t globalWorkSize[1] = { end-start };
+    cl_kernel kernel = _clKernel->GetBilinearEdgeKernel();
+
+    clSetKernelArg(kernel, 0, sizeof(cl_mem), GetVertexBuffer());
+    clSetKernelArg(kernel, 1, sizeof(cl_mem), GetVaryingBuffer());
+    clSetKernelArg(kernel, 2, sizeof(cl_mem), &_tables[E_IT].devicePtr);
+    clSetKernelArg(kernel, 3, sizeof(int), &_tableOffsets[E_IT][level-1]);
+    clSetKernelArg(kernel, 4, sizeof(int), &offset);
+    clSetKernelArg(kernel, 5, sizeof(int), &start);
+    clSetKernelArg(kernel, 6, sizeof(int), &end);
+
+    ciErrNum = clEnqueueNDRangeKernel(_clQueue, kernel, 1, NULL, globalWorkSize, NULL, 0, NULL, NULL);
+    CL_CHECK_ERROR(ciErrNum, "bilinear edge kernel %d\n", ciErrNum);
+}
+
+void
+OsdClKernelDispatcher::ApplyBilinearVertexVerticesKernel(FarMesh * mesh, int offset,
+                                                       int level, int start, int end, void * data) const {
+
+    cl_int ciErrNum;
+    size_t globalWorkSize[1] = { end-start };
+    cl_kernel kernel = _clKernel->GetBilinearVertexKernel();
+
+    clSetKernelArg(kernel, 0, sizeof(cl_mem), GetVertexBuffer());
+    clSetKernelArg(kernel, 1, sizeof(cl_mem), GetVaryingBuffer());
+    clSetKernelArg(kernel, 2, sizeof(cl_mem), &_tables[V_ITa].devicePtr);
+    clSetKernelArg(kernel, 3, sizeof(int), &_tableOffsets[V_ITa][level-1]);
+    clSetKernelArg(kernel, 4, sizeof(int), (void*)&offset);
+    clSetKernelArg(kernel, 5, sizeof(int), (void*)&start);
+    clSetKernelArg(kernel, 6, sizeof(int), (void*)&end);
+    ciErrNum = clEnqueueNDRangeKernel(_clQueue, kernel, 1, NULL, globalWorkSize, NULL, 0, NULL, NULL);
+    CL_CHECK_ERROR(ciErrNum, "bilinear vertex kernel 1 %d\n", ciErrNum);
+}
+
 void
 OsdClKernelDispatcher::ApplyCatmarkFaceVerticesKernel(FarMesh * mesh, int offset,
                                                     int level, int start, int end, void * data) const {
@@ -252,7 +299,7 @@ OsdClKernelDispatcher::ApplyCatmarkFaceVerticesKernel(FarMesh * mesh,
 }
 
 void
-OsdClKernelDispatcher::ApplyCatmarkEdgeVerticesKernel(FarMesh * mesh, int offset, 
+OsdClKernelDispatcher::ApplyCatmarkEdgeVerticesKernel(FarMesh * mesh, int offset,
                                                     int level, int start, int end, void * data) const {
 
     cl_int ciErrNum;
@@ -268,7 +315,7 @@ OsdClKernelDispatcher::ApplyCatmarkEdgeVerticesKernel(FarMesh * mesh,
     clSetKernelArg(kernel, 6, sizeof(int), &offset);
     clSetKernelArg(kernel, 7, sizeof(int), &start);
     clSetKernelArg(kernel, 8, sizeof(int), &end);
-            
+
     ciErrNum = clEnqueueNDRangeKernel(_clQueue, kernel, 1, NULL, globalWorkSize, NULL, 0, NULL, NULL);
     CL_CHECK_ERROR(ciErrNum, "edge kernel %d\n", ciErrNum);
 }
@@ -388,7 +435,7 @@ OsdClKernelDispatcher::ApplyLoopVertexVerticesKernelA(FarMesh * mesh,
 }
 
 // XXX: initCL should be removed from libosd
-void 
+void
 OsdClKernelDispatcher::initCL() {
 
     cl_int ciErrNum;
@@ -415,9 +462,8 @@ OsdClKernelDispatcher::initCL() {
         }
     }
     // -------------
-    cl_device_id cdDevice;
-    clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 1, &cdDevice, NULL);
-    
+    clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 1, &_clDevice, NULL);
+
 #if defined(_WIN32)
     cl_context_properties props[] = {
         CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(),
@@ -442,10 +488,10 @@ OsdClKernelDispatcher::initCL() {
 #endif
 
     // XXX context creation should be moved to client code
-    _clContext = clCreateContext(props, 1, &cdDevice, NULL, NULL, &ciErrNum);
+    _clContext = clCreateContext(props, 1, &_clDevice, NULL, NULL, &ciErrNum);
     CL_CHECK_ERROR(ciErrNum, "clCreateContext\n");
 
-    _clQueue = clCreateCommandQueue(_clContext, cdDevice, 0, &ciErrNum);
+    _clQueue = clCreateCommandQueue(_clContext, _clDevice, 0, &ciErrNum);
     CL_CHECK_ERROR(ciErrNum, "clCreateCommandQueue\n");
 }
 
@@ -460,6 +506,8 @@ OsdClKernelDispatcher::uninitCL() {
 // ------------------------------------------------------------------
 
 OsdClKernelDispatcher::ClKernel::ClKernel() :
+    _clBilinearEdge(NULL),
+    _clBilinearVertex(NULL),
     _clCatmarkFace(NULL),
     _clCatmarkEdge(NULL),
     _clCatmarkVertexA(NULL),
@@ -472,6 +520,11 @@ OsdClKernelDispatcher::ClKernel::ClKernel() :
 
 OsdClKernelDispatcher::ClKernel::~ClKernel() {
 
+    if (_clBilinearEdge)
+        clReleaseKernel(_clBilinearEdge);
+    if (_clBilinearVertex)
+        clReleaseKernel(_clBilinearVertex);
+
     if (_clCatmarkFace)
         clReleaseKernel(_clCatmarkFace);
     if (_clCatmarkEdge)
@@ -491,6 +544,15 @@ OsdClKernelDispatcher::ClKernel::~ClKernel() {
     if (_clProgram) clReleaseProgram(_clProgram);
 }
 
+static cl_kernel buildKernel(cl_program prog, const char * name) {
+
+    cl_int ciErr;
+    cl_kernel k = clCreateKernel(prog, name, &ciErr);
+    if (ciErr!=CL_SUCCESS)
+        printf("error building kernel '%s'\n", name);
+    return k;
+}
+
 bool
 OsdClKernelDispatcher::ClKernel::Compile(cl_context clContext, int numVertexElements, int numVaryingElements) {
 
@@ -511,29 +573,23 @@ OsdClKernelDispatcher::ClKernel::Compile(cl_context clContext, int numVertexElem
     ciErrNum = clBuildProgram(_clProgram, 0, NULL, NULL, NULL, NULL);
     if (ciErrNum != CL_SUCCESS) {
         OSD_ERROR("ERROR in clBuildProgram %d\n", ciErrNum);
-        //char cBuildLog[10240];
-        //clGetProgramBuildInfo(_clProgram, cdDevice, CL_PROGRAM_BUILD_LOG,
-        //                      sizeof(cBuildLog), cBuildLog, NULL);
-        //OSD_ERROR(cBuildLog);
+        char cBuildLog[10240];
+        clGetProgramBuildInfo(_clProgram, _clDevice, CL_PROGRAM_BUILD_LOG,
+                              sizeof(cBuildLog), cBuildLog, NULL);
+        OSD_ERROR(cBuildLog);
         return false;
     }
 
     // -------
-
-    _clCatmarkFace = clCreateKernel(_clProgram, "computeFace", &ciErrNum);
-    CL_CHECK_ERROR(ciErrNum, "clCreateKernel face\n");
-    _clCatmarkEdge = clCreateKernel(_clProgram, "computeEdge", &ciErrNum);
-    CL_CHECK_ERROR(ciErrNum, "clCreateKernel edge\n");
-    _clCatmarkVertexA = clCreateKernel(_clProgram, "computeVertexA", &ciErrNum);
-    CL_CHECK_ERROR(ciErrNum, "clCreateKernel vertex a\n");
-    _clCatmarkVertexB = clCreateKernel(_clProgram, "computeVertexB", &ciErrNum);
-    CL_CHECK_ERROR(ciErrNum, "clCreateKernel vertex b\n");
-    _clLoopEdge = clCreateKernel(_clProgram, "computeEdge", &ciErrNum);
-    CL_CHECK_ERROR(ciErrNum, "clCreateKernel edge\n");
-    _clLoopVertexA = clCreateKernel(_clProgram, "computeVertexA", &ciErrNum);
-    CL_CHECK_ERROR(ciErrNum, "clCreateKernel vertex a\n");
-    _clLoopVertexB = clCreateKernel(_clProgram, "computeLoopVertexB", &ciErrNum);
-    CL_CHECK_ERROR(ciErrNum, "clCreateKernel vertex b\n");
+    _clBilinearEdge   = buildKernel(_clProgram, "computeBilinearEdge");
+    _clBilinearVertex = buildKernel(_clProgram, "computeBilinearVertex");
+    _clCatmarkFace    = buildKernel(_clProgram, "computeFace");
+    _clCatmarkEdge    = buildKernel(_clProgram, "computeEdge");
+    _clCatmarkVertexA = buildKernel(_clProgram, "computeVertexA");
+    _clCatmarkVertexB = buildKernel(_clProgram, "computeVertexB");
+    _clLoopEdge       = buildKernel(_clProgram, "computeEdge");
+    _clLoopVertexA    = buildKernel(_clProgram, "computeVertexA");
+    _clLoopVertexB    = buildKernel(_clProgram, "computeLoopVertexB");
 
     return true;
 }
diff --git a/opensubdiv/osd/clDispatcher.h b/opensubdiv/osd/clDispatcher.h
index baf94d41..4189b285 100644
--- a/opensubdiv/osd/clDispatcher.h
+++ b/opensubdiv/osd/clDispatcher.h
@@ -93,24 +93,38 @@ public:
     virtual ~OsdClKernelDispatcher();
 
 
+    virtual void ApplyBilinearFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
+
+    virtual void ApplyBilinearEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
+
+    virtual void ApplyBilinearVertexVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
+
+
     virtual void ApplyCatmarkFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyCatmarkEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyCatmarkVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyCatmarkVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const;
 
 
     virtual void ApplyLoopEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyLoopVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyLoopVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const;
 
+    virtual void ApplyVertexEdit(FarMesh *mesh, int offset, int level, void * clientdata) const {}
+
 
     virtual void CopyTable(int tableIndex, size_t size, const void *ptr);
 
+    virtual void AllocateEditTables(int n) {}
+
+    virtual void UpdateEditTable(int tableIndex, const FarTable &offsets, const FarTable &values,
+                                 int operation, int primVarOffset, int primVarWidth) {}
+
     virtual void OnKernelLaunch() {}
 
     virtual void OnKernelFinish() {}
@@ -118,7 +132,7 @@ public:
     virtual OsdVertexBuffer *InitializeVertexBuffer(int numElements, int count);
 
     virtual void BindVertexBuffer(OsdVertexBuffer *vertex, OsdVertexBuffer *varying);
-    
+
     virtual void UnbindVertexBuffer();
 
     virtual void Synchronize();
@@ -139,13 +153,23 @@ protected:
 
         bool Compile(cl_context clContext, int numVertexElements, int numVaryingElements);
 
-        cl_kernel GetCatmarkFaceKernel() const { return _clCatmarkFace; }
-        cl_kernel GetCatmarkEdgeKernel() const { return _clCatmarkEdge; }
+        cl_kernel GetBilinearEdgeKernel() const   { return _clBilinearEdge; }
+
+        cl_kernel GetBilinearVertexKernel() const { return _clBilinearVertex; }
+
+        cl_kernel GetCatmarkFaceKernel() const    { return _clCatmarkFace; }
+
+        cl_kernel GetCatmarkEdgeKernel() const    { return _clCatmarkEdge; }
+
         cl_kernel GetCatmarkVertexKernelA() const { return _clCatmarkVertexA; }
+
         cl_kernel GetCatmarkVertexKernelB() const { return _clCatmarkVertexB; }
-        cl_kernel GetLoopEdgeKernel() const { return _clLoopEdge; }
-        cl_kernel GetLoopVertexKernelA() const { return _clLoopVertexA; }
-        cl_kernel GetLoopVertexKernelB() const { return _clLoopVertexB; }
+
+        cl_kernel GetLoopEdgeKernel() const       { return _clLoopEdge; }
+
+        cl_kernel GetLoopVertexKernelA() const    { return _clLoopVertexA; }
+
+        cl_kernel GetLoopVertexKernelB() const    { return _clLoopVertexB; }
 
         struct Match {
         Match(int numVertexElements, int numVaryingElements) :
@@ -161,12 +185,22 @@ protected:
 
     protected:
         cl_program _clProgram;
-        cl_kernel _clCatmarkFace, _clCatmarkEdge, _clCatmarkVertexA, _clCatmarkVertexB;
-        cl_kernel _clLoopEdge, _clLoopVertexA, _clLoopVertexB;
-        int _numVertexElements, _numVaryingElements;
+
+        cl_kernel _clBilinearEdge,
+                  _clBilinearVertex,
+                  _clCatmarkFace,
+                  _clCatmarkEdge,
+                  _clCatmarkVertexA,
+                  _clCatmarkVertexB,
+                  _clLoopEdge,
+                  _clLoopVertexA,
+                  _clLoopVertexB;
+
+        int _numVertexElements,
+            _numVaryingElements;
     };
 
-    struct DeviceTable 
+    struct DeviceTable
     {
         DeviceTable() : devicePtr(NULL) {}
         ~DeviceTable();
@@ -200,8 +234,9 @@ protected:
     ClKernel * _clKernel;
 
     // XXX: context and queue should be moved to client code
-    static cl_context _clContext;
+    static cl_context       _clContext;
     static cl_command_queue _clQueue;
+    static cl_device_id     _clDevice;
 
     // static shader registry (XXX tentative..)
     static std::vector kernelRegistry;
diff --git a/opensubdiv/osd/clKernel.cl b/opensubdiv/osd/clKernel.cl
index f1b1ecd2..23db07fe 100644
--- a/opensubdiv/osd/clKernel.cl
+++ b/opensubdiv/osd/clKernel.cl
@@ -92,6 +92,60 @@ __global void addVaryingWithWeight(struct Varying *dst, __global struct Varying
     }
 }
 
+__kernel void computeBilinearEdge(__global struct Vertex *vertex,
+                                   __global struct Varying *varying,
+                                   __global int *E_IT,
+                                   int ofs_E_IT,
+                                   int offset, int start, int end) {
+    E_IT += ofs_E_IT;
+
+    int i = start + get_global_id(0);
+    int eidx0 = E_IT[2*i+0];
+    int eidx1 = E_IT[2*i+1];
+
+    struct Vertex dst;
+    struct Varying dstVarying;
+    clearVertex(&dst);
+    clearVarying(&dstVarying);
+
+    addWithWeight(&dst, &vertex[eidx0], 0.5f);
+    addWithWeight(&dst, &vertex[eidx1], 0.5f);
+
+    vertex[i+offset] = dst;
+
+    if (varying) {
+        addVaryingWithWeight(&dstVarying, &varying[eidx0], 0.5f);
+        addVaryingWithWeight(&dstVarying, &varying[eidx1], 0.5f);
+        varying[i+offset] = dstVarying;
+    }
+}
+
+__kernel void computeBilinearVertex(__global struct Vertex *vertex,
+                                     __global struct Varying *varying,
+                                     __global int *V_ITa,
+                                     int ofs_V_ITa,
+                                     int offset, int start, int end) {
+
+    V_ITa += ofs_V_ITa;
+
+    int i = start + get_global_id(0);
+
+    int p = V_ITa[i];
+
+    struct Vertex dst;
+    clearVertex(&dst);
+    addWithWeight(&dst, &vertex[p], 1.0f);
+
+    vertex[i+offset] = dst;
+
+    if (varying) {
+        struct Varying dstVarying;
+        clearVarying(&dstVarying);
+        addVaryingWithWeight(&dstVarying, &varying[p], 1.0f);
+        varying[i+offset] = dstVarying;
+    }
+}
+
 // ----------------------------------------------------------------------------------------
 
 __kernel void computeFace(__global struct Vertex *vertex,
@@ -107,9 +161,9 @@ __kernel void computeFace(__global struct Vertex *vertex,
     int i = start + get_global_id(0);
     int h = F_ITa[2*i];
     int n = F_ITa[2*i+1];
-    
+
     float weight = 1.0f/n;
-    
+
     struct Vertex dst;
     struct Varying dstVarying;
     clearVertex(&dst);
@@ -136,11 +190,11 @@ __kernel void computeEdge(__global struct Vertex *vertex,
     int i = start + get_global_id(0);
     int eidx0 = E_IT[4*i+0];
     int eidx1 = E_IT[4*i+1];
-    int eidx2 = E_IT[4*i+2]; 
+    int eidx2 = E_IT[4*i+2];
     int eidx3 = E_IT[4*i+3];
-    
+
     float vertWeight = E_W[i*2+0];
-    
+
     // Fully sharp edge : vertWeight = 0.5f;
     struct Vertex dst;
     struct Varying dstVarying;
@@ -149,10 +203,10 @@ __kernel void computeEdge(__global struct Vertex *vertex,
 
     addWithWeight(&dst, &vertex[eidx0], vertWeight);
     addWithWeight(&dst, &vertex[eidx1], vertWeight);
-    
+
     if (eidx2 > -1) {
         float faceWeight = E_W[i*2+1];
-        
+
         addWithWeight(&dst, &vertex[eidx2], faceWeight);
         addWithWeight(&dst, &vertex[eidx3], faceWeight);
     }
@@ -180,15 +234,15 @@ __kernel void computeVertexA(__global struct Vertex *vertex,
     int p     = V_ITa[5*i+2];
     int eidx0 = V_ITa[5*i+3];
     int eidx1 = V_ITa[5*i+4];
-    
+
     float weight = (pass==1) ? V_W[i] : 1.0f - V_W[i];
-    
-    // In the case of fractional weight, the weight must be inverted since 
-    // the value is shared with the k_Smooth kernel (statistically the 
+
+    // In the case of fractional weight, the weight must be inverted since
+    // the value is shared with the k_Smooth kernel (statistically the
     // k_Smooth kernel runs much more often than this one)
     if (weight>0.0f && weight<1.0f && n > 0)
         weight=1.0f-weight;
-    
+
     struct Vertex dst;
     if (not pass)
         clearVertex(&dst);
@@ -227,7 +281,7 @@ __kernel void computeVertexB(__global struct Vertex *vertex,
     int h = V_ITa[5*i];
     int n = V_ITa[5*i+1];
     int p = V_ITa[5*i+2];
-    
+
     float weight = V_W[i];
     float wp = 1.0f/(float)(n*n);
     float wv = (n-2.0f) * n * wp;
@@ -236,7 +290,7 @@ __kernel void computeVertexB(__global struct Vertex *vertex,
     clearVertex(&dst);
 
     addWithWeight(&dst, &vertex[p], weight * wv);
-    
+
     for (int j = 0; j < n; ++j) {
         addWithWeight(&dst, &vertex[V_IT[h+j*2]], weight * wp);
         addWithWeight(&dst, &vertex[V_IT[h+j*2+1]], weight * wp);
@@ -267,7 +321,7 @@ __kernel void computeLoopVertexB(__global struct Vertex *vertex,
     int h = V_ITa[5*i];
     int n = V_ITa[5*i+1];
     int p = V_ITa[5*i+2];
-    
+
     float weight = V_W[i];
     float wp = 1.0f/(float)(n);
     float beta = 0.25f * cos((float)(M_PI) * 2.0f * wp) + 0.375f;
@@ -277,7 +331,7 @@ __kernel void computeLoopVertexB(__global struct Vertex *vertex,
     struct Vertex dst;
     clearVertex(&dst);
     addWithWeight(&dst, &vertex[p], weight * (1.0f - (beta * n)));
-    
+
     for (int j = 0; j < n; ++j) {
         addWithWeight(&dst, &vertex[V_IT[h+j]], weight * beta);
     }
diff --git a/opensubdiv/osd/cpuDispatcher.cpp b/opensubdiv/osd/cpuDispatcher.cpp
index 105cae5b..d5c43349 100644
--- a/opensubdiv/osd/cpuDispatcher.cpp
+++ b/opensubdiv/osd/cpuDispatcher.cpp
@@ -63,25 +63,25 @@
 #include 
 #include 
 
-#ifdef OPENSUBDIV_HAS_OPENMP 
+#ifdef OPENSUBDIV_HAS_OPENMP
     #include 
 #endif
 
 namespace OpenSubdiv {
 namespace OPENSUBDIV_VERSION {
 
-OsdCpuKernelDispatcher::SubdivisionTable::~SubdivisionTable() {
+OsdCpuKernelDispatcher::Table::~Table() {
 
-    if (ptr) 
+    if (ptr)
         free(ptr);
 }
 
 void
-OsdCpuKernelDispatcher::SubdivisionTable::Copy( int size, const void *table ) {
+OsdCpuKernelDispatcher::Table::Copy( int size, const void *table ) {
 
     if (size > 0) {
-        if (ptr) 
-	    free(ptr);
+        if (ptr)
+            free(ptr);
         ptr = malloc(size);
         memcpy(ptr, table, size);
     }
@@ -98,13 +98,13 @@ OsdCpuKernelDispatcher::~OsdCpuKernelDispatcher() {
         delete _vdesc;
 }
 
-static OsdCpuKernelDispatcher::OsdKernelDispatcher * 
+static OsdCpuKernelDispatcher::OsdKernelDispatcher *
 Create(int levels) {
     return new OsdCpuKernelDispatcher(levels);
 }
 
-#ifdef OPENSUBDIV_HAS_OPENMP 
-static OsdCpuKernelDispatcher::OsdKernelDispatcher * 
+#ifdef OPENSUBDIV_HAS_OPENMP
+static OsdCpuKernelDispatcher::OsdKernelDispatcher *
 CreateOmp(int levels) {
     return new OsdCpuKernelDispatcher(levels, omp_get_num_procs());
 }
@@ -114,15 +114,15 @@ void
 OsdCpuKernelDispatcher::Register() {
 
     Factory::GetInstance().Register(Create, kCPU);
-#ifdef OPENSUBDIV_HAS_OPENMP 
+#ifdef OPENSUBDIV_HAS_OPENMP
     Factory::GetInstance().Register(CreateOmp, kOPENMP);
 #endif
 
 }
 
-void 
+void
 OsdCpuKernelDispatcher::OnKernelLaunch() {
-#ifdef OPENSUBDIV_HAS_OPENMP 
+#ifdef OPENSUBDIV_HAS_OPENMP
     omp_set_num_threads(_numOmpThreads);
 #endif
 }
@@ -130,7 +130,34 @@ OsdCpuKernelDispatcher::OnKernelLaunch() {
 void
 OsdCpuKernelDispatcher::CopyTable(int tableIndex, size_t size, const void *ptr) {
 
-    _tables[tableIndex].Copy(size, ptr);
+    _tables[tableIndex].Copy((int)size, ptr);
+}
+
+void
+OsdCpuKernelDispatcher::AllocateEditTables(int n) {
+
+    _editTables.resize(n*2);
+    _edits.resize(n);
+}
+
+void
+OsdCpuKernelDispatcher::UpdateEditTable(int tableIndex, const FarTable &offsets, const FarTable &values,
+                                        int operation, int primVarOffset, int primVarWidth) {
+
+    _editTables[tableIndex*2+0].Copy(offsets.GetMemoryUsed(), offsets[0]);
+    _editTables[tableIndex*2+1].Copy(values.GetMemoryUsed(), values[0]);
+
+    _edits[tableIndex].offsetOffsets.resize(_maxLevel);
+    _edits[tableIndex].valueOffsets.resize(_maxLevel);
+    _edits[tableIndex].numEdits.resize(_maxLevel);
+    for (int i = 0; i < _maxLevel; ++i) {
+        _edits[tableIndex].offsetOffsets[i] = (int)(offsets[i] - offsets[0]);
+        _edits[tableIndex].valueOffsets[i] = (int)(values[i] - values[0]);
+        _edits[tableIndex].numEdits[i] = offsets.GetNumElements(i);
+    }
+    _edits[tableIndex].operation = operation;
+    _edits[tableIndex].primVarOffset = primVarOffset;
+    _edits[tableIndex].primVarWidth = primVarWidth;
 }
 
 OsdVertexBuffer *
@@ -142,14 +169,14 @@ OsdCpuKernelDispatcher::InitializeVertexBuffer(int numElements, int numVertices)
 void
 OsdCpuKernelDispatcher::BindVertexBuffer(OsdVertexBuffer *vertex, OsdVertexBuffer *varying) {
 
-    if (vertex) 
+    if (vertex)
         _currentVertexBuffer = dynamic_cast(vertex);
-    else 
+    else
         _currentVertexBuffer = NULL;
 
     if (varying)
         _currentVaryingBuffer = dynamic_cast(varying);
-    else 
+    else
         _currentVaryingBuffer = NULL;
 
     _vdesc = new VertexDescriptor(_currentVertexBuffer ? _currentVertexBuffer->GetNumElements() : 0,
@@ -172,7 +199,7 @@ OsdCpuKernelDispatcher::Synchronize() { }
 
 void
 OsdCpuKernelDispatcher::ApplyBilinearFaceVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const {
-    
+
     computeFace(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                 (int*)_tables[F_IT].ptr + _tableOffsets[F_IT][level-1],
                 (int*)_tables[F_ITa].ptr + _tableOffsets[F_ITa][level-1],
@@ -181,16 +208,16 @@ OsdCpuKernelDispatcher::ApplyBilinearFaceVerticesKernel( FarMesh * me
 
 void
 OsdCpuKernelDispatcher::ApplyBilinearEdgeVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const {
-    
+
     computeBilinearEdge(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                         (int*)_tables[E_IT].ptr + _tableOffsets[E_IT][level-1],
-                        offset, 
+                        offset,
                         start, end);
 }
 
 void
 OsdCpuKernelDispatcher::ApplyBilinearVertexVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const {
-    
+
     computeBilinearVertex(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                           (int*)_tables[V_ITa].ptr + _tableOffsets[V_ITa][level-1],
                           offset, start, end);
@@ -198,7 +225,7 @@ OsdCpuKernelDispatcher::ApplyBilinearVertexVerticesKernel( FarMesh *
 
 void
 OsdCpuKernelDispatcher::ApplyCatmarkFaceVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const {
-    
+
     computeFace(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                 (int*)_tables[F_IT].ptr + _tableOffsets[F_IT][level-1],
                 (int*)_tables[F_ITa].ptr + _tableOffsets[F_ITa][level-1],
@@ -207,17 +234,17 @@ OsdCpuKernelDispatcher::ApplyCatmarkFaceVerticesKernel( FarMesh * mes
 
 void
 OsdCpuKernelDispatcher::ApplyCatmarkEdgeVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const {
-    
+
     computeEdge(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                 (int*)_tables[E_IT].ptr + _tableOffsets[E_IT][level-1],
                 (float*)_tables[E_W].ptr + _tableOffsets[E_W][level-1],
-                offset, 
+                offset,
                 start, end);
 }
 
 void
 OsdCpuKernelDispatcher::ApplyCatmarkVertexVerticesKernelB( FarMesh * mesh, int offset, int level, int start, int end, void * data) const {
-    
+
     computeVertexB(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                    (int*)_tables[V_ITa].ptr + _tableOffsets[V_ITa][level-1],
                    (int*)_tables[V_IT].ptr + _tableOffsets[V_IT][level-1],
@@ -227,7 +254,7 @@ OsdCpuKernelDispatcher::ApplyCatmarkVertexVerticesKernelB( FarMesh *
 
 void
 OsdCpuKernelDispatcher::ApplyCatmarkVertexVerticesKernelA( FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const {
-    
+
     computeVertexA(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                    (int*)_tables[V_ITa].ptr + _tableOffsets[V_ITa][level-1],
                    (float*)_tables[V_W].ptr + _tableOffsets[V_W][level-1],
@@ -236,17 +263,17 @@ OsdCpuKernelDispatcher::ApplyCatmarkVertexVerticesKernelA( FarMesh *
 
 void
 OsdCpuKernelDispatcher::ApplyLoopEdgeVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const {
-    
+
     computeEdge(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                 (int*)_tables[E_IT].ptr + _tableOffsets[E_IT][level-1],
                 (float*)_tables[E_W].ptr + _tableOffsets[E_W][level-1],
-                offset, 
+                offset,
                 start, end);
 }
 
 void
 OsdCpuKernelDispatcher::ApplyLoopVertexVerticesKernelB( FarMesh * mesh, int offset, int level, int start, int end, void * data) const {
-    
+
     computeLoopVertexB(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                        (int*)_tables[V_ITa].ptr + _tableOffsets[V_ITa][level-1],
                        (int*)_tables[V_IT].ptr + _tableOffsets[V_IT][level-1],
@@ -256,13 +283,31 @@ OsdCpuKernelDispatcher::ApplyLoopVertexVerticesKernelB( FarMesh * mes
 
 void
 OsdCpuKernelDispatcher::ApplyLoopVertexVerticesKernelA( FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const {
-    
+
     computeVertexA(_vdesc, GetVertexBuffer(), GetVaryingBuffer(),
                    (int*)_tables[V_ITa].ptr + _tableOffsets[V_ITa][level-1],
                    (float*)_tables[V_W].ptr + _tableOffsets[V_W][level-1],
                    offset, start, end, pass);
 }
 
+void
+OsdCpuKernelDispatcher::ApplyVertexEdit(FarMesh *mesh, int offset, int level, void * clientdata) const {
+
+    for (int i=0; i<(int)_edits.size(); ++i) {
+        const VertexEditArrayInfo &info = _edits[i];
+
+        if (info.operation == FarVertexEditTables::Add) {
+            editVertexAdd(_vdesc, GetVertexBuffer(), info.primVarOffset, info.primVarWidth, info.numEdits[level-1],
+                          (int*)_editTables[i*2+0].ptr + info.offsetOffsets[level-1],
+                          (float*)_editTables[i*2+1].ptr + info.valueOffsets[level-1]);
+        } else if (info.operation == FarVertexEditTables::Set) {
+//XXX:TODO     editVertexSet(_vdesc, GetVertexBuffer(), info.primVarOffset, info.primVarWidth, info.numEdits[level],
+//                          (int*)_editTables[i*2+0].ptr + info.offsetOffsets[level],
+//                          (float*)_editTables[i*2+1].ptr + info.valueOffsets[level]);
+        }
+    }
+}
+
 } // end namespace OPENSUBDIV_VERSION
 
 } // end namespace OpenSubdiv
diff --git a/opensubdiv/osd/cpuDispatcher.h b/opensubdiv/osd/cpuDispatcher.h
index 2e5dab07..49af2b4e 100644
--- a/opensubdiv/osd/cpuDispatcher.h
+++ b/opensubdiv/osd/cpuDispatcher.h
@@ -69,35 +69,41 @@ class OsdCpuKernelDispatcher : public OsdKernelDispatcher
 {
 public:
     OsdCpuKernelDispatcher(int levels, int numOmpThreads=1);
-    
+
     virtual ~OsdCpuKernelDispatcher();
 
 
     virtual void ApplyBilinearFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyBilinearEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyBilinearVertexVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
 
 
     virtual void ApplyCatmarkFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyCatmarkEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyCatmarkVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyCatmarkVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const;
 
 
     virtual void ApplyLoopEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyLoopVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * data) const;
-    
+
     virtual void ApplyLoopVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const;
 
+    virtual void ApplyVertexEdit(FarMesh *mesh, int offset, int level, void * clientdata) const;
 
     virtual void CopyTable(int tableIndex, size_t size, const void *ptr);
 
+    virtual void AllocateEditTables(int n);
+
+    virtual void UpdateEditTable(int tableIndex, const FarTable &offsets, const FarTable &values,
+                                 int operation, int primVarOffset, int primVarWidth);
+
     virtual void OnKernelLaunch();
 
     virtual void OnKernelFinish() {}
@@ -113,15 +119,16 @@ public:
     static void Register();
 
 protected:
-    
-    struct SubdivisionTable {
-        SubdivisionTable() : ptr(NULL) { }
 
-       ~SubdivisionTable();
+    // XXX: until far refactoring finishes, use this.
+    struct Table {
+        Table() : ptr(NULL) { }
+
+       ~Table();
 
         void Copy(int size, const void *ptr);
-        
-	void *ptr;
+
+        void *ptr;
     };
 
     float *GetVertexBuffer() const { return _currentVertexBuffer ? _currentVertexBuffer->GetCpuBuffer() : NULL; }
@@ -134,7 +141,8 @@ protected:
     VertexDescriptor *_vdesc;
 
     int _numOmpThreads;
-    std::vector _tables;
+    std::vector
_tables; + std::vector
_editTables; }; } // end namespace OPENSUBDIV_VERSION diff --git a/opensubdiv/osd/cpuKernel.cpp b/opensubdiv/osd/cpuKernel.cpp index 0e9e674e..56a80401 100644 --- a/opensubdiv/osd/cpuKernel.cpp +++ b/opensubdiv/osd/cpuKernel.cpp @@ -59,7 +59,7 @@ #include "../version.h" #include "../osd/cpuKernel.h" -#ifdef OPENSUBDIV_HAS_OPENMP +#ifdef OPENSUBDIV_HAS_OPENMP #include #endif @@ -67,7 +67,7 @@ namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { void computeFace( const VertexDescriptor *vdesc, float * vertex, float * varying, const int *F_IT, const int *F_ITa, int offset, int start, int end) { - + #ifdef _OPENMP #pragma omp parallel for #endif @@ -97,20 +97,20 @@ void computeEdge( const VertexDescriptor *vdesc, float *vertex, float *varying, for (int i = start; i < end; i++) { int eidx0 = E_IT[4*i+0]; int eidx1 = E_IT[4*i+1]; - int eidx2 = E_IT[4*i+2]; + int eidx2 = E_IT[4*i+2]; int eidx3 = E_IT[4*i+3]; - + float vertWeight = E_W[i*2+0]; - + int dstIndex = offset + i; vdesc->Clear(vertex, varying, dstIndex); - + vdesc->AddWithWeight(vertex, dstIndex, eidx0, vertWeight); vdesc->AddWithWeight(vertex, dstIndex, eidx1, vertWeight); - + if (eidx2 != -1) { float faceWeight = E_W[i*2+1]; - + vdesc->AddWithWeight(vertex, dstIndex, eidx2, faceWeight); vdesc->AddWithWeight(vertex, dstIndex, eidx3, faceWeight); } @@ -130,19 +130,19 @@ void computeVertexA(const VertexDescriptor *vdesc, float *vertex, float *varying int p = V_ITa[5*i+2]; int eidx0 = V_ITa[5*i+3]; int eidx1 = V_ITa[5*i+4]; - + float weight = (pass==1) ? V_W[i] : 1.0f - V_W[i]; - - // In the case of fractional weight, the weight must be inverted since - // the value is shared with the k_Smooth kernel (statistically the + + // In the case of fractional weight, the weight must be inverted since + // the value is shared with the k_Smooth kernel (statistically the // k_Smooth kernel runs much more often than this one) if (weight>0.0f && weight<1.0f && n > 0) weight=1.0f-weight; - + int dstIndex = offset + i; if(not pass) vdesc->Clear(vertex, varying, dstIndex); - + if (eidx0==-1 || (pass==0 && (n==-1)) ) { vdesc->AddWithWeight(vertex, dstIndex, p, weight); } else { @@ -165,14 +165,14 @@ void computeVertexB(const VertexDescriptor *vdesc, float *vertex, float *varying int h = V_ITa[5*i]; int n = V_ITa[5*i+1]; int p = V_ITa[5*i+2]; - + float weight = V_W[i]; float wp = 1.0f/float(n*n); float wv = (n-2.0f) * n * wp; - + int dstIndex = offset + i; vdesc->Clear(vertex, varying, dstIndex); - + vdesc->AddWithWeight(vertex, dstIndex, p, weight * wv); for (int j = 0; j < n; ++j) { @@ -192,16 +192,16 @@ void computeLoopVertexB(const VertexDescriptor *vdesc, float *vertex, float *var int h = V_ITa[5*i]; int n = V_ITa[5*i+1]; int p = V_ITa[5*i+2]; - + float weight = V_W[i]; float wp = 1.0f/float(n); float beta = 0.25f * cosf(float(M_PI) * 2.0f * wp) + 0.375f; beta = beta * beta; beta = (0.625f - beta) * wp; - + int dstIndex = offset + i; vdesc->Clear(vertex, varying, dstIndex); - + vdesc->AddWithWeight(vertex, dstIndex, p, weight * (1.0f - (beta * n))); for (int j = 0; j < n; ++j) @@ -219,13 +219,13 @@ void computeBilinearEdge(const VertexDescriptor *vdesc, float *vertex, float *va for (int i = start; i < end; i++) { int eidx0 = E_IT[2*i+0]; int eidx1 = E_IT[2*i+1]; - + int dstIndex = offset + i; vdesc->Clear(vertex, varying, dstIndex); vdesc->AddWithWeight(vertex, dstIndex, eidx0, 0.5f); vdesc->AddWithWeight(vertex, dstIndex, eidx1, 0.5f); - + vdesc->AddVaryingWithWeight(varying, dstIndex, eidx0, 0.5f); vdesc->AddVaryingWithWeight(varying, dstIndex, eidx1, 0.5f); } @@ -238,14 +238,24 @@ void computeBilinearVertex(const VertexDescriptor *vdesc, float *vertex, float * #endif for (int i = start; i < end; i++) { int p = V_ITa[i]; - + int dstIndex = offset + i; vdesc->Clear(vertex, varying, dstIndex); - + vdesc->AddWithWeight(vertex, dstIndex, p, 1.0f); vdesc->AddVaryingWithWeight(varying, dstIndex, p, 1.0f); } } +void editVertexAdd(const VertexDescriptor *vdesc, float *vertex, int primVarOffset, int primVarWidth, int vertexCount, const int *editIndices, const float *editValues) { + +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (int i = 0; i < vertexCount; i++) { + vdesc->ApplyVertexEditAdd(vertex, primVarOffset, primVarWidth, editIndices[i], &editValues[i*primVarWidth]); + } +} + } // end namespace OPENSUBDIV_VERSION } // end namespace OpenSubdiv diff --git a/opensubdiv/osd/cpuKernel.h b/opensubdiv/osd/cpuKernel.h index c90e9f60..45913bba 100644 --- a/opensubdiv/osd/cpuKernel.h +++ b/opensubdiv/osd/cpuKernel.h @@ -58,13 +58,14 @@ #define OSD_CPU_KERNEL_H #include "../version.h" +#include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { struct VertexDescriptor { - VertexDescriptor(int numVertexElem, int numVaryingElem) + VertexDescriptor(int numVertexElem, int numVaryingElem) : numVertexElements(numVertexElem), numVaryingElements(numVaryingElem) { } void Clear(float *vertex, float *varying, int index) const { @@ -90,7 +91,14 @@ struct VertexDescriptor { for (int i = 0; i < numVaryingElements; ++i) varying[d++] += varying[s++] * weight; } - + + void ApplyVertexEditAdd(float *vertex, int primVarOffset, int primVarWidth, int editIndex, const float *editValues) const { + int d = editIndex * numVertexElements + primVarOffset; + for (int i = 0; i < primVarWidth; ++i) { + vertex[d++] += editValues[i]; + } + } + int numVertexElements; int numVaryingElements; }; @@ -111,6 +119,10 @@ void computeBilinearEdge(const VertexDescriptor *vdesc, float *vertex, float * v void computeBilinearVertex(const VertexDescriptor *vdesc, float *vertex, float * varying, const int *V_ITa, int offset, int start, int end); +void editVertexAdd(const VertexDescriptor *vdesc, float *vertex, int primVarOffset, int primVarWidth, int count, const int *editIndices, const float *editValues); + +void editVertexSet(const VertexDescriptor *vdesc, float *vertex, int primVarOffset, int primVarWidth, int count, const int *editIndices, const float *editValues); + } } // end namespace OPENSUBDIV_VERSION diff --git a/opensubdiv/osd/cudaDispatcher.cpp b/opensubdiv/osd/cudaDispatcher.cpp index ce7e2f8d..798ce8e1 100644 --- a/opensubdiv/osd/cudaDispatcher.cpp +++ b/opensubdiv/osd/cudaDispatcher.cpp @@ -76,6 +76,8 @@ void OsdCudaComputeBilinearEdge(float *vertex, float *varying, int numUserVertex void OsdCudaComputeBilinearVertex(float *vertex, float *varying, int numUserVertexElements, int numVaryingElements, int *V_ITa, int offset, int start, int end); +void OsdCudaEditVertexAdd(float *vertex, int numUserVertexElements, int primVarOffset, int primVarWidth, int numVertices, int *editIndices, float *editValues); + } namespace OpenSubdiv { @@ -145,7 +147,34 @@ OsdCudaKernelDispatcher::~OsdCudaKernelDispatcher() { void OsdCudaKernelDispatcher::CopyTable(int tableIndex, size_t size, const void *ptr) { - _tables[tableIndex].Copy(size, ptr); + _tables[tableIndex].Copy((int)size, ptr); +} + +void +OsdCudaKernelDispatcher::AllocateEditTables(int n) { + + _editTables.resize(n*2); + _edits.resize(n); +} + +void +OsdCudaKernelDispatcher::UpdateEditTable(int tableIndex, const FarTable &offsets, const FarTable &values, + int operation, int primVarOffset, int primVarWidth) { + + _editTables[tableIndex*2+0].Copy(offsets.GetMemoryUsed(), offsets[0]); + _editTables[tableIndex*2+1].Copy(values.GetMemoryUsed(), values[0]); + + _edits[tableIndex].offsetOffsets.resize(_maxLevel); + _edits[tableIndex].valueOffsets.resize(_maxLevel); + _edits[tableIndex].numEdits.resize(_maxLevel); + for (int i = 0; i < _maxLevel; ++i) { + _edits[tableIndex].offsetOffsets[i] = (int)(offsets[i] - offsets[0]); + _edits[tableIndex].valueOffsets[i] = (int)(values[i] - values[0]); + _edits[tableIndex].numEdits[i] = offsets.GetNumElements(i); + } + _edits[tableIndex].operation = operation; + _edits[tableIndex].primVarOffset = primVarOffset; + _edits[tableIndex].primVarWidth = primVarWidth; } OsdVertexBuffer * @@ -174,7 +203,7 @@ OsdCudaKernelDispatcher::BindVertexBuffer(OsdVertexBuffer *vertex, OsdVertexBuff } else { _numVertexElements = 0; } - + if (_currentVaryingBuffer) { _deviceVaryings = (float*)_currentVaryingBuffer->Map(); _numVaryingElements = _currentVaryingBuffer->GetNumElements(); @@ -222,7 +251,7 @@ OsdCudaKernelDispatcher::ApplyBilinearEdgeVerticesKernel(FarMesh * me void OsdCudaKernelDispatcher::ApplyBilinearVertexVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const { - + OsdCudaComputeBilinearVertex(_deviceVertices, _deviceVaryings, _numVertexElements-3, _numVaryingElements, (int*)_tables[V_ITa].devicePtr + _tableOffsets[V_ITa][level-1], @@ -299,5 +328,21 @@ OsdCudaKernelDispatcher::ApplyLoopVertexVerticesKernelA(FarMesh * mes offset, start, end, pass); } +void +OsdCudaKernelDispatcher::ApplyVertexEdit(FarMesh *mesh, int offset, int level, void * clientdata) const { + + for (int i=0; i<(int)_edits.size(); ++i) { + const VertexEditArrayInfo &info = _edits[i]; + + if (info.operation == FarVertexEditTables::Add) { + OsdCudaEditVertexAdd(_deviceVertices, _numVertexElements-3, info.primVarOffset, info.primVarWidth, info.numEdits[level-1], + (int*)_editTables[i*2+0].devicePtr + info.offsetOffsets[level-1], + (float*)_editTables[i*2+1].devicePtr + info.valueOffsets[level-1]); + } else if (info.operation == FarVertexEditTables::Set) { + // XXX: + } + } +} + } // end namespace OPENSUBDIV_VERSION } // end namespace OpenSubdiv diff --git a/opensubdiv/osd/cudaDispatcher.h b/opensubdiv/osd/cudaDispatcher.h index cfaa7cb2..cce912e6 100644 --- a/opensubdiv/osd/cudaDispatcher.h +++ b/opensubdiv/osd/cudaDispatcher.h @@ -92,32 +92,40 @@ public: virtual void ApplyBilinearFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyBilinearEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyBilinearVertexVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; virtual void ApplyCatmarkFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyCatmarkEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyCatmarkVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyCatmarkVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const; virtual void ApplyLoopEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyLoopVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyLoopVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const; + virtual void ApplyVertexEdit(FarMesh *mesh, int offset, int level, void * clientdata) const; + virtual void CopyTable(int tableIndex, size_t size, const void *ptr); + virtual void AllocateEditTables(int n); + + virtual void UpdateEditTable(int tableIndex, const FarTable &offsets, const FarTable &values, + int operation, int primVarOffset, int primVarWidth); + + virtual void OnKernelLaunch() {} virtual void OnKernelFinish() {} @@ -125,7 +133,7 @@ public: virtual OsdVertexBuffer *InitializeVertexBuffer(int numElements, int numVertices); virtual void BindVertexBuffer(OsdVertexBuffer *vertex, OsdVertexBuffer *varying); - + virtual void UnbindVertexBuffer(); virtual void Synchronize(); @@ -138,7 +146,7 @@ public: } protected: - struct DeviceTable + struct DeviceTable { DeviceTable() : devicePtr(NULL) {} ~DeviceTable(); @@ -149,6 +157,7 @@ protected: }; std::vector _tables; + std::vector _editTables; OsdCudaVertexBuffer *_currentVertexBuffer, *_currentVaryingBuffer; @@ -156,7 +165,7 @@ protected: float *_deviceVertices, *_deviceVaryings; - int _numVertexElements, + int _numVertexElements, _numVaryingElements; }; diff --git a/opensubdiv/osd/cudaKernel.cu b/opensubdiv/osd/cudaKernel.cu index 3a063f4f..1b67eff0 100644 --- a/opensubdiv/osd/cudaKernel.cu +++ b/opensubdiv/osd/cudaKernel.cu @@ -141,14 +141,14 @@ computeFace(float *fVertex, float *fVaryings, int *F0_IT, int *F0_ITa, int offse int h = F0_ITa[2*i]; int n = F0_ITa[2*i+1]; float weight = 1.0f/n; - + DeviceVertex dst; dst.clear(); if(NUM_VARYING_ELEMENTS > 0){ DeviceVarying dstVarying; dstVarying.clear(); - + for(int j=0; j dst; dst.clear(); - + dst.addWithWeight(&vertex[eidx0], vertWeight); dst.addWithWeight(&vertex[eidx1], vertWeight); - + if(eidx2 > -1){ float faceWeight = E0_S[i*2+1]; - + dst.addWithWeight(&vertex[eidx2], faceWeight); dst.addWithWeight(&vertex[eidx3], faceWeight); } @@ -228,27 +228,27 @@ computeEdge(float *fVertex, float *fVaryings, int *E0_IT, float *E0_S, int offse } __global__ void -computeEdge(float *fVertex, int numVertexElements, float *fVarying, int numVaryingElements, +computeEdge(float *fVertex, int numVertexElements, float *fVarying, int numVaryingElements, int *E0_IT, float *E0_S, int offset, int start, int end) { for(int i = start + threadIdx.x + blockIdx.x*blockDim.x; i < end; i+= blockDim.x * gridDim.x){ int eidx0 = E0_IT[4*i+0]; int eidx1 = E0_IT[4*i+1]; - int eidx2 = E0_IT[4*i+2]; + int eidx2 = E0_IT[4*i+2]; int eidx3 = E0_IT[4*i+3]; - + float vertWeight = E0_S[i*2+0]; // Fully sharp edge : vertWeight = 0.5f; float *dstVertex = fVertex + (i+offset)*numVertexElements; clear(dstVertex, numVertexElements); - + addWithWeight(dstVertex, fVertex + eidx0*numVertexElements, vertWeight, numVertexElements); addWithWeight(dstVertex, fVertex + eidx1*numVertexElements, vertWeight, numVertexElements); - + if(eidx2 > -1){ float faceWeight = E0_S[i*2+1]; - + addWithWeight(dstVertex, fVertex + eidx2*numVertexElements, faceWeight, numVertexElements); addWithWeight(dstVertex, fVertex + eidx3*numVertexElements, faceWeight, numVertexElements); } @@ -273,22 +273,22 @@ computeVertexA(float *fVertex, float *fVaryings, int *V0_ITa, float *V0_S, int o int p = V0_ITa[5*i+2]; int eidx0 = V0_ITa[5*i+3]; int eidx1 = V0_ITa[5*i+4]; - + float weight = (pass==1) ? V0_S[i] : 1.0f - V0_S[i]; - - // In the case of fractional weight, the weight must be inverted since - // the value is shared with the k_Smooth kernel (statistically the + + // In the case of fractional weight, the weight must be inverted since + // the value is shared with the k_Smooth kernel (statistically the // k_Smooth kernel runs much more often than this one) if (weight>0.0f && weight<1.0f && n > 0) weight=1.0f-weight; - + DeviceVertex dst; if (not pass) { dst.clear(); } else { dst = vertex[i+offset]; } - + if (eidx0==-1 || (pass==0 && (n==-1)) ) { dst.addWithWeight(&vertex[p], weight); } else { @@ -318,11 +318,11 @@ computeVertexA(float *fVertex, int numVertexElements, float *fVaryings, int numV int p = V0_ITa[5*i+2]; int eidx0 = V0_ITa[5*i+3]; int eidx1 = V0_ITa[5*i+4]; - + float weight = (pass==1) ? V0_S[i] : 1.0f - V0_S[i]; - - // In the case of fractional weight, the weight must be inverted since - // the value is shared with the k_Smooth kernel (statistically the + + // In the case of fractional weight, the weight must be inverted since + // the value is shared with the k_Smooth kernel (statistically the // k_Smooth kernel runs much more often than this one) if (weight>0.0f && weight<1.0f && n > 0) weight=1.0f-weight; @@ -331,7 +331,7 @@ computeVertexA(float *fVertex, int numVertexElements, float *fVaryings, int numV if (not pass) { clear(dstVertex, numVertexElements); } - + if (eidx0==-1 || (pass==0 && (n==-1)) ) { addWithWeight(dstVertex, fVertex + p*numVertexElements, weight, numVertexElements); } else { @@ -348,7 +348,7 @@ computeVertexA(float *fVertex, int numVertexElements, float *fVaryings, int numV } } } - + } @@ -364,11 +364,11 @@ computeVertexB(float *fVertex, float *fVaryings, int h = V0_ITa[5*i]; int n = V0_ITa[5*i+1]; int p = V0_ITa[5*i+2]; - + float weight = V0_S[i]; float wp = 1.0f/float(n*n); float wv = (n-2.0f) * n * wp; - + DeviceVertex dst; dst.clear(); dst.addWithWeight(&vertex[p], weight * wv); @@ -381,7 +381,7 @@ computeVertexB(float *fVertex, float *fVaryings, // dst.addWithWeight(&vertex[idx0], weight * wp); // dst.addWithWeight(&vertex[idx1], weight * wp); } - vertex[i+offset] = dst; + vertex[i+offset] = dst; if(NUM_VARYING_ELEMENTS > 0){ DeviceVarying dstVarying; @@ -400,11 +400,11 @@ computeVertexB(float *fVertex, int numVertexElements, float *fVaryings, int numV int h = V0_ITa[5*i]; int n = V0_ITa[5*i+1]; int p = V0_ITa[5*i+2]; - + float weight = V0_S[i]; float wp = 1.0f/float(n*n); float wv = (n-2.0f) * n * wp; - + float *dstVertex = fVertex + (i+offset)*numVertexElements; clear(dstVertex, numVertexElements); addWithWeight(dstVertex, fVertex + p*numVertexElements, weight*wv, numVertexElements); @@ -421,7 +421,7 @@ computeVertexB(float *fVertex, int numVertexElements, float *fVaryings, int numV } } } - + // -------------------------------------------------------------------------------------------- @@ -434,22 +434,22 @@ computeLoopVertexB(float *fVertex, float *fVaryings, int *V0_ITa, int *V0_IT, fl int h = V0_ITa[5*i]; int n = V0_ITa[5*i+1]; int p = V0_ITa[5*i+2]; - + float weight = V0_S[i]; float wp = 1.0f/float(n); float beta = 0.25f * __cosf(float(M_PI) * 2.0f * wp) + 0.375f; beta = beta * beta; beta = (0.625f - beta) * wp; - + DeviceVertex dst; dst.clear(); - + dst.addWithWeight(&vertex[p], weight * (1.0f - (beta * n))); for(int j = 0; j < n; ++j){ dst.addWithWeight(&vertex[V0_IT[h+j]], weight * beta); } - vertex[i+offset] = dst; + vertex[i+offset] = dst; if(NUM_VARYING_ELEMENTS > 0){ DeviceVarying dstVarying; @@ -468,13 +468,13 @@ computeLoopVertexB(float *fVertex, int numVertexElements, float *fVaryings, int int h = V0_ITa[5*i]; int n = V0_ITa[5*i+1]; int p = V0_ITa[5*i+2]; - + float weight = V0_S[i]; float wp = 1.0f/float(n); float beta = 0.25f * __cosf(float(M_PI) * 2.0f * wp) + 0.375f; beta = beta * beta; beta = (0.625f - beta) * wp; - + float *dstVertex = fVertex + (i+offset)*numVertexElements; clear(dstVertex, numVertexElements); addWithWeight(dstVertex, fVertex + p*numVertexElements, weight*(1.0f-(beta*n)), numVertexElements); @@ -490,7 +490,7 @@ computeLoopVertexB(float *fVertex, int numVertexElements, float *fVaryings, int } } } - + // -------------------------------------------------------------------------------------------- template __global__ void @@ -501,13 +501,13 @@ computeBilinearEdge(float *fVertex, float *fVaryings, int *E0_IT, int offset, in for(int i = start + threadIdx.x + blockIdx.x*blockDim.x; i < end; i+= blockDim.x * gridDim.x){ int eidx0 = E0_IT[2*i+0]; int eidx1 = E0_IT[2*i+1]; - + DeviceVertex dst; dst.clear(); - + dst.addWithWeight(&vertex[eidx0], 0.5f); dst.addWithWeight(&vertex[eidx1], 0.5f); - + vertex[offset+i] = dst; if(NUM_VARYING_ELEMENTS > 0){ @@ -521,19 +521,19 @@ computeBilinearEdge(float *fVertex, float *fVaryings, int *E0_IT, int offset, in } __global__ void -computeBilinearEdge(float *fVertex, int numVertexElements, float *fVarying, int numVaryingElements, +computeBilinearEdge(float *fVertex, int numVertexElements, float *fVarying, int numVaryingElements, int *E0_IT, int offset, int start, int end) { for(int i = start + threadIdx.x + blockIdx.x*blockDim.x; i < end; i+= blockDim.x * gridDim.x){ int eidx0 = E0_IT[2*i+0]; int eidx1 = E0_IT[2*i+1]; - + float *dstVertex = fVertex + (i+offset)*numVertexElements; clear(dstVertex, numVertexElements); - + addWithWeight(dstVertex, fVertex + eidx0*numVertexElements, 0.5f, numVertexElements); addWithWeight(dstVertex, fVertex + eidx1*numVertexElements, 0.5f, numVertexElements); - + if(numVaryingElements > 0){ float *dstVarying = fVarying + i*numVaryingElements; clear(dstVarying, numVaryingElements); @@ -551,12 +551,12 @@ computeBilinearVertex(float *fVertex, float *fVaryings, int *V0_ITa, int offset, DeviceVarying *varyings = (DeviceVarying*)fVaryings; for(int i = start + threadIdx.x + blockIdx.x*blockDim.x; i < end; i += blockDim.x * gridDim.x){ int p = V0_ITa[i]; - + DeviceVertex dst; dst.clear(); - + dst.addWithWeight(&vertex[p], 1.0f); - vertex[i+offset] = dst; + vertex[i+offset] = dst; if(NUM_VARYING_ELEMENTS > 0){ DeviceVarying dstVarying; @@ -573,7 +573,7 @@ computeBilinearVertex(float *fVertex, int numVertexElements, float *fVaryings, i { for(int i = start + threadIdx.x + blockIdx.x*blockDim.x; i < end; i += blockDim.x * gridDim.x){ int p = V0_ITa[i]; - + float *dstVertex = fVertex + (i+offset)*numVertexElements; clear(dstVertex, numVertexElements); addWithWeight(dstVertex, fVertex + p*numVertexElements, 1.0f, numVertexElements); @@ -585,14 +585,27 @@ computeBilinearVertex(float *fVertex, int numVertexElements, float *fVaryings, i } } } - +// -------------------------------------------------------------------------------------------- + +__global__ void +editVertexAdd(float *fVertex, int numVertexElements, int primVarOffset, int primVarWidth, + int numVertices, const int *editIndices, const float *editValues) +{ + for(int i = threadIdx.x + blockIdx.x*blockDim.x; i < numVertices; i += blockDim.x * gridDim.x) { + float *dstVertex = fVertex + editIndices[i] * numVertexElements + primVarOffset; + + for(int j = 0; j < primVarWidth; j++) { + *dstVertex++ += editValues[j]; + } + } +} // -------------------------------------------------------------------------------------------- #include "../version.h" -// XXX: this macro usage is tentative. Since cuda kernel can't be dynamically configured, +// XXX: this macro usage is tentative. Since cuda kernel can't be dynamically configured, // still trying to find better way to have optimized kernel.. #define OPT_KERNEL(NUM_USER_VERTEX_ELEMENTS, NUM_VARYING_ELEMENTS, KERNEL, X, Y, ARG) \ @@ -632,7 +645,7 @@ void OsdCudaComputeEdge(float *vertex, float *varying, E_IT, E_W, offset, start, end); } -void OsdCudaComputeVertexA(float *vertex, float *varying, +void OsdCudaComputeVertexA(float *vertex, float *varying, int numUserVertexElements, int numVaryingElements, int *V_ITa, float *V_W, int offset, int start, int end, int pass) { @@ -688,7 +701,7 @@ void OsdCudaComputeBilinearEdge(float *vertex, float *varying, E_IT, offset, start, end); } -void OsdCudaComputeBilinearVertex(float *vertex, float *varying, +void OsdCudaComputeBilinearVertex(float *vertex, float *varying, int numUserVertexElements, int numVaryingElements, int *V_ITa, int offset, int start, int end) { @@ -702,4 +715,11 @@ void OsdCudaComputeBilinearVertex(float *vertex, float *varying, V_ITa, offset, start, end); } +void OsdCudaEditVertexAdd(float *vertex, int numUserVertexElements, + int primVarOffset, int primVarWidth, int numVertices, int *editIndices, float *editValues) +{ + editVertexAdd<<<512, 32>>>(vertex, 3+numUserVertexElements, primVarOffset, primVarWidth, + numVertices, editIndices, editValues); +} + } diff --git a/opensubdiv/osd/glslDispatcher.cpp b/opensubdiv/osd/glslDispatcher.cpp index 93cb0367..411b0e30 100644 --- a/opensubdiv/osd/glslDispatcher.cpp +++ b/opensubdiv/osd/glslDispatcher.cpp @@ -59,7 +59,7 @@ #if not defined(__APPLE__) #include #else - #include + #include #endif #include "../osd/glslDispatcher.h" @@ -97,7 +97,7 @@ static const char *shaderDefines = "" ; std::vector OsdGlslKernelDispatcher::shaderRegistry; - + OsdGlslKernelDispatcher::OsdGlslKernelDispatcher(int levels) : OsdKernelDispatcher(levels) { @@ -200,7 +200,7 @@ OsdGlslKernelDispatcher::BindVertexBuffer(OsdVertexBuffer *vertex, OsdVertexBuff if (vertex) _currentVertexBuffer = dynamic_cast(vertex); - else + else _currentVertexBuffer = NULL; if (varying) @@ -310,7 +310,7 @@ OsdGlslKernelDispatcher::ApplyBilinearFaceVerticesKernel( void OsdGlslKernelDispatcher::ApplyBilinearEdgeVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const { - + _shader->ApplyBilinearEdgeVerticesKernel(_currentVertexBuffer, _currentVaryingBuffer, _tableOffsets[E_IT][level-1], offset, start, end); @@ -319,7 +319,7 @@ OsdGlslKernelDispatcher::ApplyBilinearEdgeVerticesKernel( void OsdGlslKernelDispatcher::ApplyBilinearVertexVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const { - + _shader->ApplyBilinearVertexVerticesKernel(_currentVertexBuffer, _currentVaryingBuffer, _tableOffsets[V_ITa][level-1], offset, start, end); @@ -341,7 +341,7 @@ OsdGlslKernelDispatcher::ApplyCatmarkFaceVerticesKernel( void OsdGlslKernelDispatcher::ApplyCatmarkEdgeVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const { - + _shader->ApplyCatmarkEdgeVerticesKernel(_currentVertexBuffer, _currentVaryingBuffer, _tableOffsets[E_IT][level-1], _tableOffsets[E_W][level-1], @@ -351,7 +351,7 @@ OsdGlslKernelDispatcher::ApplyCatmarkEdgeVerticesKernel( void OsdGlslKernelDispatcher::ApplyCatmarkVertexVerticesKernelB( FarMesh * mesh, int offset, int level, int start, int end, void * data) const { - + _shader->ApplyCatmarkVertexVerticesKernelB(_currentVertexBuffer, _currentVaryingBuffer, _tableOffsets[V_IT][level-1], _tableOffsets[V_ITa][level-1], @@ -362,7 +362,7 @@ OsdGlslKernelDispatcher::ApplyCatmarkVertexVerticesKernelB( void OsdGlslKernelDispatcher::ApplyCatmarkVertexVerticesKernelA( FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const { - + _shader->ApplyCatmarkVertexVerticesKernelA(_currentVertexBuffer, _currentVaryingBuffer, _tableOffsets[V_ITa][level-1], _tableOffsets[V_W][level-1], @@ -374,7 +374,7 @@ OsdGlslKernelDispatcher::ApplyCatmarkVertexVerticesKernelA( void OsdGlslKernelDispatcher::ApplyLoopEdgeVerticesKernel( FarMesh * mesh, int offset, int level, int start, int end, void * data) const { - + _shader->ApplyLoopEdgeVerticesKernel(_currentVertexBuffer, _currentVaryingBuffer, _tableOffsets[E_IT][level-1], _tableOffsets[E_W][level-1], @@ -384,7 +384,7 @@ OsdGlslKernelDispatcher::ApplyLoopEdgeVerticesKernel( void OsdGlslKernelDispatcher::ApplyLoopVertexVerticesKernelB( FarMesh * mesh, int offset, int level, int start, int end, void * data) const { - + _shader->ApplyLoopVertexVerticesKernelB(_currentVertexBuffer, _currentVaryingBuffer, _tableOffsets[V_IT][level-1], _tableOffsets[V_ITa][level-1], @@ -395,7 +395,7 @@ OsdGlslKernelDispatcher::ApplyLoopVertexVerticesKernelB( void OsdGlslKernelDispatcher::ApplyLoopVertexVerticesKernelA( FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const { - + _shader->ApplyLoopVertexVerticesKernelA(_currentVertexBuffer, _currentVaryingBuffer, _tableOffsets[V_ITa][level-1], _tableOffsets[V_W][level-1], @@ -411,7 +411,7 @@ OsdGlslKernelDispatcher::ComputeShader::ComputeShader() : OsdGlslKernelDispatcher::ComputeShader::~ComputeShader() { - if (_program) + if (_program) glDeleteProgram(_program); } @@ -439,10 +439,10 @@ OsdGlslKernelDispatcher::ComputeShader::Compile(int numVertexElements, int numVa glCompileShader(shader); glAttachShader(_program, shader); - const char *outputs[] = { "outPosition", - "outNormal", - "gl_NextBuffer", - "outVaryingData" }; + const char *outputs[] = { "outPosition", + "outNormal", + "gl_NextBuffer", + "outVaryingData" }; int nOutputs = numVaryingElements > 0 ? 4 : 2; @@ -463,7 +463,7 @@ OsdGlslKernelDispatcher::ComputeShader::Compile(int numVertexElements, int numVa glGetProgramInfoLog(_program, 1024, NULL, buffer); OSD_ERROR(buffer); - + glDeleteProgram(_program); _program = 0; // XXX ERROR HANDLE @@ -539,7 +539,7 @@ OsdGlslKernelDispatcher::ComputeShader::transformGpuBufferData(OsdGpuVertexBuffe glBeginTransformFeedback(GL_POINTS); CHECK_GL_ERROR("transformGpuBufferData glBeginTransformFeedback\n"); - + // draw array ----------------------------------------- glDrawArrays(GL_POINTS, 0, count); CHECK_GL_ERROR("transformGpuBufferData DrawArray (%d)\n", count); @@ -578,7 +578,7 @@ void OsdGlslKernelDispatcher::ComputeShader::ApplyBilinearVertexVerticesKernel( OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int V_ITa_ofs, int offset, int start, int end) { - + glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &_subComputeVertex); glUniform1i(_tableOffsetUniforms[V_ITa], V_ITa_ofs); transformGpuBufferData(vertex, varying, offset, start, end); @@ -611,7 +611,7 @@ void OsdGlslKernelDispatcher::ComputeShader::ApplyCatmarkVertexVerticesKernelB( OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int V_IT_ofs, int V_ITa_ofs, int V_W_ofs, int offset, int start, int end) { - + glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &_subComputeCatmarkVertexB); glUniform1i(_tableOffsetUniforms[V_IT], V_IT_ofs); glUniform1i(_tableOffsetUniforms[V_ITa], V_ITa_ofs); @@ -623,7 +623,7 @@ void OsdGlslKernelDispatcher::ComputeShader::ApplyCatmarkVertexVerticesKernelA( OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int V_ITa_ofs, int V_W_ofs, int offset, bool pass, int start, int end) { - + glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &_subComputeVertexA); glUniform1i(_uniformVertexPass, pass ? 1 : 0); glUniform1i(_tableOffsetUniforms[V_ITa], V_ITa_ofs); @@ -635,7 +635,7 @@ void OsdGlslKernelDispatcher::ComputeShader::ApplyLoopEdgeVerticesKernel( OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int E_IT_ofs, int E_W_ofs, int offset, int start, int end) { - + glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &_subComputeEdge); glUniform1i(_tableOffsetUniforms[E_IT], E_IT_ofs); glUniform1i(_tableOffsetUniforms[E_W], E_W_ofs); @@ -646,7 +646,7 @@ void OsdGlslKernelDispatcher::ComputeShader::ApplyLoopVertexVerticesKernelB( OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int V_IT_ofs, int V_ITa_ofs, int V_W_ofs, int offset, int start, int end) { - + glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &_subComputeLoopVertexB); glUniform1i(_tableOffsetUniforms[V_IT], V_IT_ofs); glUniform1i(_tableOffsetUniforms[V_ITa], V_ITa_ofs); @@ -658,7 +658,7 @@ void OsdGlslKernelDispatcher::ComputeShader::ApplyLoopVertexVerticesKernelA( OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int V_ITa_ofs, int V_W_ofs, int offset, bool pass, int start, int end) { - + glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &_subComputeVertexA); glUniform1i(_uniformVertexPass, pass ? 1 : 0); glUniform1i(_tableOffsetUniforms[V_ITa], V_ITa_ofs); diff --git a/opensubdiv/osd/glslDispatcher.h b/opensubdiv/osd/glslDispatcher.h index b3250e6d..e958cd7b 100644 --- a/opensubdiv/osd/glslDispatcher.h +++ b/opensubdiv/osd/glslDispatcher.h @@ -60,7 +60,7 @@ #if not defined(__APPLE__) #include #else - #include + #include #endif #include "../version.h" @@ -73,34 +73,41 @@ class OsdGlslKernelDispatcher : public OsdKernelDispatcher { public: OsdGlslKernelDispatcher(int levels); - + virtual ~OsdGlslKernelDispatcher(); virtual void ApplyBilinearFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyBilinearEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyBilinearVertexVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; virtual void ApplyCatmarkFaceVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyCatmarkEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyCatmarkVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyCatmarkVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const; virtual void ApplyLoopEdgeVerticesKernel(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyLoopVertexVerticesKernelB(FarMesh * mesh, int offset, int level, int start, int end, void * data) const; - + virtual void ApplyLoopVertexVerticesKernelA(FarMesh * mesh, int offset, bool pass, int level, int start, int end, void * data) const; + virtual void ApplyVertexEdit(FarMesh *mesh, int offset, int level, void * clientdata) const {} + virtual void CopyTable(int tableIndex, size_t size, const void *ptr); + virtual void AllocateEditTables(int n) {} + + virtual void UpdateEditTable(int tableIndex, const FarTable &offsets, const FarTable &values, + int operation, int primVarOffset, int primVarWidth) {} + virtual void OnKernelLaunch(); virtual void OnKernelFinish(); @@ -154,33 +161,33 @@ protected: void ApplyCatmarkVertexVerticesKernelA(OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int V_ITa_ofs, int V_W_ofs, int offset, bool pass, int start, int end); - + void ApplyLoopEdgeVerticesKernel(OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int E_IT_ofs, int E_W_ofs, int offset, int start, int end); void ApplyLoopVertexVerticesKernelB(OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int V_IT_ofs, int V_ITa_ofs, int V_W_ofs, int offset, int start, int end); - + void ApplyLoopVertexVerticesKernelA(OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, int V_ITa_ofs, int V_W_ofs, int offset, bool pass, int start, int end); - + void UseProgram () const; struct Match { Match(int numVertexElements, int numVaryingElements) : _numVertexElements(numVertexElements), _numVaryingElements(numVaryingElements) { } - + bool operator() (ComputeShader const & shader) { return (shader._numVertexElements == _numVertexElements && shader._numVaryingElements == _numVaryingElements); } - - int _numVertexElements, + + int _numVertexElements, _numVaryingElements; }; friend struct Match; private: - void transformGpuBufferData(OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, + void transformGpuBufferData(OsdGpuVertexBuffer *vertex, OsdGpuVertexBuffer *varying, GLint offset, int start, int end) const; int _numVertexElements; @@ -191,10 +198,10 @@ protected: GLuint _uniformVertexPass; GLuint _uniformIndexStart; GLuint _uniformIndexOffset; - + GLuint _vertexUniform, _varyingUniform; - + // shader locations GLuint _subComputeFace, // general face-vertex kernel (all schemes) _subComputeEdge, // edge-vertex kernel (catmark + loop schemes) @@ -216,9 +223,9 @@ protected: ComputeShader * _shader; // texture for vertex - GLuint _vertexTexture, + GLuint _vertexTexture, _varyingTexture; - + OsdGpuVertexBuffer *_currentVertexBuffer, *_currentVaryingBuffer; diff --git a/opensubdiv/osd/glslKernel.glsl b/opensubdiv/osd/glslKernel.glsl index 93063870..9d9eb5b4 100644 --- a/opensubdiv/osd/glslKernel.glsl +++ b/opensubdiv/osd/glslKernel.glsl @@ -68,8 +68,8 @@ uniform samplerBuffer _E0_S; uniform samplerBuffer _V0_S; uniform bool vertexPass; -uniform int indexOffset = 0; // index offset for the level -uniform int indexStart = 0; // start index for given batch +uniform int indexOffset = 0; // index offset for the level +uniform int indexStart = 0; // start index for given batch uniform int F_IT_ofs; uniform int F_ITa_ofs; @@ -83,8 +83,8 @@ uniform int V_W_ofs; +-----+---------------------------------+----- n-1 | Level n || | n+1 +-----+---------------------------------+----- - ^ ^ - indexOffset | + ^ ^ + indexOffset | indexStart */ @@ -107,9 +107,9 @@ uniform samplerBuffer varyingData; // float[NUM_VARYING] out vec3 outPosition; out vec3 outNormal; #if NUM_VARYING > 0 -out float outVaryingData[NUM_VARYING]; // output feedback (mapped as a subrange of vertices) +out float outVaryingData[NUM_VARYING]; // output feedback (mapped as a subrange of vertices) #endif -//out vec3 outVaryingData; // output feedback (mapped as a subrange of vertices) +//out vec3 outVaryingData; // output feedback (mapped as a subrange of vertices) void clear(out Vertex v) { @@ -221,7 +221,7 @@ void catmarkComputeEdge() #ifdef OPT_E0_S_VEC2 float faceWeight = weight.y; #else - float faceWeight = texelFetch(_E0_S, E_W_ofs/2+i*2+1).x; + float faceWeight = texelFetch(_E0_S, E_W_ofs+i*2+1).x; #endif addWithWeight(dst, readVertex(eidx.z), faceWeight); @@ -292,8 +292,8 @@ void catmarkComputeVertexA() ? texelFetch(_V0_S, V_W_ofs+i).x : 1.0 - texelFetch(_V0_S, V_W_ofs+i).x; - // In the case of fractional weight, the weight must be inverted since - // the value is shared with the k_Smooth kernel (statistically the + // In the case of fractional weight, the weight must be inverted since + // the value is shared with the k_Smooth kernel (statistically the // k_Smooth kernel runs much more often than this one) if (weight>0.0 && weight<1.0 && n > 0) weight=1.0-weight; @@ -336,7 +336,7 @@ void catmarkComputeVertexB() Vertex dst; clear(dst); - + addWithWeight(dst, readVertex(p), weight * wv); for(int j = 0; j < n; ++j){ @@ -372,7 +372,7 @@ void loopComputeVertexB() Vertex dst; clear(dst); - + addWithWeight(dst, readVertex(p), weight * (1.0-(beta*n))); for(int j = 0; j < n; ++j){ diff --git a/opensubdiv/osd/kernelDispatcher.h b/opensubdiv/osd/kernelDispatcher.h index 0035d03f..eca2d318 100644 --- a/opensubdiv/osd/kernelDispatcher.h +++ b/opensubdiv/osd/kernelDispatcher.h @@ -87,9 +87,14 @@ public: virtual void CopyTable(int tableIndex, size_t size, const void *ptr) = 0; + virtual void AllocateEditTables(int n) = 0; + + virtual void UpdateEditTable(int tableIndex, const FarTable &offsets, const FarTable &values, + int operation, int primVarOffset, int primVarWidth) = 0; + virtual void OnKernelLaunch() = 0; - + virtual void OnKernelFinish() = 0; virtual OsdVertexBuffer *InitializeVertexBuffer(int numElements, int count) = 0; @@ -101,12 +106,12 @@ public: virtual void Synchronize() = 0; template void UpdateTable(int tableIndex, const T & table) { - + CopyTable(tableIndex, table.GetMemoryUsed(), table[0]); _tableOffsets[tableIndex].resize(_maxLevel); for (int i = 0; i < _maxLevel; ++i) - _tableOffsets[tableIndex][i] = table[i] - table[0]; + _tableOffsets[tableIndex][i] = (int)(table[i] - table[0]); } static OsdKernelDispatcher *CreateKernelDispatcher( int levels, int kernel ) { @@ -123,7 +128,7 @@ public: enum { E_IT, E_W, - V_ITa, + V_ITa, V_IT, V_W, F_IT, @@ -165,7 +170,7 @@ protected: static Factory &GetInstance() { return _instance; } - + static Factory _instance; protected: @@ -187,6 +192,16 @@ protected: protected: int _maxLevel; std::vector _tableOffsets[TABLE_MAX]; + + struct VertexEditArrayInfo { + std::vector offsetOffsets; + std::vector valueOffsets; + std::vector numEdits; + int operation; + int primVarOffset; + int primVarWidth; + }; + std::vector _edits; }; } // end namespace OPENSUBDIV_VERSION diff --git a/opensubdiv/osd/local.h b/opensubdiv/osd/local.h index afb511ed..21aa341e 100644 --- a/opensubdiv/osd/local.h +++ b/opensubdiv/osd/local.h @@ -15,7 +15,7 @@ #define OSD_ERROR(...) printf(__VA_ARGS__); //#define OSD_DEBUG(...) printf(__VA_ARGS__); -#define OSD_DEBUG(...) - +#define OSD_DEBUG(...) + #endif // OSD_LOCAL_H diff --git a/opensubdiv/osd/mesh.cpp b/opensubdiv/osd/mesh.cpp index b4488134..b1b1d19d 100644 --- a/opensubdiv/osd/mesh.cpp +++ b/opensubdiv/osd/mesh.cpp @@ -54,6 +54,13 @@ // exclude the implied warranties of merchantability, fitness for // a particular purpose and non-infringement. // + +#if not defined(__APPLE__) + #include +#else + #include +#endif + #include #include "../version.h" @@ -67,15 +74,21 @@ namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { -OsdMesh::OsdMesh() : _fMesh(NULL), _dispatcher(NULL) { } +OsdMesh::OsdMesh() : _farMesh(NULL), _dispatcher(NULL) { } OsdMesh::~OsdMesh() { - if(_dispatcher) + if(_dispatcher) delete _dispatcher; - - if(_fMesh) - delete _fMesh; + + if(_farMesh) + delete _farMesh; + + // delete ptex coordinates + for (int i=0; i<(int)_ptexCoordinates.size(); ++i) { + if (glIsTexture(_ptexCoordinates[i])) + glDeleteTextures(1,&_ptexCoordinates[i]); + } } void @@ -87,12 +100,12 @@ OsdMesh::createTables( FarSubdivisionTables const * tables ) { _dispatcher->UpdateTable(OsdKernelDispatcher::E_W, tables->Get_E_W()); _dispatcher->UpdateTable(OsdKernelDispatcher::V_W, tables->Get_V_W()); - if ( const FarCatmarkSubdivisionTables * cctable = + if ( const FarCatmarkSubdivisionTables * cctable = dynamic_cast*>(tables) ) { // catmark _dispatcher->UpdateTable(OsdKernelDispatcher::F_IT, cctable->Get_F_IT()); _dispatcher->UpdateTable(OsdKernelDispatcher::F_ITa, cctable->Get_F_ITa()); - } else if ( const FarBilinearSubdivisionTables * btable = + } else if ( const FarBilinearSubdivisionTables * btable = dynamic_cast*>(tables) ) { // bilinear _dispatcher->UpdateTable(OsdKernelDispatcher::F_IT, btable->Get_F_IT()); @@ -102,8 +115,20 @@ OsdMesh::createTables( FarSubdivisionTables const * tables ) { _dispatcher->CopyTable(OsdKernelDispatcher::F_IT, 0, NULL); _dispatcher->CopyTable(OsdKernelDispatcher::F_ITa, 0, NULL); } +} - CHECK_GL_ERROR("Mesh, update tables\n"); +void +OsdMesh::createEditTables( FarVertexEditTables const *editTables ) { + + int numEditBatches = editTables->GetNumBatches(); + + _dispatcher->AllocateEditTables(numEditBatches); + + for (int i=0; i::VertexEdit & edit = editTables->GetBatch(i); + _dispatcher->UpdateEditTable(i, edit.Get_Offsets(), edit.Get_Values(), + edit.GetOperation(), edit.GetPrimvarOffset(), edit.GetPrimvarWidth()); + } } bool @@ -119,31 +144,60 @@ OsdMesh::Create(OsdHbrMesh *hbrMesh, int level, int kernel, std::vector * r } _level = level; - + // create Far mesh OSD_DEBUG("Create MeshFactory\n"); FarMeshFactory meshFactory(hbrMesh, _level); - _fMesh = meshFactory.Create(_dispatcher); - - OSD_DEBUG("PREP: NumCoarseVertex = %d\n", _fMesh->GetNumCoarseVertices()); - OSD_DEBUG("PREP: NumVertex = %d\n", _fMesh->GetNumVertices()); + _farMesh = meshFactory.Create(_dispatcher); + + OSD_DEBUG("PREP: NumCoarseVertex = %d\n", _farMesh->GetNumCoarseVertices()); + OSD_DEBUG("PREP: NumVertex = %d\n", _farMesh->GetNumVertices()); + + createTables( _farMesh->GetSubdivision() ); + + FarVertexEditTables const *editTables = _farMesh->GetVertexEdit(); + if (editTables) + createEditTables( editTables ); - createTables( _fMesh->GetSubdivision() ); - // copy the remapping table if the client needs to remap vertex indices from // Osd to Hbr for comparison / regression purposes. if (remap) (*remap)=meshFactory.GetRemappingTable(); + // create ptex coordinates if exists in hbr + for (int i=0; i<(int)_ptexCoordinates.size(); ++i) { + if (glIsTexture(_ptexCoordinates[i])) + glDeleteTextures(1,&_ptexCoordinates[i]); + } + _ptexCoordinates.resize(level, 0); + for (int i=0; i & ptexCoordinates = _farMesh->GetPtexCoordinates(i+1); + if (ptexCoordinates.empty()) + continue; + + int size = (int)ptexCoordinates.size() * sizeof(GLint); + const void *data = &ptexCoordinates[0]; + + GLuint buffer; + glGenBuffers(1, & buffer ); + glBindBuffer( GL_TEXTURE_BUFFER, buffer ); + glBufferData( GL_TEXTURE_BUFFER, size, data, GL_STATIC_DRAW); + + glGenTextures(1, & _ptexCoordinates[i]); + glBindTexture( GL_TEXTURE_BUFFER, _ptexCoordinates[i]); + glTexBuffer( GL_TEXTURE_BUFFER, GL_RG32I, buffer); + glDeleteBuffers(1, & buffer ); + } return true; } OsdVertexBuffer * -OsdMesh::InitializeVertexBuffer(int numElements) -{ - if (!_dispatcher) return NULL; +OsdMesh::InitializeVertexBuffer(int numElements) { + + if (!_dispatcher) + return NULL; return _dispatcher->InitializeVertexBuffer(numElements, GetTotalVertices()); } @@ -154,7 +208,7 @@ OsdMesh::Subdivide(OsdVertexBuffer *vertex, OsdVertexBuffer *varying) { _dispatcher->OnKernelLaunch(); - _fMesh->Subdivide(_level+1); + _farMesh->Subdivide(_level+1); _dispatcher->OnKernelFinish(); diff --git a/opensubdiv/osd/mesh.h b/opensubdiv/osd/mesh.h index e9b48d97..e9074bab 100644 --- a/opensubdiv/osd/mesh.h +++ b/opensubdiv/osd/mesh.h @@ -83,6 +83,8 @@ typedef HbrVertex OsdHbrVertex; typedef HbrHalfedge OsdHbrHalfedge; typedef HbrFace OsdHbrFace; +class OsdPtexIndicesBuffer; + class OsdMesh { public: @@ -90,16 +92,18 @@ public: virtual ~OsdMesh(); - // Given a valid HbrMesh, create an OsdMesh - // - cappable of densely refining up to 'level' + // Given a valid HbrMesh, create an OsdMesh + // - capable of densely refining up to 'level' // - subdivision kernel one of (kCPU, kOPENMP, kCUDA, kGLSL, kCL) - // - optional "remapping" vector that connects Osd and Hbr vertex indices + // - optional "remapping" vector that connects Osd and Hbr vertex indices // (for regression) bool Create(OsdHbrMesh *hbrMesh, int level, int kernel, std::vector * remap=0); - FarMesh *GetFarMesh() { return _fMesh; } + FarMesh *GetFarMesh() { return _farMesh; } - OsdVertexBuffer *InitializeVertexBuffer(int numElements); + int GetLevel() const { return _level; } + + OsdVertexBuffer * InitializeVertexBuffer(int numElements); // for non-interleaved vertex data void Subdivide(OsdVertexBuffer *vertex, OsdVertexBuffer *varying = NULL); @@ -111,19 +115,28 @@ public: void Synchronize(); - int GetTotalVertices() const { return _fMesh->GetNumVertices(); } + int GetTotalVertices() const { return _farMesh->GetNumVertices(); } - int GetNumCoarseVertices() const { return _fMesh->GetNumCoarseVertices(); } + int GetNumCoarseVertices() const { return _farMesh->GetNumCoarseVertices(); } + // Returns the texture buffer containing the ptex face index for each face of + // the mesh. + GLuint GetPtexCoordinatesTextureBuffer(int level) const { return _ptexCoordinates[level-1]; } + protected: void createTables( FarSubdivisionTables const * tables ); - - FarMesh *_fMesh; - + + void createEditTables( FarVertexEditTables const * editTables ); + + FarMesh *_farMesh; + int _level; OsdKernelDispatcher * _dispatcher; + + std::vector _ptexCoordinates; // index of the coarse parent face + sub-face coordinates (cf. far) + }; } // end namespace OPENSUBDIV_VERSION diff --git a/opensubdiv/osd/pTexture.cpp b/opensubdiv/osd/pTexture.cpp new file mode 100644 index 00000000..c1762689 --- /dev/null +++ b/opensubdiv/osd/pTexture.cpp @@ -0,0 +1,1062 @@ +// +// Copyright (C) Pixar. All rights reserved. +// +// This license governs use of the accompanying software. If you +// use the software, you accept this license. If you do not accept +// the license, do not use the software. +// +// 1. Definitions +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. +// copyright law. A "contribution" is the original software, or +// any additions or changes to the software. +// A "contributor" is any person or entity that distributes its +// contribution under this license. +// "Licensed patents" are a contributor's patent claims that read +// directly on its contribution. +// +// 2. Grant of Rights +// (A) Copyright Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free copyright license to reproduce its contribution, +// prepare derivative works of its contribution, and distribute +// its contribution or any derivative works that you create. +// (B) Patent Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free license under its licensed patents to make, have +// made, use, sell, offer for sale, import, and/or otherwise +// dispose of its contribution in the software or derivative works +// of the contribution in the software. +// +// 3. Conditions and Limitations +// (A) No Trademark License- This license does not grant you +// rights to use any contributor's name, logo, or trademarks. +// (B) If you bring a patent claim against any contributor over +// patents that you claim are infringed by the software, your +// patent license from such contributor to the software ends +// automatically. +// (C) If you distribute any portion of the software, you must +// retain all copyright, patent, trademark, and attribution +// notices that are present in the software. +// (D) If you distribute any portion of the software in source +// code form, you may do so only under this license by including a +// complete copy of this license with your distribution. If you +// distribute any portion of the software in compiled or object +// code form, you may only do so under a license that complies +// with this license. +// (E) The software is licensed "as-is." You bear the risk of +// using it. The contributors give no express warranties, +// guarantees or conditions. You may have additional consumer +// rights under your local laws which this license cannot change. +// To the extent permitted under your local laws, the contributors +// exclude the implied warranties of merchantability, fitness for +// a particular purpose and non-infringement. +// + +#if not defined(__APPLE__) + #include +#else + #include +#endif + +#include + +#include +#include + +#include "../osd/local.h" +#include "../osd/pTexture.h" + +namespace OpenSubdiv { +namespace OPENSUBDIV_VERSION { + +int OsdPTexture::_gutterWidth = 0; +int OsdPTexture::_pageMargin = 0; +int OsdPTexture::_gutterDebug = 0; + +// block : atomic texture unit, points to the texels contained in a face +// +// |-----------------------| |-----------------------| +// | (u,v) | | (u,v) | +// | | | | +// | | | | +// | Block 0 | | Block 1 | +// | | | | +// | vres | + | vres | ... +// | | | | +// | | | | +// | | | | +// | | | | +// | ures | | ures | +// |-----------------------| |-----------------------| +// +struct block { + + int idx; // PTex face index + + unsigned short u, v; // location in memory pages + + Ptex::Res current, // current resolution of the block + native; // native resolution of the block + + // comparison operator : true when the current texel area of "b" is greater than "a" + static bool currentAreaSort(block const * a, block const * b) { + int darea = a->current.ulog2 * a->current.vlog2 - + b->current.ulog2 * b->current.vlog2; + if (darea==0) + return a->current.ulog2 < b->current.ulog2; + else + return darea < 0; + } + + // returns a "distance" metric from the native texel resolution + int8_t distanceFromNative( ) const { + int8_t udist = native.ulog2-current.ulog2, + vdist = native.vlog2-current.vlog2; + + return udist * udist + vdist * vdist; + } + + // desirability predicates for resolution scaling optimizations + static bool downsizePredicate( block const * b0, block const * b1 ) { + int8_t d0 = b0->distanceFromNative(), + d1 = b1->distanceFromNative(); + + if (d0==d1) + return (b0->current.ulog2 * b0->current.vlog2) < + (b1->current.ulog2 * b1->current.vlog2); + else + return d0 < d1; + } + + static bool upsizePredicate( block const * b0, block const * b1 ) { + int8_t d0 = b0->distanceFromNative(), + d1 = b1->distanceFromNative(); + + if (d0==d1) + return (b0->current.ulog2 * b0->current.vlog2) < + (b1->current.ulog2 * b1->current.vlog2); + else + return d0 > d1; + } + + friend std::ostream & operator <<(std::ostream &s, block const & b); +}; + +// page : a handle on a single page of the GL texture array that contains the +// packed PTex texels. Pages populate "empty" slots with "blocks" of +// texels. +// Note : pages are square, because i said so... +// +// |--------------------------| |------------|-------------| +// | | |............|.............| +// | | |............|.............| +// | | |............|.............| +// | | |.... B 0 ...|.... B 1 ..../ +// | | |............|.............| +// | | |............|.............| +// | | |............|.............| +// | Empty Page | |------------|-------------| +// | | packed => |..........................| +// | | |..........................| +// | | |..........................| +// | | |.......... B 2 ...........| +// | | |..........................| +// | | |..........................| +// | | |..........................| +// |--------------------------| |--------------------------| +// +struct page { + + //---------------------------------------------------------------- + // slot : rectangular block of available texels in a page + struct slot { + GLushort u, v, ures, vres; + + slot( GLushort size ) : u(0), v(0), ures(size), vres(size) { } + + slot( GLushort iu, GLushort iv, GLushort iures, GLushort ivres ) : + u(iu), v(iv), ures(iures), vres(ivres) { } + + // true if a block can fit in this slot + bool fits( block const * b ) { + return ( (b->current.u()+2*OsdPTexture::GetGutterWidth())<=ures ) && + ((b->current.v()+2*OsdPTexture::GetGutterWidth())<=vres); + } + }; + + //---------------------------------------------------------------- + typedef std::list blist; + blist blocks; + + typedef std::list slist; + slist slots; + + // construct a page with a single empty slot the size of the page + page( GLushort pagesize ) { + slots.push_back( slot( pagesize) ); + } + + // true if there is no empty texels in the page (ie. no slots left) + bool isFull( ) const { + return slots.size()==0; + } + + // true when the block "b" is successfully added to this page : + // + // |--------------------------| |------------|-------------| + // | | |............| | + // | | |............| | + // | | |.... B .....| Right Slot | + // | | |............| | + // | | |............| | + // | | |------------|-------------| + // | Original Slot | ==> | | + // | | | | + // | | | Bottom Slot | + // | | | | + // | | | | + // |--------------------------| |--------------------------| + // + bool addBlock( block * b ) { + for (slist::iterator i=slots.begin(); i!=slots.end(); ++i) { + + if (i->fits( b )) { + + blocks.push_back( b ); + + int w = OsdPTexture::GetGutterWidth(); + + b->u=i->u + w; + b->v=i->v + w; + + // add new slot to the right + if (i->ures > (b->current.u()+2*w)) { + slots.push_front( slot( i->u+b->current.u()+2*w, + i->v, + i->ures-b->current.u()-2*w, + b->current.v()+2*w)); + } + + // add new slot to the bottom + if (i->vres > (b->current.v()+2*w)) { + slots.push_back( slot( i->u, + i->v+b->current.v()+2*w, + i->ures, + i->vres-b->current.v()-2*w )); + } + + slots.erase( i ); + return true; + } + } + return false; + } + + friend std::ostream & operator <<(std::ostream &s, const page & p); +}; + +// Ptex reader helper - manages up/down sizing and texel packing of blocks into +// texel pages and generate the GL texture buffers for rendering : +// +// Pages table : maps the face (quad) to a page based on gl_PrimitiveID +// +// face idx = 1 +// V +// 0 1 2 ... +// |----------|----------|----------|-------- +// | page idx | page idx | page idx | ... +// |----------|----------|----------|-------- +// +// Layout table : coordinates of the gprim in the page +// +// - layout coords = vec4 normalized(top left (u,v), ures, vres)) +// +// face idx = 1 +// V +// 0 1 2 ... +// |--------|--------|--------|-------- +// | layout | layout | layout | ... +// |--------|--------|--------|-------- +// +// Texels buffer : the packed texels +// +// page 0 page 1 +// |------------|-------------||------------|-------------||------ +// |............|.............||............|.............|| +// |............|.............||............|.............|| +// |............|.............||............|..... ( X ) .|| +// |.... B 0 ...|.... B 1 ....||.... B 3 ...|.............|| +// |............|.............||............|.............|| +// |............|.............||............|.............|| +// |............|.............||............|.............|| +// |------------|-------------||------------|.... B 5 ....|| +// |..........................||............|.............|| +// |..........................||............|.............|| +// |..........................||............|.............|| +// |.......... B 2 ...........||.... B 4 ...|.............|| +// |..........................||............|.............|| +// |..........................||............|.............|| +// |..........................||............|.............|| +// |--------------------------||--------------------------||------- +// +// GLSL shader computes texel coordinates with : +// * vec3 ( X ) = ( layout.u + X, layout.v + Y, page idx ) +// +class loader { +public: + loader( PtexTexture * ); + + ~loader( ) { + ClearPages(); + } + + const GLushort GetPageSize( ) const { + return _pagesize; + } + + const unsigned long int GetNumBlocks( ) const { + return (unsigned long int)_blocks.size(); + } + + const unsigned long int GetNumPages( ) const { + return (unsigned long int)_pages.size(); + } + + const GLuint * GetIndexBuffer( ) const { + return _indexBuffer; + } + + const GLfloat * GetLayoutBuffer( ) const { + return _layoutBuffer; + } + + const GLubyte * GetTexelBuffer( ) const { + return _texelBuffer; + } + + unsigned long int GetUncompressedSize() const { + return _txc * _bpp; + } + + unsigned long int GetNativeUncompressedSize() const { + return _txn * _bpp; + } + + void OptimizeResolution( unsigned long int memrec ); + + void OptimizePacking( int maxnumpages ); + + bool GenerateBuffers( ); + + float EvaluateWaste( ) const; + + void ClearPages( ); + + void ClearBuffers(); + + void PrintBlocks() const; + + void PrintPages() const; + +protected: + + friend struct block; + + PtexTexture * _ptex; + +private: + + int _bpp; // bits per pixel + + unsigned long int _txc, // texel count for current resolution + _txn; // texel count for native resolution + + std::vector _blocks; + + std::vector _pages; + GLushort _pagesize; + + GLuint * _indexBuffer; + GLfloat * _layoutBuffer; + GLubyte * _texelBuffer; +}; + + +loader::loader( PtexTexture * p ) : + _ptex(p), _indexBuffer( NULL ), _layoutBuffer( NULL ), _texelBuffer(NULL) +{ + _bpp = p->numChannels() * Ptex::DataSize( p->dataType() ); + + _txn = 0; + + int nf = p->numFaces(); + _blocks.clear(); + _blocks.resize( nf ); + + for (int i=0; igetFaceInfo(i); + _blocks[i].idx=i; + _blocks[i].current=_blocks[i].native=f.res; + _txn += f.res.u() * f.res.v(); + } + + _txc = _txn; +} + +// attempt to re-size per-face resolutions to hit the uncompressed texel +// memory use requirement +void +loader::OptimizeResolution( unsigned long int memrec ) +{ + unsigned long int txrec = memrec / _bpp; + + if (txrec==_txc) + return; + else + { + unsigned long int txcur = _txc; + + if (_blocks.size()==0) + return; + + std::vector blocks( _blocks.size() ); + for (unsigned long int i=0; i0) && (txcur>txrec) ) + { + unsigned long int txsaved = txcur; + + // start stealing from largest to smallest down + for (int i=(int)blocks.size()-1; i>=0; --i) + { + block * b = blocks[i]; + + // we have already hit rock bottom resolution... skip this block + if (b->current.ulog2==0 || b->current.vlog2==0) + continue; + + GLushort ures = (1<<(unsigned)(b->current.ulog2-1)), + vres = (1<<(unsigned)(b->current.vlog2-1)); + + int diff = b->current.size() - ures * vres; + + // we are about to overshoot the limit with our big blocks : + // skip until we find something smaller + if ( ((unsigned long int)diff>txcur) || ((txcur-diff)current.ulog2--; + b->current.vlog2--; + txcur-=diff; + } + + // couldn't scavenge anymore even from smallest faces : time to bail out. + if (txsaved==txcur) + break; + } + _txc = txcur; + } else { + + // increasing footprint -------------------------------------- + + // blocks that have already been resized heavily will be considered first + std::sort(blocks.begin(), blocks.end(), block::upsizePredicate ); + + while ( (txcur < _txn) && (txcur < txrec) ) + { + unsigned long int txsaved = txcur; + + // start adding back to the largest faces first + for (int i=0; i<(int)blocks.size(); ++i) + { + block * b = blocks[i]; + + // already at native resolution... nothing to be done + if (b->current == b->native) + continue; + + GLushort ures = (1<<(unsigned)(b->current.ulog2+1)), + vres = (1<<(unsigned)(b->current.vlog2+1)); + + int diff = ures * vres - b->current.size(); + + // we are about to overshoot the limit with our big blocks : + // skip until we find something smaller + if ( (txcur + diff) > txrec ) + continue; + + b->current.ulog2++; + b->current.vlog2++; + txcur+=diff; + } + + // couldn't scavenge anymore even from smallest faces : time to bail out. + if (txsaved==txcur) + break; + } + _txc = txcur; + } + } +} + +// greedy packing of blocks into pages +void +loader::OptimizePacking( int maxnumpages ) +{ + if (_blocks.size()==0) + return; + + // generate a vector of pointers to the blocks ------------------- + std::vector blocks( _blocks.size() ); + for (unsigned long int i=0; icurrent.ulog2 > blocks[0]->current.vlog2 ? + blocks[0]->current.u() : blocks[0]->current.v(); + + // at least 2*GUTTER_WIDTH of margin required for each page to fit + _pagesize += OsdPTexture::GetPageMargin(); + + // grow the pagesize to make sure the optimization will not exceed the maximum + // number of pages allowed + for (int npages=_txc/(_pagesize*_pagesize); npages>maxnumpages; _pagesize<<=1) + npages = _txc/(_pagesize*_pagesize ); + + ClearPages( ); + + // save some memory allocation time : guess the number of pages from the + // number of texels + _pages.reserve( _txc / (_pagesize*_pagesize) + 1 ); + + // pack blocks into slots ---------------------------------------- + for (unsigned long int i=0, firstslot=0; i<_blocks.size(); ++i ) { + + block * b = blocks[i]; + + // traverse existing pages for a suitable slot --------------- + bool added=false; + for( unsigned long int p=firstslot; p<_pages.size(); ++p ) + if( (added=_pages[p]->addBlock( b )) ) { + break; + } + + // if none was found : start new page + if( !added ) { + page * p = new page( _pagesize ); + p->addBlock(b); + _pages.push_back( p ); + } + + // adjust the page flag to the first page with open slots + if( (_pages.size()>(firstslot+1)) && + (_pages[firstslot+1]->isFull()) ) + ++firstslot; + } +} + +// resample border texels for guttering +// +static void +resampleBorder(PtexTexture * ptex, int face, int edgeId, GLubyte *result, int edge, + int dstLength, int bpp, float srcStart=0.0f, float srcEnd=1.0f) +{ + const Ptex::FaceInfo & pf = ptex->getFaceInfo(face); + PtexFaceData * data = ptex->getData(face); + + int edgeLength = (edgeId==0||edgeId==2) ? pf.res.u() : pf.res.v(); + int srcOffset = (int)(srcStart*edgeLength); + int srcLength = (int)((srcEnd-srcStart)*edgeLength); + + GLubyte *border = new GLubyte[bpp*srcLength]; + + // order of the result will be flipped to match adjacent pixel order + for(int i=0;igetPixel(u, v, &border[i*bpp]); + } + // nearest resample to fit dstLength + for(int i=0;idataType(), 4); + } + } + + delete[] border; +} + +// flip order of pixel buffer +static void +flipBuffer(GLubyte *buffer, int length, int bpp) +{ + for(int i=0; igetFaceInfo(face); + + // copy adjacent borders + int adjface = fi.adjface(edge); + if(adjface != -1) { + int ae = fi.adjedge(edge); + if (!fi.isSubface() && ptex->getFaceInfo(adjface).isSubface()) { + /* nonsubface -> subface (1:0.5) see http://ptex.us/adjdata.html for more detail + +------------------+ + | face | + +--------edge------+ + | adj face | | + +----------+-------+ + */ + resampleBorder(ptex, adjface, ae, border, edge, length/2, bpp); + const Ptex::FaceInfo &sfi1 = ptex->getFaceInfo(adjface); + adjface = sfi1.adjface((ae+3)%4); + const Ptex::FaceInfo &sfi2 = ptex->getFaceInfo(adjface); + ae = (sfi1.adjedge((ae+3)%4)+3)%4; + resampleBorder(ptex, adjface, ae, border+(length/2*bpp), edge, length/2, bpp); + + } else if (fi.isSubface() && !ptex->getFaceInfo(adjface).isSubface()) { + /* subface -> nonsubface (0.5:1). two possible configuration + case 1 case 2 + +----------+----------+ +----------+----------+--------+ + | face | B | | | face | B | + +---edge---+----------+ +----------+--edge----+--------+ + |0.0 0.5 1.0| |0.0 0.5 1.0| + | adj face | | adj face | + +---------------------+ +---------------------+ + */ + int Bf = fi.adjface((edge+1)%4); + int Be = fi.adjedge((edge+1)%4); + int f = ptex->getFaceInfo(Bf).adjface((Be+1)%4); + int e = ptex->getFaceInfo(Bf).adjedge((Be+1)%4); + if(f == adjface && e == ae) // case 1 + resampleBorder(ptex, adjface, ae, border, edge, length, bpp, 0.0, 0.5); + else // case 2 + resampleBorder(ptex, adjface, ae, border, edge, length, bpp, 0.5, 1.0); + + } else { + /* ordinary case (1:1 match) + +------------------+ + | face | + +--------edge------+ + | adj face | + +----------+-------+ + */ + resampleBorder(ptex, adjface, ae, border, edge, length, bpp); + } + } else { + /* border edge. duplicate itself + +-----------------+ + | face | + +-------edge------+ + */ + resampleBorder(ptex, face, edge, border, edge, length, bpp); + flipBuffer(border, length, bpp); + } +} + +// average corner pixels by traversing all adjacent faces around vertex +// +static bool +averageCorner(PtexTexture *ptex, float *accumPixel, int numchannels, int face, int edge) +{ + const Ptex::FaceInfo &fi = ptex->getFaceInfo(face); + + int adjface = fi.adjface(edge); + + // don't average T-vertex. + if (fi.isSubface() && !ptex->getFaceInfo(adjface).isSubface()) + return false; + + int valence = 0; + int currentFace = face; + int currentEdge = edge; + int uv[4][2] = {{0,0}, {1,0}, {1,1}, {0,1}}; + float *pixel = (float*)alloca(sizeof(float)*numchannels); + + // clear result buffer + memset(accumPixel, 0, sizeof(float)*numchannels); + + do { + valence++; + Ptex::FaceInfo info = ptex->getFaceInfo(currentFace); + ptex->getPixel(currentFace, + uv[currentEdge][0] * (info.res.u()-1), + uv[currentEdge][1] * (info.res.v()-1), + pixel, 0, numchannels); + for(int j=0; jgetFaceInfo(b->idx); + GLubyte * border = new GLubyte[_pagesize * _bpp]; + + for(int w=0; wcurrent.u() : b->current.v(); + // XXX: for now, sample same edge regardless of gutter depth + sampleNeighbor(_ptex, border, b->idx, edge, len, _bpp); + + GLubyte *s = border, *d; + for(int j=0;jv-1-w) + _bpp*(b->u+j); + break; + case Ptex::e_right: + d += stride*(b->v+j) + _bpp*(b->u+b->current.u()+w); + break; + case Ptex::e_top: + d += stride*(b->v+b->current.v()+w) + _bpp*(b->u+len-j-1); + break; + case Ptex::e_left: + d += stride*(b->v+len-j-1) + _bpp*(b->u-1-w); + break; + } + for(int k=0; k<_bpp; k++) + *d++ = *s++; + } + } + } + delete[] border; + + // average corner pixels + int numchannels = _ptex->numChannels(); + float *accumPixel = new float[numchannels]; + int uv[4][2] = {{-1,-1}, {1,-1}, {1,1}, {-1,1}}; + for(int edge=0; edge<4; edge++) { + + if(averageCorner(_ptex, accumPixel, numchannels, b->idx, edge)) { + // set accumPixel to 4 corner + int du = (b->u+gwidth*uv[edge][0]); + int dv = (b->v+gwidth*uv[edge][1]); + if(edge==1||edge==2) du += b->current.u()-gwidth-1; + if(edge==2||edge==3) dv += b->current.v()-gwidth-1; + // .. over (gwidth+1)x(gwidth+1) pixels for each corner + for(int u=0; u<=gwidth; ++u) { + for(int v=0; v<=gwidth; ++v) { + GLubyte *d = pptr + (dv+u)*stride + (du+v)*_bpp; + Ptex::ConvertFromFloat(d, accumPixel, _ptex->dataType(), numchannels); + } + } + } + } + delete[] accumPixel; +} + +// prepares the data for the texture samplers used by the GLSL tables to render +// PTex texels +bool +loader::GenerateBuffers( ) +{ + if (_pages.size()==0) return false; + + // populate the page index lookup texture ------------------------ + _indexBuffer = new GLuint[ _blocks.size() ]; + for (unsigned long int i=0; i<_pages.size(); ++i) { + page * p = _pages[i]; + for (page::blist::iterator j=p->blocks.begin(); j!=p->blocks.end(); ++j) + _indexBuffer[ (*j)->idx ] = i; + } + + // populate the layout lookup texture ---------------------------- + GLfloat * lptr = _layoutBuffer = new GLfloat[ 4 * _blocks.size() ]; + for (unsigned long int i=0; i<_blocks.size(); ++ i) { + // normalize coordinates by pagesize resolution ! + *lptr++ = (GLfloat) _blocks[i].u / (GLfloat) _pagesize; + *lptr++ = (GLfloat) _blocks[i].v / (GLfloat) _pagesize; + *lptr++ = (GLfloat) _blocks[i].current.u() / (GLfloat) _pagesize; + *lptr++ = (GLfloat) _blocks[i].current.v() / (GLfloat) _pagesize; + } + + // populate the texels ------------------------------------------- + int stride = _bpp * _pagesize, + pagestride = stride * _pagesize; + + GLubyte * pptr = _texelBuffer = new GLubyte[ pagestride * _pages.size() ]; + + for (unsigned long int i=0; i<_pages.size(); i++) { + + page * p = _pages[i]; + + for (page::blist::iterator b=p->blocks.begin(); b!=p->blocks.end(); ++b) { + _ptex->getData( (*b)->idx, pptr + stride*(*b)->v + _bpp*(*b)->u, stride, (*b)->current ); + + if(OsdPTexture::GetGutterWidth() > 0) + guttering(_ptex, *b, pptr, _bpp, _pagesize, stride, OsdPTexture::GetGutterWidth()); + } + + pptr += pagestride; + } + + return true; +} + +void +loader::ClearBuffers( ) +{ delete [] _indexBuffer; + delete [] _layoutBuffer; + delete [] _texelBuffer; +} + +// returns a ratio of texels wasted in the final GPU texture : anything under 5% +// is pretty good compared to our previous solution... +float +loader::EvaluateWaste( ) const +{ + unsigned long int wasted=0; + for( unsigned long int i=0; i<_pages.size(); i++ ) { + page * p = _pages[i]; + for( page::slist::iterator s=p->slots.begin(); s!=p->slots.end(); ++s ) + wasted += s->ures * s->vres; + } + return (float)((double)wasted/(double)_txc); +} + +void +loader::ClearPages( ) +{ for( unsigned long int i=0; i<_pages.size(); i++ ) + delete _pages[i]; + _pages.clear(); +} + +void +loader::PrintBlocks() const +{ for( unsigned long int i=0; i<_blocks.size(); ++i ) + std::cout<<_blocks[i]<u<<" "<v<<" "<ures<<" "<vres<<"} "; + s<<" }\n"; + + s<<" blocks {"; + for (page::blist::const_iterator i=p.blocks.begin(); i!=p.blocks.end(); ++i) + s<<" "<< **i; + s<<" }\n"; + + s<<"}"; + return s; +} + +OsdPTexture::~OsdPTexture() +{ + // delete pages lookup --------------------------------- + if (glIsTexture(_pages)) + glDeleteTextures(1,&_pages); + + // delete layout lookup -------------------------------- + if (glIsTexture(_layout)) + glDeleteTextures(1,&_layout); + + // delete textures lookup ------------------------------ + if (glIsTexture(_texels)) + glDeleteTextures(1,&_texels); +} + +static GLuint genTextureBuffer( GLenum format, GLsizeiptr size, GLvoid const * data ) { + + GLuint buffer, result; + glGenBuffers(1, & buffer ); + glBindBuffer( GL_TEXTURE_BUFFER, buffer ); + glBufferData( GL_TEXTURE_BUFFER, size, data, GL_STATIC_DRAW); + + glGenTextures(1, & result); + glBindTexture( GL_TEXTURE_BUFFER, result); + glTexBuffer( GL_TEXTURE_BUFFER, format, buffer); + + glDeleteBuffers(1,&buffer); + + return result; +} + +OsdPTexture::OsdPTexture() + : _width(0), _height(0), _depth(0),_pages(0), _layout(0), _texels(0) +{ } + +OsdPTexture * +OsdPTexture::Create( PtexTexture * reader, unsigned long int targetMemory ) { + OsdPTexture * result=NULL; + + // Read the ptexture data and pack the texels + loader ldr( reader ); + + unsigned long int nativeSize = ldr.GetNativeUncompressedSize(), + targetSize = targetMemory; + + if (targetSize!=0 && targetSize!=nativeSize) + ldr.OptimizeResolution( targetSize ); + + GLint maxnumpages = 0; + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxnumpages); + + ldr.OptimizePacking( maxnumpages ); + + if (!ldr.GenerateBuffers( )) + return result; + + // Setup GPU memory + unsigned long int nfaces = ldr.GetNumBlocks(); + + GLuint pages = genTextureBuffer( GL_R32I, + nfaces * sizeof(GLint), + ldr.GetIndexBuffer() ); + + GLuint layout = genTextureBuffer( GL_RGBA32F, + nfaces * 4 * sizeof(GLfloat), + ldr.GetLayoutBuffer() ); + + GLenum format, type; + switch(reader->dataType()) + { + case Ptex::dt_uint16 : type = GL_UNSIGNED_SHORT; break; + case Ptex::dt_float : type = GL_FLOAT; break; + case Ptex::dt_half : type = GL_HALF_FLOAT_ARB; break; + default : type = GL_UNSIGNED_BYTE; break; + } + + switch(reader->numChannels()) + { + case 1 : format = GL_LUMINANCE; break; + case 2 : format = GL_LUMINANCE_ALPHA; break; + case 3 : format = GL_RGB; break; + case 4 : format = GL_RGBA; break; + default: format = GL_LUMINANCE; break; + } + + // actual texels texture array + GLuint texels; + glGenTextures(1,&texels); + glBindTexture(GL_TEXTURE_2D_ARRAY,texels); + + // XXXX for the time being, filtering is off - once cross-patch filtering + // is in place, we will use glGenSamplers to dynamically access these settings. + if (GetGutterWidth() > 0) { + glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, + (type==GL_FLOAT) ? GL_RGBA32F : GL_RGBA, + ldr.GetPageSize(), + ldr.GetPageSize(), + ldr.GetNumPages(), + 0, format, type, + ldr.GetTexelBuffer()); + + if (GLuint err = glGetError()) { + printf("(OsdPtexture::Create) GL error %x :", err); + return result; + } + + ldr.ClearBuffers( ); + + // Return the Osd Ptexture object + result = new OsdPTexture; + + result->_width = ldr.GetPageSize(); + result->_height = ldr.GetPageSize(); + result->_depth = ldr.GetNumPages(); + + result->_format = format; + + result->_pages = pages; + result->_layout = layout; + result->_texels = texels; + + return result; +} + +} // end namespace OPENSUBDIV_VERSION +} // end namespace OpenSubdiv + diff --git a/opensubdiv/osd/pTexture.h b/opensubdiv/osd/pTexture.h new file mode 100644 index 00000000..62681cd5 --- /dev/null +++ b/opensubdiv/osd/pTexture.h @@ -0,0 +1,159 @@ +// +// Copyright (C) Pixar. All rights reserved. +// +// This license governs use of the accompanying software. If you +// use the software, you accept this license. If you do not accept +// the license, do not use the software. +// +// 1. Definitions +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. +// copyright law. A "contribution" is the original software, or +// any additions or changes to the software. +// A "contributor" is any person or entity that distributes its +// contribution under this license. +// "Licensed patents" are a contributor's patent claims that read +// directly on its contribution. +// +// 2. Grant of Rights +// (A) Copyright Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free copyright license to reproduce its contribution, +// prepare derivative works of its contribution, and distribute +// its contribution or any derivative works that you create. +// (B) Patent Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free license under its licensed patents to make, have +// made, use, sell, offer for sale, import, and/or otherwise +// dispose of its contribution in the software or derivative works +// of the contribution in the software. +// +// 3. Conditions and Limitations +// (A) No Trademark License- This license does not grant you +// rights to use any contributor's name, logo, or trademarks. +// (B) If you bring a patent claim against any contributor over +// patents that you claim are infringed by the software, your +// patent license from such contributor to the software ends +// automatically. +// (C) If you distribute any portion of the software, you must +// retain all copyright, patent, trademark, and attribution +// notices that are present in the software. +// (D) If you distribute any portion of the software in source +// code form, you may do so only under this license by including a +// complete copy of this license with your distribution. If you +// distribute any portion of the software in compiled or object +// code form, you may only do so under a license that complies +// with this license. +// (E) The software is licensed "as-is." You bear the risk of +// using it. The contributors give no express warranties, +// guarantees or conditions. You may have additional consumer +// rights under your local laws which this license cannot change. +// To the extent permitted under your local laws, the contributors +// exclude the implied warranties of merchantability, fitness for +// a particular purpose and non-infringement. +// +#ifndef OSD_PTEXTURE_H +#define OSD_PTEXTURE_H + +#if not defined(__APPLE__) + #if defined(_WIN32) + #include + #endif + #include +#else + #include +#endif + +#include "../version.h" + +#include +#include +#include +#include + +class PtexTexture; +class OsdMesh; + +namespace OpenSubdiv { +namespace OPENSUBDIV_VERSION { + +// OsdPTexture : implements simple support for ptex textures +// +// The current implementation declares _texels as a GL_TEXTURE_2D_ARRAY of +// n pages of a resolution that matches that of the largest face in the PTex file. +// +// Two GL_TEXTURE_BUFFER constructs are used +// as lookup tables : +// * _pages stores the array index in which a given face is located +// * _layout stores 4 float coordinates : top-left corner and width/height for each face +// +// GLSL fragments use gl_PrimitiveID and gl_TessCoords to access the _pages and _layout +// indirection tables, which provide then texture coordinates for the texels stored in +// the _texels texture array. +// +// Hbr provides per-face support for a ptex face indexing scheme. This +// class provides a container that can be initialized by an OsdMesh and +// instantiated in GPU memory as a texture buffer object that can be +// accessed by GLSL shaders. +// + +class OsdPTexture { +public: + + static OsdPTexture * Create( PtexTexture * reader, unsigned long int targetMemory ); + + // Returns the texture buffer containing the lookup table associate each ptex + // face index with its 3D texture page in the texels texture array. + GLuint GetPagesTextureBuffer() const { return _pages; } + + // Returns the texture buffer containing the layout of the ptex faces in the + // texels texture array. + GLuint GetLayoutTextureBuffer() const { return _layout; } + + // Returns the texels texture array. + GLuint GetTexelsTexture() const { return _texels; } + + ~OsdPTexture( ); + + // get/set guttering control variables + static int GetGutterWidth() { return _gutterWidth; } + + static int GetPageMargin() { return _pageMargin; } + + static int GetGutterDebug() { return _gutterDebug; } + + static void SetGutterWidth(int width) { _gutterWidth = width; } + + static void SetPageMargin(int margin) { _pageMargin = margin; } + + static void SetGutterDebug(int debug) { _gutterDebug = debug; } + +private: + OsdPTexture(); + + // Non-copyable, so these are not implemented: + OsdPTexture(OsdPTexture const &); + OsdPTexture & operator=(OsdPTexture const &); + + + GLsizei _width, // widht / height / depth of the 3D texel buffer + _height, + _depth; + + GLint _format; // texel color format + + GLuint _pages, // per-face page indices into the texel array + _layout, // per-face lookup table (vec4 : top-left corner & width / height) + _texels; // texel data + + static int _gutterWidth, _pageMargin, _gutterDebug; +}; + +} // end namespace OPENSUBDIV_VERSION +using namespace OPENSUBDIV_VERSION; + +} // end namespace OpenSubdiv + +#endif // OSD_PTEXTURE_H diff --git a/opensubdiv/osd/vertex.h b/opensubdiv/osd/vertex.h index 996f39c9..4130d643 100644 --- a/opensubdiv/osd/vertex.h +++ b/opensubdiv/osd/vertex.h @@ -1,7 +1,9 @@ -#ifndef GSD_VERTEX_H -#define GSD_VERTEX_H +#ifndef OSD_VERTEX_H +#define OSD_VERTEX_H #include "../version.h" +#include "../hbr/face.h" +#include "../hbr/vertexEdit.h" namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { @@ -13,8 +15,10 @@ public: OsdVertex(const OsdVertex &src) {} void AddWithWeight(const OsdVertex & i, float weight, void * = 0) {} - void AddVaryingWithWeight(const OsdVertex & i, float weight, void * = 0) {} + void AddVaryingWithWeight(const OsdVertex & i, float weight, void * = 0) {} void Clear(void * = 0) {} + void ApplyVertexEdit(const OpenSubdiv::HbrVertexEdit &) { } + void ApplyMovingVertexEdit(const OpenSubdiv::HbrMovingVertexEdit &) { } }; } // end namespace OPENSUBDIV_VERSION @@ -22,4 +26,4 @@ using namespace OPENSUBDIV_VERSION; } // end namespace OpenSubdiv -#endif // GSD_VERTEX_H +#endif // OSD_VERTEX_H diff --git a/opensubdiv/osd/vertexBuffer.cpp b/opensubdiv/osd/vertexBuffer.cpp index f9f9bf13..97922d12 100644 --- a/opensubdiv/osd/vertexBuffer.cpp +++ b/opensubdiv/osd/vertexBuffer.cpp @@ -1,20 +1,75 @@ +// +// Copyright (C) Pixar. All rights reserved. +// +// This license governs use of the accompanying software. If you +// use the software, you accept this license. If you do not accept +// the license, do not use the software. +// +// 1. Definitions +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. +// copyright law. A "contribution" is the original software, or +// any additions or changes to the software. +// A "contributor" is any person or entity that distributes its +// contribution under this license. +// "Licensed patents" are a contributor's patent claims that read +// directly on its contribution. +// +// 2. Grant of Rights +// (A) Copyright Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free copyright license to reproduce its contribution, +// prepare derivative works of its contribution, and distribute +// its contribution or any derivative works that you create. +// (B) Patent Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free license under its licensed patents to make, have +// made, use, sell, offer for sale, import, and/or otherwise +// dispose of its contribution in the software or derivative works +// of the contribution in the software. +// +// 3. Conditions and Limitations +// (A) No Trademark License- This license does not grant you +// rights to use any contributor's name, logo, or trademarks. +// (B) If you bring a patent claim against any contributor over +// patents that you claim are infringed by the software, your +// patent license from such contributor to the software ends +// automatically. +// (C) If you distribute any portion of the software, you must +// retain all copyright, patent, trademark, and attribution +// notices that are present in the software. +// (D) If you distribute any portion of the software in source +// code form, you may do so only under this license by including a +// complete copy of this license with your distribution. If you +// distribute any portion of the software in compiled or object +// code form, you may only do so under a license that complies +// with this license. +// (E) The software is licensed "as-is." You bear the risk of +// using it. The contributors give no express warranties, +// guarantees or conditions. You may have additional consumer +// rights under your local laws which this license cannot change. +// To the extent permitted under your local laws, the contributors +// exclude the implied warranties of merchantability, fitness for +// a particular purpose and non-infringement. +// #include "../version.h" #if not defined(__APPLE__) #include #else - #include + #include #endif -#include "vertexBuffer.h" +#include "../osd/vertexBuffer.h" #include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { -OsdVertexBuffer::~OsdVertexBuffer() -{ +OsdVertexBuffer::~OsdVertexBuffer() { } OsdGpuVertexBuffer::OsdGpuVertexBuffer(int numElements, int numVertices) : @@ -28,21 +83,30 @@ OsdGpuVertexBuffer::OsdGpuVertexBuffer(int numElements, int numVertices) : } void -OsdGpuVertexBuffer::UpdateData(const float *src, int numVertices) -{ - glBindBuffer(GL_ARRAY_BUFFER, _vbo); +OsdGpuVertexBuffer::UpdateData(const float *src, int numVertices) { + + glBindBuffer(GL_ARRAY_BUFFER, GetGpuBuffer()); float * pointer = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - memcpy(pointer, src, _numElements * numVertices * sizeof(float)); + memcpy(pointer, src, GetNumElements() * numVertices * sizeof(float)); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, 0); } -OsdGpuVertexBuffer::~OsdGpuVertexBuffer() -{ - glDeleteBuffers(1, &_vbo); +void +OsdGpuVertexBuffer::GetBufferData(float * data, int firstVert, int numVerts) { + glBindBuffer(GL_ARRAY_BUFFER, GetGpuBuffer()); + + glGetBufferSubData(GL_ARRAY_BUFFER, GetNumElements() * firstVert * sizeof(float), + GetNumElements() * numVerts * sizeof(float), + data); + glBindBuffer(GL_ARRAY_BUFFER, 0); } +OsdGpuVertexBuffer::~OsdGpuVertexBuffer() { + + glDeleteBuffers(1, &_vbo); +} OsdCpuVertexBuffer::OsdCpuVertexBuffer(int numElements, int numVertices) : OsdVertexBuffer(numElements), _cpuVbo(NULL), _vboSize(0), _vbo(0) @@ -51,27 +115,29 @@ OsdCpuVertexBuffer::OsdCpuVertexBuffer(int numElements, int numVertices) : _cpuVbo = new float[numElements * numVertices]; } -OsdCpuVertexBuffer::~OsdCpuVertexBuffer() -{ +OsdCpuVertexBuffer::~OsdCpuVertexBuffer() { + delete [] _cpuVbo; if (_vbo) glDeleteBuffers(1, &_vbo); } void -OsdCpuVertexBuffer::UpdateData(const float *src, int numVertices) -{ +OsdCpuVertexBuffer::UpdateData(const float *src, int numVertices) { + memcpy(_cpuVbo, src, _numElements * numVertices * sizeof(float)); } GLuint -OsdCpuVertexBuffer::GetGpuBuffer() -{ +OsdCpuVertexBuffer::GetGpuBuffer() { + if (!_vbo) glGenBuffers(1, &_vbo); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBufferData(GL_ARRAY_BUFFER, _vboSize * sizeof(float), _cpuVbo, GL_STREAM_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); + return _vbo; } diff --git a/opensubdiv/osd/vertexBuffer.h b/opensubdiv/osd/vertexBuffer.h index 0973c6f8..c67321fe 100644 --- a/opensubdiv/osd/vertexBuffer.h +++ b/opensubdiv/osd/vertexBuffer.h @@ -1,3 +1,59 @@ +// +// Copyright (C) Pixar. All rights reserved. +// +// This license governs use of the accompanying software. If you +// use the software, you accept this license. If you do not accept +// the license, do not use the software. +// +// 1. Definitions +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. +// copyright law. A "contribution" is the original software, or +// any additions or changes to the software. +// A "contributor" is any person or entity that distributes its +// contribution under this license. +// "Licensed patents" are a contributor's patent claims that read +// directly on its contribution. +// +// 2. Grant of Rights +// (A) Copyright Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free copyright license to reproduce its contribution, +// prepare derivative works of its contribution, and distribute +// its contribution or any derivative works that you create. +// (B) Patent Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free license under its licensed patents to make, have +// made, use, sell, offer for sale, import, and/or otherwise +// dispose of its contribution in the software or derivative works +// of the contribution in the software. +// +// 3. Conditions and Limitations +// (A) No Trademark License- This license does not grant you +// rights to use any contributor's name, logo, or trademarks. +// (B) If you bring a patent claim against any contributor over +// patents that you claim are infringed by the software, your +// patent license from such contributor to the software ends +// automatically. +// (C) If you distribute any portion of the software, you must +// retain all copyright, patent, trademark, and attribution +// notices that are present in the software. +// (D) If you distribute any portion of the software in source +// code form, you may do so only under this license by including a +// complete copy of this license with your distribution. If you +// distribute any portion of the software in compiled or object +// code form, you may only do so under a license that complies +// with this license. +// (E) The software is licensed "as-is." You bear the risk of +// using it. The contributors give no express warranties, +// guarantees or conditions. You may have additional consumer +// rights under your local laws which this license cannot change. +// To the extent permitted under your local laws, the contributors +// exclude the implied warranties of merchantability, fitness for +// a particular purpose and non-infringement. +// #ifndef OSD_VERTEX_BUFFER_H #define OSD_VERTEX_BUFFER_H @@ -7,7 +63,7 @@ #endif #include #else - #include + #include #endif #include // memcpy (tobe moved to cpp) @@ -44,6 +100,10 @@ public: return _vbo; } + // Copies the vertex data from the compute device into + // the pointer. + void GetBufferData(float * data, int firstVert, int numVerts); + protected: GLuint _vbo; }; @@ -51,7 +111,7 @@ protected: class OsdCpuVertexBuffer : public OsdVertexBuffer { public: OsdCpuVertexBuffer(int numElements, int numVertices); - + virtual ~OsdCpuVertexBuffer(); virtual void UpdateData(const float *src, int numVertices); diff --git a/opensubdiv/tools/stringify/main.cpp b/opensubdiv/tools/stringify/main.cpp index a801bbac..6985fb33 100644 --- a/opensubdiv/tools/stringify/main.cpp +++ b/opensubdiv/tools/stringify/main.cpp @@ -69,11 +69,11 @@ std::string stringify( std::string const & line ) { for (int i=0; i<(int)line.size(); ++i) { // escape double quotes - if (line[i]=='"') { + if (line[i]=='"') { s << '\\' ; inconstant = inconstant ? false : true; } - + // escape backslash if (inconstant and line[i]=='\\') s << '\\' ; diff --git a/opensubdiv/version.h b/opensubdiv/version.h index b546cfad..ef593a1c 100644 --- a/opensubdiv/version.h +++ b/opensubdiv/version.h @@ -58,6 +58,6 @@ #ifndef OPENSUBDIV_VERSION_H #define OPENSUBDIV_VERSION_H -#define OPENSUBDIV_VERSION v1_0 +#define OPENSUBDIV_VERSION v1_1 #endif /* OPENSUBDIV_VERSION_H */ diff --git a/regression/CMakeLists.txt b/regression/CMakeLists.txt index fe160cb0..98fcaeb1 100644 --- a/regression/CMakeLists.txt +++ b/regression/CMakeLists.txt @@ -57,4 +57,28 @@ add_subdirectory(far_regression) -add_subdirectory(osd_regression) +if( OPENGL_FOUND AND GLEW_FOUND AND GLUT_FOUND) + add_subdirectory(osd_regression) +else() + set(MISSING "") + + if (NOT OPENGL_FOUND) + list(APPEND MISSING OpenGL) + endif() + + if (NOT GLEW_FOUND) + list(APPEND MISSING glew) + endif() + + if (NOT GLUT_FOUND) + list(APPEND MISSING glut) + endif() + + message(WARNING + "The following libraries could not be found : ${MISSING}. " + "The osd regression test will not be available. " + "If you have these libraries installed, please specify their " + "path to cmake (through the GLEW_LOCATION and GLUT_LOCATION " + "command line arguments or environment variables)." + ) +endif() diff --git a/regression/common/shape_utils.h b/regression/common/shape_utils.h index 9ba5aff4..93862ff8 100644 --- a/regression/common/shape_utils.h +++ b/regression/common/shape_utils.h @@ -57,6 +57,9 @@ #ifndef SHAPE_UTILS_H #define SHAPE_UTILS_H +#include +#include + #include #include @@ -64,14 +67,14 @@ #include #include -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ static char const * sgets( char * s, int size, char ** stream ) { - for (int i=0; i intargs; std::vector floatargs; std::vector stringargs; }; static shape * parseShape(char const * shapestr, int axis=1); - + ~shape(); - + int getNverts() const { return (int)verts.size()/3; } int getNfaces() const { return (int)nvertsPerFace.size(); } std::vector verts; std::vector uvs; - std::vector nvertsPerFace; - std::vector faceverts; + std::vector nvertsPerFace; + std::vector faceverts; std::vector faceuvs; std::vector tags; -}; +}; -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ shape::~shape() { for (int i=0; i<(int)tags.size(); ++i) delete tags[i]; } -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ shape::tag * shape::tag::parseTag(char const * line) { tag * t = 0; - - const char* cp = &line[2]; + + const char* cp = &line[2]; char name[50]; while (*cp == ' ') cp++; - if (sscanf(cp, "%s", &name )!=1) return t; - while (*cp && *cp != ' ') cp++; + if (sscanf(cp, "%s", name )!=1) return t; + while (*cp && *cp != ' ') cp++; int nints=0, nfloats=0, nstrings=0; while (*cp == ' ') cp++; if (sscanf(cp, "%d/%d/%d", &nints, &nfloats, &nstrings)!=3) return t; - while (*cp && *cp != ' ') cp++; + while (*cp && *cp != ' ') cp++; std::vector intargs; for (int i=0; i floatargs; @@ -150,16 +153,16 @@ shape::tag * shape::tag::parseTag(char const * line) { while (*cp == ' ') cp++; if (sscanf(cp, "%f", &val)!=1) return t; floatargs.push_back(val); - while (*cp && *cp != ' ') cp++; + while (*cp && *cp != ' ') cp++; } std::vector stringargs; for (int i=0; iintargs = intargs; t->floatargs = floatargs; t->stringargs = stringargs; - + return t; } -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ shape * shape::parseShape(char const * shapestr, int axis ) { shape * s = new shape; char * str=const_cast(shapestr), line[256]; bool done = false; - while( not done ) + while( not done ) { done = sgets(line, sizeof(line), &str)==0; - char* end = &line[strlen(line)-1]; - if (*end == '\n') *end = '\0'; // strip trailing nl - float x, y, z, u, v; - switch (line[0]) { - case 'v': switch (line[1]) - { case ' ': if(sscanf(line, "v %f %f %f", &x, &y, &z) == 3) - s->verts.push_back(x); - switch( axis ) { - case 0 : s->verts.push_back(-z); + char* end = &line[strlen(line)-1]; + if (*end == '\n') *end = '\0'; // strip trailing nl + float x, y, z, u, v; + switch (line[0]) { + case 'v': switch (line[1]) + { case ' ': if(sscanf(line, "v %f %f %f", &x, &y, &z) == 3) + s->verts.push_back(x); + switch( axis ) { + case 0 : s->verts.push_back(-z); s->verts.push_back(y); break; - case 1 : s->verts.push_back(y); + case 1 : s->verts.push_back(y); s->verts.push_back(z); break; - } break; - case 't': if(sscanf(line, "vt %f %f", &u, &v) == 2) { - s->uvs.push_back(u); - s->uvs.push_back(v); + } break; + case 't': if(sscanf(line, "vt %f %f", &u, &v) == 2) { + s->uvs.push_back(u); + s->uvs.push_back(v); } break; case 'n' : break; // skip normals for now } break; case 'f': if(line[1] == ' ') { - int vi, ti, ni; - const char* cp = &line[2]; - while (*cp == ' ') cp++; - int nverts = 0, nitems=0; - while( (nitems=sscanf(cp, "%d/%d/%d", &vi, &ti, &ni))>0) { - nverts++; - s->faceverts.push_back(vi-1); - if(nitems >= 1) s->faceuvs.push_back(ti-1); - while (*cp && *cp != ' ') cp++; - while (*cp == ' ') cp++; - } - s->nvertsPerFace.push_back(nverts); + int vi, ti, ni; + const char* cp = &line[2]; + while (*cp == ' ') cp++; + int nverts = 0, nitems=0; + while( (nitems=sscanf(cp, "%d/%d/%d", &vi, &ti, &ni))>0) { + nverts++; + s->faceverts.push_back(vi-1); + if(nitems >= 1) s->faceuvs.push_back(ti-1); + while (*cp && *cp != ' ') cp++; + while (*cp == ' ') cp++; + } + s->nvertsPerFace.push_back(nverts); } break; case 't' : if(line[1] == ' ') { @@ -226,19 +229,19 @@ shape * shape::parseShape(char const * shapestr, int axis ) { return s; } -//------------------------------------------------------------------------------ -template +//------------------------------------------------------------------------------ +template void applyTags( OpenSubdiv::HbrMesh * mesh, shape const * sh ) { for (int i=0; i<(int)sh->tags.size(); ++i) { shape::tag * t = sh->tags[i]; if (t->name=="crease") { - for (int j=0; j<(int)t->intargs.size()-1; ++j) { + for (int j=0; j<(int)t->intargs.size()-1; j += 2) { OpenSubdiv::HbrVertex * v = mesh->GetVertex( t->intargs[j] ), * w = mesh->GetVertex( t->intargs[j+1] ); OpenSubdiv::HbrHalfedge * e = 0; - if( v && w ) { + if( v && w ) { if( !(e = v->GetEdge(w) ) ) e = w->GetEdge(v); if(e) { @@ -270,7 +273,7 @@ void applyTags( OpenSubdiv::HbrMesh * mesh, shape const * sh ) { printf("expecting 1 integer for \"interpolateboundary\" tag n. %d\n", i); continue; } - switch( t->intargs[0] ) { + switch( t->intargs[0] ) { case 0 : mesh->SetInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryNone); break; case 1 : mesh->SetInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryEdgeAndCorner); break; case 2 : mesh->SetInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryEdgeOnly); break; @@ -282,20 +285,20 @@ void applyTags( OpenSubdiv::HbrMesh * mesh, shape const * sh ) { else printf( "expecting single int argument for \"facevaryingpropagatecorners\"\n" ); } else if (t->name=="creasemethod") { - - OpenSubdiv::HbrCatmarkSubdivision * scheme = + + OpenSubdiv::HbrCatmarkSubdivision * scheme = dynamic_cast *>( mesh->GetSubdivision() ); - + if (not scheme) { printf("the \"creasemethod\" tag can only be applied to Catmark meshes\n"); continue; } - + if ((int)t->stringargs.size()==0) { printf("the \"creasemethod\" tag expects a string argument\n"); continue; } - + if( t->stringargs[0]=="normal" ) scheme->SetTriangleSubdivisionMethod( OpenSubdiv::HbrCatmarkSubdivision::k_Old); @@ -304,12 +307,140 @@ void applyTags( OpenSubdiv::HbrMesh * mesh, shape const * sh ) { OpenSubdiv::HbrCatmarkSubdivision::k_New); else printf("the \"creasemethod\" tag only accepts \"normal\" or \"chaikin\" as value (%s)\n", t->stringargs[0].c_str()); - + } else if (t->name=="vertexedit" or t->name=="edgeedit") { - printf("hierarchical edits not supported (yet)\n"); + int nops = 0; + int floatstride = 0; + int maxfloatwidth = 0; + std::vector::Operation > ops; + std::vector opnames; + std::vector varnames; + std::vector::Operation > opmodifiers; + std::vector floatwidths; + std::vector isP; + std::vector vvindex; + + for (int j=0; j<(int)t->stringargs.size(); j+=3) { + const std::string & opname = t->stringargs[j+2]; + const std::string & opmodifiername = t->stringargs[j]; + const std::string & varname = t->stringargs[j+1]; + + typename OpenSubdiv::HbrHierarchicalEdit::Operation opmodifier = OpenSubdiv::HbrVertexEdit::Set; + if (opmodifiername == "set") { + opmodifier = OpenSubdiv::HbrHierarchicalEdit::Set; + } else if (opmodifiername == "add") { + opmodifier = OpenSubdiv::HbrHierarchicalEdit::Add; + } else if (opmodifiername == "subtract") { + opmodifier = OpenSubdiv::HbrHierarchicalEdit::Subtract; + } else { + printf("invalid modifier %s\n", opmodifiername.c_str()); + continue; + } + + if (t->name=="vertexedit" && opname=="value" || opname=="sharpness") { + nops++; + + // only varname="P" is supported here for now. + if (varname != "P") continue; + + vvindex.push_back(0); + isP.push_back(true); + opnames.push_back(opname); + opmodifiers.push_back(opmodifier); + varnames.push_back(varname); + + if (opname=="sharpness") { + floatwidths.push_back(1); + floatstride += 1; + } else { + // assuming width of P == 3. should be replaced with 'P 0 3' like declaration + int numElements = 3; + maxfloatwidth = std::max(maxfloatwidth, numElements); + floatwidths.push_back(numElements); + floatstride += numElements; + } + } else { + printf("%s tag specifies invalid operation '%s %s' on Subdivmesh\n", t->name.c_str(), opmodifiername.c_str(), opname.c_str()); + } + } + + float *xformed = (float*)alloca(maxfloatwidth * sizeof(float)); + + int floatoffset = 0; + for(int j=0; jintargs.size();) { + int pathlength = t->intargs[k]; + + int faceid = t->intargs[k+1]; + int vertexid = t->intargs[k+pathlength]; + int nsubfaces = pathlength - 2; + int *subfaces = &t->intargs[k+2]; + OpenSubdiv::HbrFace * f = mesh->GetFace(faceid); + if (!f) { + printf("Invalid face %d specified for %s tag on SubdivisionMesh.\n", faceid, t->name.c_str()); + goto nexttag; + } + // Found the face. Do some preliminary error checking to make sure the path is + // correct. First value in path depends on the number of vertices of the face + // which we have in hand + if (nsubfaces && (subfaces[0] < 0 || subfaces[0] >= f->GetNumVertices()) ) { + printf("Invalid path component %d in %s tag on SubdivisionMesh.\n", subfaces[0], t->name.c_str()); + goto nexttag; + } + + // All subsequent values must be less than 4 (FIXME or 3 in the loop case?) + for (int l=1; l 3) { + printf("Invalid path component %d in %s tag on SubdivisionMesh.\n", subfaces[0], t->name.c_str()); + goto nexttag; + } + } + if (vertexid < 0 || vertexid > 3) { + printf("Invalid path component (vertexid) %d in %s tag on SubdivisionMesh.\n", vertexid, t->name.c_str()); + goto nexttag; + } + + // Transform all the float values associated with the tag if needed + if(opnames[j] != "sharpness") { + for(int l=0; lfloatargs[l + floatidx]; + } + + // Edits of facevarying data are a different hierarchical edit type altogether + OpenSubdiv::HbrVertexEdit * edit = new OpenSubdiv::HbrVertexEdit(faceid, nsubfaces, subfaces, + vertexid, vvindex[j], floatwidths[j], + isP[j], opmodifiers[j], xformed); + mesh->AddHierarchicalEdit(edit); + } else { + if (t->name == "vertexedit") { + OpenSubdiv::HbrCornerEdit * edit = new OpenSubdiv::HbrCornerEdit(faceid, nsubfaces, subfaces, + vertexid, opmodifiers[j], t->floatargs[floatidx]); + mesh->AddHierarchicalEdit(edit); + } else { + OpenSubdiv::HbrCreaseEdit * edit = new OpenSubdiv::HbrCreaseEdit(faceid, nsubfaces, subfaces, + vertexid, opmodifiers[j], t->floatargs[floatidx]); + mesh->AddHierarchicalEdit(edit); + } + } + + // Advance to next path + k += pathlength + 1; + + // Advance to start of float data + floatidx += floatstride; + } // End of integer processing loop + + // Next subop + floatoffset += floatwidths[j]; + + } // End of subop processing loop + } else if (t->name=="faceedit") { + printf("hierarchical face edits not supported (yet)\n"); } else { printf("Unknown tag : \"%s\" - skipping\n", t->name.c_str()); - } + } +nexttag: ; } } @@ -325,17 +456,17 @@ template OpenSubdiv::HbrMesh * createMesh( Scheme scheme=kCatmark) { OpenSubdiv::HbrMesh * mesh = 0; - + static OpenSubdiv::HbrBilinearSubdivision _bilinear; static OpenSubdiv::HbrLoopSubdivision _loop; static OpenSubdiv::HbrCatmarkSubdivision _catmark; - + switch (scheme) { case kBilinear : mesh = new OpenSubdiv::HbrMesh( &_bilinear ); break; case kLoop : mesh = new OpenSubdiv::HbrMesh( &_loop ); break; case kCatmark : mesh = new OpenSubdiv::HbrMesh( &_catmark ); break; } - + return mesh; } @@ -348,7 +479,7 @@ createVertices( shape const * sh, OpenSubdiv::HbrMesh * mesh, std::vectorverts[i*3], sh->verts[i*3+1], sh->verts[i*3+2] ); mesh->NewVertex( i, v ); } - + if (verts) *verts = sh->verts; } @@ -370,37 +501,37 @@ createTopology( shape const * sh, OpenSubdiv::HbrMesh * mesh, Scheme scheme) const int * fv=&(sh->faceverts[0]); for(int f=0, ptxidx=0;fgetNfaces(); f++ ) { - + int nv = sh->nvertsPerFace[f]; if ((scheme==kLoop) and (nv!=3)) { - printf("Trying to create a Loop surbd with non-triangle face\n"); - exit(1); + printf("Trying to create a Loop surbd with non-triangle face\n"); + exit(1); } - for(int j=0;j * origin = mesh->GetVertex( fv[j] ); + for(int j=0;j * origin = mesh->GetVertex( fv[j] ); OpenSubdiv::HbrVertex * destination = mesh->GetVertex( fv[ (j+1)%nv] ); OpenSubdiv::HbrHalfedge * opposite = destination->GetEdge(origin); - if(origin==NULL || destination==NULL) { - printf(" An edge was specified that connected a nonexistent vertex\n"); - exit(1); + if(origin==NULL || destination==NULL) { + printf(" An edge was specified that connected a nonexistent vertex\n"); + exit(1); } - if(origin == destination) { - printf(" An edge was specified that connected a vertex to itself\n"); - exit(1); + if(origin == destination) { + printf(" An edge was specified that connected a vertex to itself\n"); + exit(1); } - if(opposite && opposite->GetOpposite() ) { - printf(" A non-manifold edge incident to more than 2 faces was found\n"); - exit(1); + if(opposite && opposite->GetOpposite() ) { + printf(" A non-manifold edge incident to more than 2 faces was found\n"); + exit(1); } - if(origin->GetEdge(destination)) { + if(origin->GetEdge(destination)) { printf(" An edge connecting two vertices was specified more than once." - " It's likely that an incident face was flipped\n"); + " It's likely that an incident face was flipped\n"); exit(1); } } @@ -431,13 +562,13 @@ simpleHbr(char const * shapestr, Scheme scheme, std::vector * verts=0) { shape * sh = shape::parseShape( shapestr ); OpenSubdiv::HbrMesh * mesh = createMesh(scheme); - + createVertices(sh, mesh, verts); - + createTopology(sh, mesh, scheme); - + delete sh; - + return mesh; } @@ -448,13 +579,13 @@ simpleHbr(char const * shapestr, Scheme scheme, std::vector & verts) { shape * sh = shape::parseShape( shapestr ); OpenSubdiv::HbrMesh * mesh = createMesh(scheme); - + createVertices(sh, mesh, verts); - + createTopology(sh, mesh, scheme); - + delete sh; - + return mesh; } diff --git a/regression/far_regression/CMakeLists.txt b/regression/far_regression/CMakeLists.txt index 123475ea..8241cc07 100644 --- a/regression/far_regression/CMakeLists.txt +++ b/regression/far_regression/CMakeLists.txt @@ -55,18 +55,6 @@ # a particular purpose and non-infringement. # -find_package(IlmBase REQUIRED) - -if(NOT ILMBASE_FOUND) - message(WARNING - "IlmBase could not be found, so the OpenSubdiv far library regression " - "will not be available. If you do have IlmBase installed and see this " - "message, please add your IlmBase path to cmake/FindIlmBase.cmake or set it " - "in the ILMBASE_LOCATION environment variable." - ) - return() -endif() - include_directories( ${ILMBASE_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/opensubdiv @@ -82,6 +70,6 @@ add_executable(far_regression ) target_link_libraries(far_regression - ${ILMBASE_LIBS_DIRECTORY} + ${ILMBASE_LIBRARIES} ) diff --git a/regression/far_regression/main.cpp b/regression/far_regression/main.cpp index fce5f8e5..f6f7ad62 100644 --- a/regression/far_regression/main.cpp +++ b/regression/far_regression/main.cpp @@ -76,7 +76,7 @@ // - precision is currently held at 1e-6 // // - results cannot be bitwise identical as some vertex interpolations -// are not happening in the same order. +// are not happening in the same order. // // - only vertex interpolation is being tested at the moment. // @@ -96,17 +96,38 @@ struct xyzVV { void AddVaryingWithWeight(const xyzVV& , float, void * =0 ) { } void Clear( void * =0 ) { _pos.setValue(0.f, 0.f, 0.f); } void SetPosition(float x, float y, float z) { _pos=Imath::Vec3(x,y,z); } + void ApplyVertexEdit(const OpenSubdiv::HbrVertexEdit & edit) { + const float *src = edit.GetEdit(); + switch(edit.GetOperation()) { + case OpenSubdiv::HbrHierarchicalEdit::Set: + _pos.x = src[0]; + _pos.y = src[1]; + _pos.z = src[2]; + break; + case OpenSubdiv::HbrHierarchicalEdit::Add: + _pos.x += src[0]; + _pos.y += src[1]; + _pos.z += src[2]; + break; + case OpenSubdiv::HbrHierarchicalEdit::Subtract: + _pos.x -= src[0]; + _pos.y -= src[1]; + _pos.z -= src[2]; + break; + } + } + void ApplyMovingVertexEdit(const OpenSubdiv::HbrMovingVertexEdit &) { } const Imath::Vec3& GetPos() const { return _pos; } -private: +private: Imath::Vec3 _pos; }; //------------------------------------------------------------------------------ class xyzFV; typedef OpenSubdiv::HbrMesh xyzmesh; -typedef OpenSubdiv::HbrFace xyzface; -typedef OpenSubdiv::HbrVertex xyzvertex; +typedef OpenSubdiv::HbrFace xyzface; +typedef OpenSubdiv::HbrVertex xyzvertex; typedef OpenSubdiv::HbrHalfedge xyzhalfedge; typedef OpenSubdiv::HbrFaceOperator xyzFaceOperator; typedef OpenSubdiv::HbrVertexOperator xyzVertexOperator; @@ -119,9 +140,9 @@ static bool g_debugmode = false; static bool g_dumphbr = false; //------------------------------------------------------------------------------ -// visual debugging using Maya +// visual debugging using Maya // python dictionary dump - requires the script createMesh.py to read into Maya -// format is : [ { 'verts':[(1, 0, 0),(2, 0, 0)], +// format is : [ { 'verts':[(1, 0, 0),(2, 0, 0)], // 'faces':[[1 2 3 4],[5,6,7,8]] }, ... ] //------------------------------------------------------------------------------ static void dumpVerts( xyzmesh * mesh, int level ) { @@ -136,7 +157,7 @@ static void dumpVerts( xyzmesh * mesh, int level ) { } if (counter!=0 and (counter+1)%6==0) printf("\n\t\t\t"); } - printf("],\n"); + printf("],\n"); } //------------------------------------------------------------------------------ @@ -157,21 +178,21 @@ static void dumpFaces( xyzmesh * mesh, int level ) { continue; if (f->GetDepth()==level) { if (f->GetNumVertices()==4) - printf("[%6d, %6d, %6d, %6d], ", f->GetVertex(0)->GetID()-vertofs, - f->GetVertex(1)->GetID()-vertofs, + printf("[%6d, %6d, %6d, %6d], ", f->GetVertex(0)->GetID()-vertofs, + f->GetVertex(1)->GetID()-vertofs, f->GetVertex(2)->GetID()-vertofs, f->GetVertex(3)->GetID()-vertofs ); else if (f->GetNumVertices()==3) - printf("[%6d, %6d, %6d], ", f->GetVertex(0)->GetID()-vertofs, - f->GetVertex(1)->GetID()-vertofs, + printf("[%6d, %6d, %6d], ", f->GetVertex(0)->GetID()-vertofs, + f->GetVertex(1)->GetID()-vertofs, f->GetVertex(2)->GetID()-vertofs ); ++counter; - if (counter!=0 and (counter+4)%32==0) + if (counter!=0 and (counter+4)%32==0) printf("\n\t\t\t"); } } - printf("]\n"); + printf("]\n"); } //------------------------------------------------------------------------------ @@ -195,10 +216,10 @@ static void dumpVerts( fMesh * mesh, int level ) { printf("(%10f, %10f, %10f), ",verts[i].GetPos()[0], verts[i].GetPos()[1], verts[i].GetPos()[2] ); - if (i!=0 and (i+1)%6==0) + if (i!=0 and (i+1)%6==0) printf("\n\t\t\t"); } - printf("],\n"); + printf("],\n"); } //------------------------------------------------------------------------------ @@ -209,14 +230,14 @@ static void dumpQuadFaces( fMesh * mesh, int level ) { printf("\t'faces':[\t"); for (size_t i=0; i<(fverts.size()); i+=4) { - printf("[%6d, %6d, %6d, %6d], ", fverts[i ]-ofs, - fverts[i+1]-ofs, - fverts[i+2]-ofs, + printf("[%6d, %6d, %6d, %6d], ", fverts[i ]-ofs, + fverts[i+1]-ofs, + fverts[i+2]-ofs, fverts[i+3]-ofs ); - if (i!=0 and (i+4)%32==0) + if (i!=0 and (i+4)%32==0) printf("\n\t\t\t"); } - printf("]\n"); + printf("]\n"); } //------------------------------------------------------------------------------ @@ -228,10 +249,10 @@ static void dumpTriFaces( fMesh * mesh, int level ) { printf("\t'faces':[\t"); for (size_t i=0; i<(fverts.size()); i+=3) { printf("[%6d, %6d, %6d], ", fverts[i]-ofs, fverts[i+1]-ofs, fverts[i+2]-ofs ); - if (i!=0 and (i+4)%32==0) + if (i!=0 and (i+4)%32==0) printf("\n\t\t\t"); } - printf("]\n"); + printf("]\n"); } //------------------------------------------------------------------------------ @@ -249,20 +270,20 @@ static void dumpMesh( fMesh * mesh, int level, Scheme scheme=kCatmark ) { //------------------------------------------------------------------------------ // Returns true if a vertex or any of its parents is on a boundary bool VertexOnBoundary( xyzvertex const * v ) { - - if (not v) + + if (not v) return false; - + if (v->OnBoundary()) return true; - + xyzvertex const * pv = v->GetParentVertex(); if (pv) return VertexOnBoundary(pv); else { xyzhalfedge const * pe = v->GetParentEdge(); if (pe) { - return VertexOnBoundary(pe->GetOrgVertex()) or + return VertexOnBoundary(pe->GetOrgVertex()) or VertexOnBoundary(pe->GetDestVertex()); } else { xyzface const * pf = v->GetParentFace(), * rootf = pf; @@ -285,8 +306,8 @@ int checkMesh( char const * msg, xyzmesh * hmesh, int levels, Scheme scheme=kCat assert(msg); - int count=0; - Imath::Vec3 deltaAvg(0.0, 0.0, 0.0); + int count=0; + Imath::Vec3 deltaAvg(0.0, 0.0, 0.0); Imath::Vec3 deltaCnt(0, 0, 0); // subdivide on the Nsd side @@ -298,11 +319,11 @@ int checkMesh( char const * msg, xyzmesh * hmesh, int levels, Scheme scheme=kCat for (int i=1; i<=levels; ++i) if (g_dumphbr) dumpXYZMesh( hmesh, i, scheme ); - else + else dumpMesh( m, i, scheme ); } else printf("- %s (scheme=%d)\n", msg, scheme); - + std::vector const & remap = fact.GetRemappingTable(); int nverts = m->GetNumVertices(); @@ -318,13 +339,13 @@ int checkMesh( char const * msg, xyzmesh * hmesh, int levels, Scheme scheme=kCat if ( hmesh->GetInterpolateBoundaryMethod()==xyzmesh::k_InterpolateBoundaryNone and VertexOnBoundary(hv) ) continue; - - if ( hv->GetData().GetPos()[0] != nv.GetPos()[0] ) + + if ( hv->GetData().GetPos()[0] != nv.GetPos()[0] ) deltaCnt[0]++; - if ( hv->GetData().GetPos()[1] != nv.GetPos()[1] ) + if ( hv->GetData().GetPos()[1] != nv.GetPos()[1] ) deltaCnt[1]++; - if ( hv->GetData().GetPos()[2] != nv.GetPos()[2] ) + if ( hv->GetData().GetPos()[2] != nv.GetPos()[2] ) deltaCnt[2]++; Imath::Vec3 delta = hv->GetData().GetPos() - nv.GetPos(); @@ -341,10 +362,10 @@ int checkMesh( char const * msg, xyzmesh * hmesh, int levels, Scheme scheme=kCat nv.GetPos()[0], nv.GetPos()[1], nv.GetPos()[2] ); - count++; + count++; } } - + if (deltaCnt[0]) deltaAvg[0]/=deltaCnt[0]; if (deltaCnt[1]) @@ -353,20 +374,20 @@ int checkMesh( char const * msg, xyzmesh * hmesh, int levels, Scheme scheme=kCat deltaAvg[2]/=deltaCnt[2]; if (not g_debugmode) { - printf(" delta ratio : (%d/%d %d/%d %d/%d)\n", (int)deltaCnt.x, nverts, - (int)deltaCnt.y, nverts, + printf(" delta ratio : (%d/%d %d/%d %d/%d)\n", (int)deltaCnt.x, nverts, + (int)deltaCnt.y, nverts, (int)deltaCnt.x, nverts ); - printf(" average delta : (%.10f %.10f %.10f)\n", deltaAvg.x, + printf(" average delta : (%.10f %.10f %.10f)\n", deltaAvg.x, deltaAvg.y, deltaAvg.z ); if (count==0) printf(" success !\n"); } - + delete hmesh; delete m; - - return count; + + return count; } //------------------------------------------------------------------------------ @@ -411,6 +432,10 @@ int main(int argc, char ** argv) { #define test_catmark_tent #define test_catmark_tent_creases0 #define test_catmark_tent_creases1 +#define test_catmark_square_hedit0 +#define test_catmark_square_hedit1 +#define test_catmark_square_hedit2 +#define test_catmark_square_hedit3 #define test_loop_triangle_edgeonly #define test_loop_triangle_edgecorner @@ -428,141 +453,161 @@ int main(int argc, char ** argv) { #ifdef test_catmark_edgeonly #include "../shapes/catmark_edgeonly.h" - total += checkMesh( "test_catmark_edgeonly", simpleHbr(catmark_edgeonly, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_edgeonly", simpleHbr(catmark_edgeonly, kCatmark, 0), levels ); #endif #ifdef test_catmark_edgecorner #include "../shapes/catmark_edgecorner.h" - total += checkMesh( "test_catmark_edgeonly", simpleHbr(catmark_edgecorner, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_edgeonly", simpleHbr(catmark_edgecorner, kCatmark, 0), levels ); #endif #ifdef test_catmark_pyramid #include "../shapes/catmark_pyramid.h" - total += checkMesh( "test_catmark_pyramid", simpleHbr(catmark_pyramid, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_pyramid", simpleHbr(catmark_pyramid, kCatmark, 0), levels ); #endif #ifdef test_catmark_pyramid_creases0 #include "../shapes/catmark_pyramid_creases0.h" - total += checkMesh( "test_catmark_pyramid_creases0", simpleHbr(catmark_pyramid_creases0, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_pyramid_creases0", simpleHbr(catmark_pyramid_creases0, kCatmark, 0), levels ); #endif #ifdef test_catmark_pyramid_creases1 #include "../shapes/catmark_pyramid_creases1.h" - total += checkMesh( "test_catmark_pyramid_creases1", simpleHbr(catmark_pyramid_creases1, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_pyramid_creases1", simpleHbr(catmark_pyramid_creases1, kCatmark, 0), levels ); #endif #ifdef test_catmark_cube #include "../shapes/catmark_cube.h" - total += checkMesh( "test_catmark_cube", simpleHbr(catmark_cube, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_cube", simpleHbr(catmark_cube, kCatmark, 0), levels ); #endif #ifdef test_catmark_cube_creases0 #include "../shapes/catmark_cube_creases0.h" - total += checkMesh( "test_catmark_cube_creases0", simpleHbr(catmark_cube_creases0, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_cube_creases0", simpleHbr(catmark_cube_creases0, kCatmark, 0), levels ); #endif #ifdef test_catmark_cube_creases1 #include "../shapes/catmark_cube_creases1.h" - total += checkMesh( "test_catmark_cube_creases1", simpleHbr(catmark_cube_creases1, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_cube_creases1", simpleHbr(catmark_cube_creases1, kCatmark, 0), levels ); #endif #ifdef test_catmark_cube_corner0 #include "../shapes/catmark_cube_corner0.h" - total += checkMesh( "test_catmark_cube_corner0", simpleHbr(catmark_cube_corner0, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_cube_corner0", simpleHbr(catmark_cube_corner0, kCatmark, 0), levels ); #endif #ifdef test_catmark_cube_corner1 #include "../shapes/catmark_cube_corner1.h" - total += checkMesh( "test_catmark_cube_corner1", simpleHbr(catmark_cube_corner1, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_cube_corner1", simpleHbr(catmark_cube_corner1, kCatmark, 0), levels ); #endif #ifdef test_catmark_cube_corner2 #include "../shapes/catmark_cube_corner2.h" - total += checkMesh( "test_catmark_cube_corner2", simpleHbr(catmark_cube_corner2, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_cube_corner2", simpleHbr(catmark_cube_corner2, kCatmark, 0), levels ); #endif #ifdef test_catmark_cube_corner3 #include "../shapes/catmark_cube_corner3.h" - total += checkMesh( "test_catmark_cube_corner3", simpleHbr(catmark_cube_corner3, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_cube_corner3", simpleHbr(catmark_cube_corner3, kCatmark, 0), levels ); #endif #ifdef test_catmark_cube_corner4 #include "../shapes/catmark_cube_corner4.h" - total += checkMesh( "test_catmark_cube_corner4", simpleHbr(catmark_cube_corner4, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_cube_corner4", simpleHbr(catmark_cube_corner4, kCatmark, 0), levels ); #endif #ifdef test_catmark_dart_edgecorner #include "../shapes/catmark_dart_edgecorner.h" - total += checkMesh( "test_catmark_dart_edgecorner", simpleHbr(catmark_dart_edgecorner, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_dart_edgecorner", simpleHbr(catmark_dart_edgecorner, kCatmark, 0), levels ); #endif #ifdef test_catmark_dart_edgeonly #include "../shapes/catmark_dart_edgeonly.h" - total += checkMesh( "test_catmark_dart_edgeonly", simpleHbr(catmark_dart_edgeonly, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_dart_edgeonly", simpleHbr(catmark_dart_edgeonly, kCatmark, 0), levels ); #endif #ifdef test_catmark_tent #include "../shapes/catmark_tent.h" - total += checkMesh( "test_catmark_tent", simpleHbr(catmark_tent, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_tent", simpleHbr(catmark_tent, kCatmark, 0), levels ); #endif #ifdef test_catmark_tent_creases0 #include "../shapes/catmark_tent_creases0.h" - total += checkMesh( "test_catmark_tent_creases0", simpleHbr(catmark_tent_creases0, kCatmark, 0), levels ); + total += checkMesh( "test_catmark_tent_creases0", simpleHbr(catmark_tent_creases0, kCatmark, 0), levels ); #endif #ifdef test_catmark_tent_creases1 #include "../shapes/catmark_tent_creases1.h" - total += checkMesh( "test_catmark_tent_creases1", simpleHbr(catmark_tent_creases1, kCatmark, NULL), levels ); + total += checkMesh( "test_catmark_tent_creases1", simpleHbr(catmark_tent_creases1, kCatmark, NULL), levels ); +#endif + +#ifdef test_catmark_square_hedit0 +#include "../shapes/catmark_square_hedit0.h" + total += checkMesh( "test_catmark_square_hedit0", simpleHbr(catmark_square_hedit0, kCatmark, 0), levels ); +#endif + +#ifdef test_catmark_square_hedit1 +#include "../shapes/catmark_square_hedit1.h" + total += checkMesh( "test_catmark_square_hedit1", simpleHbr(catmark_square_hedit1, kCatmark, 0), levels ); +#endif + +#ifdef test_catmark_square_hedit2 +#include "../shapes/catmark_square_hedit2.h" + total += checkMesh( "test_catmark_square_hedit2", simpleHbr(catmark_square_hedit2, kCatmark, 0), levels ); +#endif + +#ifdef test_catmark_square_hedit3 +#include "../shapes/catmark_square_hedit3.h" + total += checkMesh( "test_catmark_square_hedit3", simpleHbr(catmark_square_hedit3, kCatmark, 0), levels ); #endif #ifdef test_loop_triangle_edgeonly #include "../shapes/loop_triangle_edgeonly.h" - total += checkMesh( "test_loop_triangle_edgeonly", simpleHbr(loop_triangle_edgeonly, kLoop, 0), levels, kLoop ); + total += checkMesh( "test_loop_triangle_edgeonly", simpleHbr(loop_triangle_edgeonly, kLoop, 0), levels, kLoop ); #endif #ifdef test_loop_triangle_edgecorner #include "../shapes/loop_triangle_edgecorner.h" - total += checkMesh( "test_loop_triangle_edgecorner", simpleHbr(loop_triangle_edgecorner, kLoop, 0), levels, kLoop ); + total += checkMesh( "test_loop_triangle_edgecorner", simpleHbr(loop_triangle_edgecorner, kLoop, 0), levels, kLoop ); #endif #ifdef test_loop_saddle_edgeonly #include "../shapes/loop_saddle_edgeonly.h" - total += checkMesh( "test_loop_saddle_edgeonly", simpleHbr(loop_saddle_edgeonly, kLoop, 0), levels, kLoop ); + total += checkMesh( "test_loop_saddle_edgeonly", simpleHbr(loop_saddle_edgeonly, kLoop, 0), levels, kLoop ); #endif #ifdef test_loop_saddle_edgecorner #include "../shapes/loop_saddle_edgecorner.h" - total += checkMesh( "test_loop_saddle_edgecorner", simpleHbr(loop_saddle_edgecorner, kLoop, 0), levels, kLoop ); + total += checkMesh( "test_loop_saddle_edgecorner", simpleHbr(loop_saddle_edgecorner, kLoop, 0), levels, kLoop ); #endif #ifdef test_loop_icosahedron #include "../shapes/loop_icosahedron.h" - total += checkMesh( "test_loop_icosahedron", simpleHbr(loop_icosahedron, kLoop, 0), levels, kLoop ); + total += checkMesh( "test_loop_icosahedron", simpleHbr(loop_icosahedron, kLoop, 0), levels, kLoop ); #endif #ifdef test_loop_cube #include "../shapes/loop_cube.h" - total += checkMesh( "test_loop_cube", simpleHbr(loop_cube, kLoop, 0), levels, kLoop ); + total += checkMesh( "test_loop_cube", simpleHbr(loop_cube, kLoop, 0), levels, kLoop ); #endif #ifdef test_loop_cube_creases0 #include "../shapes/loop_cube_creases0.h" - total += checkMesh( "test_loop_cube_creases0", simpleHbr(loop_cube_creases0, kLoop, 0), levels, kLoop ); + total += checkMesh( "test_loop_cube_creases0", simpleHbr(loop_cube_creases0, kLoop, 0), levels, kLoop ); #endif #ifdef test_loop_cube_creases1 #include "../shapes/loop_cube_creases1.h" - total += checkMesh( "test_loop_cube_creases1", simpleHbr(loop_cube_creases1, kLoop, 0), levels, kLoop ); + total += checkMesh( "test_loop_cube_creases1", simpleHbr(loop_cube_creases1, kLoop, 0), levels, kLoop ); #endif #ifdef test_bilinear_cube #include "../shapes/bilinear_cube.h" - total += checkMesh( "test_bilinear_cube", simpleHbr(bilinear_cube, kBilinear, 0), levels, kBilinear ); + total += checkMesh( "test_bilinear_cube", simpleHbr(bilinear_cube, kBilinear, 0), levels, kBilinear ); #endif diff --git a/regression/osd_regression/CMakeLists.txt b/regression/osd_regression/CMakeLists.txt index 5ad6bf1e..d19c5259 100644 --- a/regression/osd_regression/CMakeLists.txt +++ b/regression/osd_regression/CMakeLists.txt @@ -55,21 +55,11 @@ # a particular purpose and non-infringement. # -find_package(IlmBase REQUIRED) - -if(NOT ILMBASE_FOUND) - message(WARNING - "IlmBase could not be found, so the OpenSubdiv osd library regression " - "will not be available. If you do have IlmBase installed and see this " - "message, please add your IlmBase path to cmake/FindIlmBase.cmake or set it " - "in the ILMBASE_LOCATION environment variable." - ) - return() -endif() - include_directories( ${ILMBASE_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/opensubdiv + ${GLEW_INCLUDE_DIR} + ${GLUT_INCLUDE_DIR} ) set(SOURCE_FILES @@ -83,6 +73,8 @@ add_executable(osd_regression target_link_libraries(osd_regression ${OSD_LINK_TARGET} - ${ILMBASE_LIBS_DIRECTORY} + ${ILMBASE_LIBRARIES} + ${OPENGL_LIBRARY} + ${GLEW_LIBRARY} + ${GLUT_LIBRARIES} ) - diff --git a/regression/osd_regression/main.cpp b/regression/osd_regression/main.cpp index b96a15da..d691fc13 100644 --- a/regression/osd_regression/main.cpp +++ b/regression/osd_regression/main.cpp @@ -54,6 +54,15 @@ // exclude the implied warranties of merchantability, fitness for // a particular purpose and non-infringement. // + +#if defined(__APPLE__) + #include +#else + #include + #include + #include +#endif + #include #include @@ -88,7 +97,7 @@ // - precision is currently held at 1e-6 // // - results cannot be bitwise identical as some vertex interpolations -// are not happening in the same order. +// are not happening in the same order. // // - only vertex interpolation is being tested at the moment. // @@ -108,17 +117,19 @@ struct xyzVV { void AddVaryingWithWeight(const xyzVV& , float, void * =0 ) { } void Clear( void * =0 ) { _pos.setValue(0.f, 0.f, 0.f); } void SetPosition(float x, float y, float z) { _pos=Imath::Vec3(x,y,z); } + void ApplyVertexEdit(const OpenSubdiv::HbrVertexEdit &) { } + void ApplyMovingVertexEdit(const OpenSubdiv::HbrMovingVertexEdit &) { } const Imath::Vec3& GetPos() const { return _pos; } -private: +private: Imath::Vec3 _pos; }; //------------------------------------------------------------------------------ class xyzFV; typedef OpenSubdiv::HbrMesh xyzmesh; -typedef OpenSubdiv::HbrFace xyzface; -typedef OpenSubdiv::HbrVertex xyzvertex; +typedef OpenSubdiv::HbrFace xyzface; +typedef OpenSubdiv::HbrVertex xyzvertex; typedef OpenSubdiv::HbrHalfedge xyzhalfedge; typedef OpenSubdiv::HbrFaceOperator xyzFaceOperator; typedef OpenSubdiv::HbrVertexOperator xyzVertexOperator; @@ -126,20 +137,20 @@ typedef OpenSubdiv::HbrVertexOperator xyzVertexOperator; //------------------------------------------------------------------------------ // Returns true if a vertex or any of its parents is on a boundary bool VertexOnBoundary( xyzvertex const * v ) { - - if (not v) + + if (not v) return false; - + if (v->OnBoundary()) return true; - + xyzvertex const * pv = v->GetParentVertex(); if (pv) return VertexOnBoundary(pv); else { xyzhalfedge const * pe = v->GetParentEdge(); if (pe) { - return VertexOnBoundary(pe->GetOrgVertex()) or + return VertexOnBoundary(pe->GetOrgVertex()) or VertexOnBoundary(pe->GetDestVertex()); } else { xyzface const * pf = v->GetParentFace(), * rootf = pf; @@ -159,18 +170,18 @@ bool VertexOnBoundary( xyzvertex const * v ) { //------------------------------------------------------------------------------ -int checkVertexBuffer( xyzmesh * hmesh, - OpenSubdiv::OsdCpuVertexBuffer * vb, +int checkVertexBuffer( xyzmesh * hmesh, + OpenSubdiv::OsdCpuVertexBuffer * vb, std::vector const & remap) { - int count=0; - Imath::Vec3 deltaAvg(0.0, 0.0, 0.0); + int count=0; + Imath::Vec3 deltaAvg(0.0, 0.0, 0.0); Imath::Vec3 deltaCnt(0,0,0); - + int nverts = hmesh->GetNumVertices(); for (int i=0; iGetVertex(i); - + float * ov = & vb->GetCpuBuffer()[ remap[ hv->GetID() ] * vb->GetNumElements() ]; // boundary interpolation rules set to "none" produce "undefined" vertices on @@ -180,11 +191,11 @@ int checkVertexBuffer( xyzmesh * hmesh, continue; - if ( hv->GetData().GetPos()[0] != ov[0] ) + if ( hv->GetData().GetPos()[0] != ov[0] ) deltaCnt[0]++; - if ( hv->GetData().GetPos()[1] != ov[1] ) + if ( hv->GetData().GetPos()[1] != ov[1] ) deltaCnt[1]++; - if ( hv->GetData().GetPos()[2] != ov[2] ) + if ( hv->GetData().GetPos()[2] != ov[2] ) deltaCnt[2]++; Imath::Vec3 delta = hv->GetData().GetPos() - Imath::Vec3(ov[0],ov[1],ov[2]); @@ -200,7 +211,7 @@ int checkVertexBuffer( xyzmesh * hmesh, ov[0], ov[1], ov[2] ); - count++; + count++; } } @@ -211,15 +222,15 @@ int checkVertexBuffer( xyzmesh * hmesh, if (deltaCnt[2]) deltaAvg[2]/=deltaCnt[2]; - printf(" delta ratio : (%d/%d %d/%d %d/%d)\n", (int)deltaCnt.x, nverts, - (int)deltaCnt.y, nverts, + printf(" delta ratio : (%d/%d %d/%d %d/%d)\n", (int)deltaCnt.x, nverts, + (int)deltaCnt.y, nverts, (int)deltaCnt.x, nverts ); - printf(" average delta : (%.10f %.10f %.10f)\n", deltaAvg.x, + printf(" average delta : (%.10f %.10f %.10f)\n", deltaAvg.x, deltaAvg.y, deltaAvg.z ); if (count==0) printf(" success !\n"); - + return count; } @@ -243,14 +254,14 @@ int checkMesh( char const * msg, char const * shape, int levels, Scheme scheme=k int result =0; printf("- %s (scheme=%d)\n", msg, scheme); - + xyzmesh * refmesh = simpleHbr(shape, scheme, 0); - + refine( refmesh, levels ); std::vector coarseverts; - + OpenSubdiv::OsdHbrMesh * hmesh = simpleHbr(shape, scheme, coarseverts); OpenSubdiv::OsdMesh * omesh = new OpenSubdiv::OsdMesh(); @@ -258,29 +269,34 @@ int checkMesh( char const * msg, char const * shape, int levels, Scheme scheme=k std::vector remap; - { + { omesh->Create(hmesh, levels, (int)OpenSubdiv::OsdKernelDispatcher::kCPU, &remap); - - OpenSubdiv::OsdCpuVertexBuffer * vb = + + OpenSubdiv::OsdCpuVertexBuffer * vb = dynamic_cast(omesh->InitializeVertexBuffer(3)); - - vb->UpdateData( & coarseverts[0], (int)coarseverts.size() ); - + + vb->UpdateData( & coarseverts[0], (int)coarseverts.size()/3 ); + omesh->Subdivide( vb, NULL ); - + omesh->Synchronize(); - - checkVertexBuffer(refmesh, vb, remap); + + checkVertexBuffer(refmesh, vb, remap); } - + delete hmesh; - + return result; } //------------------------------------------------------------------------------ int main(int argc, char ** argv) { + // Make sure we have an OpenGL context. + glutInit(&argc, argv); + glutCreateWindow("osd_regression"); + glewInit(); + int levels=5, total=0; // Register Osd compute kernels @@ -318,141 +334,141 @@ int main(int argc, char ** argv) { #ifdef test_catmark_edgeonly #include "../shapes/catmark_edgeonly.h" - total += checkMesh( "test_catmark_edgeonly", catmark_edgeonly, levels, kCatmark ); + total += checkMesh( "test_catmark_edgeonly", catmark_edgeonly, levels, kCatmark ); #endif #ifdef test_catmark_edgecorner #include "../shapes/catmark_edgecorner.h" - total += checkMesh( "test_catmark_edgeonly", catmark_edgecorner, levels, kCatmark ); + total += checkMesh( "test_catmark_edgeonly", catmark_edgecorner, levels, kCatmark ); #endif #ifdef test_catmark_pyramid #include "../shapes/catmark_pyramid.h" - total += checkMesh( "test_catmark_pyramid", catmark_pyramid, levels, kCatmark ); + total += checkMesh( "test_catmark_pyramid", catmark_pyramid, levels, kCatmark ); #endif #ifdef test_catmark_pyramid_creases0 #include "../shapes/catmark_pyramid_creases0.h" - total += checkMesh( "test_catmark_pyramid_creases0", catmark_pyramid_creases0, levels, kCatmark ); + total += checkMesh( "test_catmark_pyramid_creases0", catmark_pyramid_creases0, levels, kCatmark ); #endif #ifdef test_catmark_pyramid_creases1 #include "../shapes/catmark_pyramid_creases1.h" - total += checkMesh( "test_catmark_pyramid_creases1", catmark_pyramid_creases1, levels, kCatmark ); + total += checkMesh( "test_catmark_pyramid_creases1", catmark_pyramid_creases1, levels, kCatmark ); #endif #ifdef test_catmark_cube #include "../shapes/catmark_cube.h" - total += checkMesh( "test_catmark_cube", catmark_cube, levels, kCatmark ); + total += checkMesh( "test_catmark_cube", catmark_cube, levels, kCatmark ); #endif #ifdef test_catmark_cube_creases0 #include "../shapes/catmark_cube_creases0.h" - total += checkMesh( "test_catmark_cube_creases0", catmark_cube_creases0, levels, kCatmark ); + total += checkMesh( "test_catmark_cube_creases0", catmark_cube_creases0, levels, kCatmark ); #endif #ifdef test_catmark_cube_creases1 #include "../shapes/catmark_cube_creases1.h" - total += checkMesh( "test_catmark_cube_creases1", catmark_cube_creases1, levels, kCatmark ); + total += checkMesh( "test_catmark_cube_creases1", catmark_cube_creases1, levels, kCatmark ); #endif #ifdef test_catmark_cube_corner0 #include "../shapes/catmark_cube_corner0.h" - total += checkMesh( "test_catmark_cube_corner0", catmark_cube_corner0, levels, kCatmark ); + total += checkMesh( "test_catmark_cube_corner0", catmark_cube_corner0, levels, kCatmark ); #endif #ifdef test_catmark_cube_corner1 #include "../shapes/catmark_cube_corner1.h" - total += checkMesh( "test_catmark_cube_corner1", catmark_cube_corner1, levels, kCatmark ); + total += checkMesh( "test_catmark_cube_corner1", catmark_cube_corner1, levels, kCatmark ); #endif #ifdef test_catmark_cube_corner2 #include "../shapes/catmark_cube_corner2.h" - total += checkMesh( "test_catmark_cube_corner2", catmark_cube_corner2, levels, kCatmark ); + total += checkMesh( "test_catmark_cube_corner2", catmark_cube_corner2, levels, kCatmark ); #endif #ifdef test_catmark_cube_corner3 #include "../shapes/catmark_cube_corner3.h" - total += checkMesh( "test_catmark_cube_corner3", catmark_cube_corner3, levels, kCatmark ); + total += checkMesh( "test_catmark_cube_corner3", catmark_cube_corner3, levels, kCatmark ); #endif #ifdef test_catmark_cube_corner4 #include "../shapes/catmark_cube_corner4.h" - total += checkMesh( "test_catmark_cube_corner4", catmark_cube_corner4, levels, kCatmark ); + total += checkMesh( "test_catmark_cube_corner4", catmark_cube_corner4, levels, kCatmark ); #endif #ifdef test_catmark_dart_edgecorner #include "../shapes/catmark_dart_edgecorner.h" - total += checkMesh( "test_catmark_dart_edgecorner", catmark_dart_edgecorner, levels, kCatmark ); + total += checkMesh( "test_catmark_dart_edgecorner", catmark_dart_edgecorner, levels, kCatmark ); #endif #ifdef test_catmark_dart_edgeonly #include "../shapes/catmark_dart_edgeonly.h" - total += checkMesh( "test_catmark_dart_edgeonly", catmark_dart_edgeonly, levels, kCatmark ); + total += checkMesh( "test_catmark_dart_edgeonly", catmark_dart_edgeonly, levels, kCatmark ); #endif #ifdef test_catmark_tent #include "../shapes/catmark_tent.h" - total += checkMesh( "test_catmark_tent", catmark_tent, levels, kCatmark ); + total += checkMesh( "test_catmark_tent", catmark_tent, levels, kCatmark ); #endif #ifdef test_catmark_tent_creases0 #include "../shapes/catmark_tent_creases0.h" - total += checkMesh( "test_catmark_tent_creases0", catmark_tent_creases0, levels ); + total += checkMesh( "test_catmark_tent_creases0", catmark_tent_creases0, levels ); #endif #ifdef test_catmark_tent_creases1 #include "../shapes/catmark_tent_creases1.h" - total += checkMesh( "test_catmark_tent_creases1", catmark_tent_creases1, levels ); + total += checkMesh( "test_catmark_tent_creases1", catmark_tent_creases1, levels ); #endif #ifdef test_loop_triangle_edgeonly #include "../shapes/loop_triangle_edgeonly.h" - total += checkMesh( "test_loop_triangle_edgeonly", loop_triangle_edgeonly, levels, kLoop ); + total += checkMesh( "test_loop_triangle_edgeonly", loop_triangle_edgeonly, levels, kLoop ); #endif #ifdef test_loop_triangle_edgecorner #include "../shapes/loop_triangle_edgecorner.h" - total += checkMesh( "test_loop_triangle_edgecorner", loop_triangle_edgecorner, levels, kLoop ); + total += checkMesh( "test_loop_triangle_edgecorner", loop_triangle_edgecorner, levels, kLoop ); #endif #ifdef test_loop_saddle_edgeonly #include "../shapes/loop_saddle_edgeonly.h" - total += checkMesh( "test_loop_saddle_edgeonly", loop_saddle_edgeonly, levels, kLoop ); + total += checkMesh( "test_loop_saddle_edgeonly", loop_saddle_edgeonly, levels, kLoop ); #endif #ifdef test_loop_saddle_edgecorner #include "../shapes/loop_saddle_edgecorner.h" - total += checkMesh( "test_loop_saddle_edgecorner", loop_saddle_edgecorner, levels, kLoop ); + total += checkMesh( "test_loop_saddle_edgecorner", loop_saddle_edgecorner, levels, kLoop ); #endif #ifdef test_loop_icosahedron #include "../shapes/loop_icosahedron.h" - total += checkMesh( "test_loop_icosahedron", loop_icosahedron, levels, kLoop ); + total += checkMesh( "test_loop_icosahedron", loop_icosahedron, levels, kLoop ); #endif #ifdef test_loop_cube #include "../shapes/loop_cube.h" - total += checkMesh( "test_loop_cube", loop_cube, levels, kLoop ); + total += checkMesh( "test_loop_cube", loop_cube, levels, kLoop ); #endif #ifdef test_loop_cube_creases0 #include "../shapes/loop_cube_creases0.h" - total += checkMesh( "test_loop_cube_creases0", loop_cube_creases0,levels, kLoop ); + total += checkMesh( "test_loop_cube_creases0", loop_cube_creases0,levels, kLoop ); #endif #ifdef test_loop_cube_creases1 #include "../shapes/loop_cube_creases1.h" - total += checkMesh( "test_loop_cube_creases1", loop_cube_creases1, levels, kLoop ); + total += checkMesh( "test_loop_cube_creases1", loop_cube_creases1, levels, kLoop ); #endif #ifdef test_bilinear_cube #include "../shapes/bilinear_cube.h" - total += checkMesh( "test_bilinear_cube", bilinear_cube, levels, kBilinear ); + total += checkMesh( "test_bilinear_cube", bilinear_cube, levels, kBilinear ); #endif if (total==0) diff --git a/regression/shapes/catmark_square_hedit0.h b/regression/shapes/catmark_square_hedit0.h new file mode 100644 index 00000000..3553d4d6 --- /dev/null +++ b/regression/shapes/catmark_square_hedit0.h @@ -0,0 +1,92 @@ +// +// Copyright (C) Pixar. All rights reserved. +// +// This license governs use of the accompanying software. If you +// use the software, you accept this license. If you do not accept +// the license, do not use the software. +// +// 1. Definitions +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. +// copyright law. A "contribution" is the original software, or +// any additions or changes to the software. +// A "contributor" is any person or entity that distributes its +// contribution under this license. +// "Licensed patents" are a contributor's patent claims that read +// directly on its contribution. +// +// 2. Grant of Rights +// (A) Copyright Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free copyright license to reproduce its contribution, +// prepare derivative works of its contribution, and distribute +// its contribution or any derivative works that you create. +// (B) Patent Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free license under its licensed patents to make, have +// made, use, sell, offer for sale, import, and/or otherwise +// dispose of its contribution in the software or derivative works +// of the contribution in the software. +// +// 3. Conditions and Limitations +// (A) No Trademark License- This license does not grant you +// rights to use any contributor's name, logo, or trademarks. +// (B) If you bring a patent claim against any contributor over +// patents that you claim are infringed by the software, your +// patent license from such contributor to the software ends +// automatically. +// (C) If you distribute any portion of the software, you must +// retain all copyright, patent, trademark, and attribution +// notices that are present in the software. +// (D) If you distribute any portion of the software in source +// code form, you may do so only under this license by including a +// complete copy of this license with your distribution. If you +// distribute any portion of the software in compiled or object +// code form, you may only do so under a license that complies +// with this license. +// (E) The software is licensed "as-is." You bear the risk of +// using it. The contributors give no express warranties, +// guarantees or conditions. You may have additional consumer +// rights under your local laws which this license cannot change. +// To the extent permitted under your local laws, the contributors +// exclude the implied warranties of merchantability, fitness for +// a particular purpose and non-infringement. +// +static char const * catmark_square_hedit0 = +"# This file uses centimeters as units for non-parametric coordinates.\n" +"\n" +"v -1 -1 0\n" +"v -0.333333 -1 0\n" +"v 0.333333 -1 0\n" +"v 1 -1 0\n" +"v -1 -0.333333 0\n" +"v -0.333333 -0.333333 0\n" +"v 0.333333 -0.333333 0\n" +"v 1 -0.333333 0\n" +"v -1 0.333333 0\n" +"v -0.333333 0.333333 0\n" +"v 0.333333 0.333333 0\n" +"v 1 0.333333 0\n" +"v -1 1 0\n" +"v -0.333333 1 0\n" +"v 0.333333 1 0\n" +"v 1 1 0\n" +"vt 0.0 0.0\n" +"vn 0.0 0.0 0.0\n" +"s off\n" +"f 1/1/1 2/1/1 6/1/1 5/1/1\n" +"f 2/1/1 3/1/1 7/1/1 6/1/1\n" +"f 3/1/1 4/1/1 8/1/1 7/1/1\n" +"f 5/1/1 6/1/1 10/1/1 9/1/1\n" +"f 6/1/1 7/1/1 11/1/1 10/1/1\n" +"f 7/1/1 8/1/1 12/1/1 11/1/1\n" +"f 9/1/1 10/1/1 14/1/1 13/1/1\n" +"f 10/1/1 11/1/1 15/1/1 14/1/1\n" +"f 11/1/1 12/1/1 16/1/1 15/1/1\n" +"t interpolateboundary 1/0/0 2\n" +"t vertexedit 16/24/3 3 0 1 0 3 0 1 1 3 0 1 2 3 0 1 3 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 add P value\n" +"t vertexedit 20/24/3 4 4 1 1 0 4 4 1 1 1 4 4 1 1 2 4 4 1 1 3 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 add P value\n" +"t vertexedit 24/24/3 5 8 0 1 1 0 5 8 0 1 1 1 5 8 0 1 1 2 5 8 0 1 1 3 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 add P value\n" +; diff --git a/regression/shapes/catmark_square_hedit1.h b/regression/shapes/catmark_square_hedit1.h new file mode 100644 index 00000000..11305de7 --- /dev/null +++ b/regression/shapes/catmark_square_hedit1.h @@ -0,0 +1,91 @@ +// +// Copyright (C) Pixar. All rights reserved. +// +// This license governs use of the accompanying software. If you +// use the software, you accept this license. If you do not accept +// the license, do not use the software. +// +// 1. Definitions +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. +// copyright law. A "contribution" is the original software, or +// any additions or changes to the software. +// A "contributor" is any person or entity that distributes its +// contribution under this license. +// "Licensed patents" are a contributor's patent claims that read +// directly on its contribution. +// +// 2. Grant of Rights +// (A) Copyright Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free copyright license to reproduce its contribution, +// prepare derivative works of its contribution, and distribute +// its contribution or any derivative works that you create. +// (B) Patent Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free license under its licensed patents to make, have +// made, use, sell, offer for sale, import, and/or otherwise +// dispose of its contribution in the software or derivative works +// of the contribution in the software. +// +// 3. Conditions and Limitations +// (A) No Trademark License- This license does not grant you +// rights to use any contributor's name, logo, or trademarks. +// (B) If you bring a patent claim against any contributor over +// patents that you claim are infringed by the software, your +// patent license from such contributor to the software ends +// automatically. +// (C) If you distribute any portion of the software, you must +// retain all copyright, patent, trademark, and attribution +// notices that are present in the software. +// (D) If you distribute any portion of the software in source +// code form, you may do so only under this license by including a +// complete copy of this license with your distribution. If you +// distribute any portion of the software in compiled or object +// code form, you may only do so under a license that complies +// with this license. +// (E) The software is licensed "as-is." You bear the risk of +// using it. The contributors give no express warranties, +// guarantees or conditions. You may have additional consumer +// rights under your local laws which this license cannot change. +// To the extent permitted under your local laws, the contributors +// exclude the implied warranties of merchantability, fitness for +// a particular purpose and non-infringement. +// +static char const * catmark_square_hedit1 = +"# This file uses centimeters as units for non-parametric coordinates.\n" +"\n" +"v -1 -1 0\n" +"v -0.333333 -1 0\n" +"v 0.333333 -1 0\n" +"v 1 -1 0\n" +"v -1 -0.333333 0\n" +"v -0.333333 -0.333333 0\n" +"v 0.333333 -0.333333 0\n" +"v 1 -0.333333 0\n" +"v -1 0.333333 0\n" +"v -0.333333 0.333333 0\n" +"v 0.333333 0.333333 0\n" +"v 1 0.333333 0\n" +"v -1 1 0\n" +"v -0.333333 1 0\n" +"v 0.333333 1 0\n" +"v 1 1 0\n" +"vt 0.0 0.0\n" +"vn 0.0 0.0 0.0\n" +"s off\n" +"f 1/1/1 2/1/1 6/1/1 5/1/1\n" +"f 2/1/1 3/1/1 7/1/1 6/1/1\n" +"f 3/1/1 4/1/1 8/1/1 7/1/1\n" +"f 5/1/1 6/1/1 10/1/1 9/1/1\n" +"f 6/1/1 7/1/1 11/1/1 10/1/1\n" +"f 7/1/1 8/1/1 12/1/1 11/1/1\n" +"f 9/1/1 10/1/1 14/1/1 13/1/1\n" +"f 10/1/1 11/1/1 15/1/1 14/1/1\n" +"f 11/1/1 12/1/1 16/1/1 15/1/1\n" +"t interpolateboundary 1/0/0 2\n" +"t vertexedit 16/24/3 3 4 1 0 3 4 1 1 3 4 1 2 3 4 1 3 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 add P value\n" +"t vertexedit 16/4/3 3 4 1 0 3 4 1 1 3 4 1 2 3 4 1 3 10 10 10 10 set P sharpness\n" +; diff --git a/regression/shapes/catmark_square_hedit2.h b/regression/shapes/catmark_square_hedit2.h new file mode 100644 index 00000000..3a4ed16b --- /dev/null +++ b/regression/shapes/catmark_square_hedit2.h @@ -0,0 +1,91 @@ +// +// Copyright (C) Pixar. All rights reserved. +// +// This license governs use of the accompanying software. If you +// use the software, you accept this license. If you do not accept +// the license, do not use the software. +// +// 1. Definitions +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. +// copyright law. A "contribution" is the original software, or +// any additions or changes to the software. +// A "contributor" is any person or entity that distributes its +// contribution under this license. +// "Licensed patents" are a contributor's patent claims that read +// directly on its contribution. +// +// 2. Grant of Rights +// (A) Copyright Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free copyright license to reproduce its contribution, +// prepare derivative works of its contribution, and distribute +// its contribution or any derivative works that you create. +// (B) Patent Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free license under its licensed patents to make, have +// made, use, sell, offer for sale, import, and/or otherwise +// dispose of its contribution in the software or derivative works +// of the contribution in the software. +// +// 3. Conditions and Limitations +// (A) No Trademark License- This license does not grant you +// rights to use any contributor's name, logo, or trademarks. +// (B) If you bring a patent claim against any contributor over +// patents that you claim are infringed by the software, your +// patent license from such contributor to the software ends +// automatically. +// (C) If you distribute any portion of the software, you must +// retain all copyright, patent, trademark, and attribution +// notices that are present in the software. +// (D) If you distribute any portion of the software in source +// code form, you may do so only under this license by including a +// complete copy of this license with your distribution. If you +// distribute any portion of the software in compiled or object +// code form, you may only do so under a license that complies +// with this license. +// (E) The software is licensed "as-is." You bear the risk of +// using it. The contributors give no express warranties, +// guarantees or conditions. You may have additional consumer +// rights under your local laws which this license cannot change. +// To the extent permitted under your local laws, the contributors +// exclude the implied warranties of merchantability, fitness for +// a particular purpose and non-infringement. +// +static char const * catmark_square_hedit2 = +"# This file uses centimeters as units for non-parametric coordinates.\n" +"\n" +"v -1 -1 0\n" +"v -0.333333 -1 0\n" +"v 0.333333 -1 0\n" +"v 1 -1 0\n" +"v -1 -0.333333 0\n" +"v -0.333333 -0.333333 0\n" +"v 0.333333 -0.333333 0\n" +"v 1 -0.333333 0\n" +"v -1 0.333333 0\n" +"v -0.333333 0.333333 0\n" +"v 0.333333 0.333333 0\n" +"v 1 0.333333 0\n" +"v -1 1 0\n" +"v -0.333333 1 0\n" +"v 0.333333 1 0\n" +"v 1 1 0\n" +"vt 0.0 0.0\n" +"vn 0.0 0.0 0.0\n" +"s off\n" +"f 1/1/1 2/1/1 6/1/1 5/1/1\n" +"f 2/1/1 3/1/1 7/1/1 6/1/1\n" +"f 3/1/1 4/1/1 8/1/1 7/1/1\n" +"f 5/1/1 6/1/1 10/1/1 9/1/1\n" +"f 6/1/1 7/1/1 11/1/1 10/1/1\n" +"f 7/1/1 8/1/1 12/1/1 11/1/1\n" +"f 9/1/1 10/1/1 14/1/1 13/1/1\n" +"f 10/1/1 11/1/1 15/1/1 14/1/1\n" +"f 11/1/1 12/1/1 16/1/1 15/1/1\n" +"t interpolateboundary 1/0/0 2\n" +"t vertexedit 16/24/3 3 4 1 0 3 4 1 1 3 4 1 2 3 4 1 3 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 add P value\n" +"t edgeedit 16/4/3 3 4 1 0 3 4 1 1 3 4 1 2 3 4 1 3 10 10 10 10 set P sharpness\n" +; diff --git a/regression/shapes/catmark_square_hedit3.h b/regression/shapes/catmark_square_hedit3.h new file mode 100644 index 00000000..42fbc706 --- /dev/null +++ b/regression/shapes/catmark_square_hedit3.h @@ -0,0 +1,92 @@ +// +// Copyright (C) Pixar. All rights reserved. +// +// This license governs use of the accompanying software. If you +// use the software, you accept this license. If you do not accept +// the license, do not use the software. +// +// 1. Definitions +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. +// copyright law. A "contribution" is the original software, or +// any additions or changes to the software. +// A "contributor" is any person or entity that distributes its +// contribution under this license. +// "Licensed patents" are a contributor's patent claims that read +// directly on its contribution. +// +// 2. Grant of Rights +// (A) Copyright Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free copyright license to reproduce its contribution, +// prepare derivative works of its contribution, and distribute +// its contribution or any derivative works that you create. +// (B) Patent Grant- Subject to the terms of this license, +// including the license conditions and limitations in section 3, +// each contributor grants you a non-exclusive, worldwide, +// royalty-free license under its licensed patents to make, have +// made, use, sell, offer for sale, import, and/or otherwise +// dispose of its contribution in the software or derivative works +// of the contribution in the software. +// +// 3. Conditions and Limitations +// (A) No Trademark License- This license does not grant you +// rights to use any contributor's name, logo, or trademarks. +// (B) If you bring a patent claim against any contributor over +// patents that you claim are infringed by the software, your +// patent license from such contributor to the software ends +// automatically. +// (C) If you distribute any portion of the software, you must +// retain all copyright, patent, trademark, and attribution +// notices that are present in the software. +// (D) If you distribute any portion of the software in source +// code form, you may do so only under this license by including a +// complete copy of this license with your distribution. If you +// distribute any portion of the software in compiled or object +// code form, you may only do so under a license that complies +// with this license. +// (E) The software is licensed "as-is." You bear the risk of +// using it. The contributors give no express warranties, +// guarantees or conditions. You may have additional consumer +// rights under your local laws which this license cannot change. +// To the extent permitted under your local laws, the contributors +// exclude the implied warranties of merchantability, fitness for +// a particular purpose and non-infringement. +// +static char const * catmark_square_hedit3 = +"# This file uses centimeters as units for non-parametric coordinates.\n" +"\n" +"v -1 -1 0\n" +"v -0.333333 -1 0\n" +"v 0.333333 -1 0\n" +"v 1 -1 0\n" +"v -1 -0.333333 0\n" +"v -0.333333 -0.333333 0\n" +"v 0.333333 -0.333333 0\n" +"v 1 -0.333333 0\n" +"v -1 0.333333 0\n" +"v -0.333333 0.333333 0\n" +"v 0.333333 0.333333 0\n" +"v 1 0.333333 0\n" +"v -1 1 0\n" +"v -0.333333 1 0\n" +"v 0.333333 1 0\n" +"v 1 1 0\n" +"vt 0.0 0.0\n" +"vn 0.0 0.0 0.0\n" +"s off\n" +"f 1/1/1 2/1/1 6/1/1 5/1/1\n" +"f 2/1/1 3/1/1 7/1/1 6/1/1\n" +"f 3/1/1 4/1/1 8/1/1 7/1/1\n" +"f 5/1/1 6/1/1 10/1/1 9/1/1\n" +"f 6/1/1 7/1/1 11/1/1 10/1/1\n" +"f 7/1/1 8/1/1 12/1/1 11/1/1\n" +"f 9/1/1 10/1/1 14/1/1 13/1/1\n" +"f 10/1/1 11/1/1 15/1/1 14/1/1\n" +"f 11/1/1 12/1/1 16/1/1 15/1/1\n" +"t interpolateboundary 1/0/0 2\n" +"t vertexedit 16/24/3 3 4 1 0 3 4 1 1 3 4 1 2 3 4 1 3 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 add P value\n" +"t edgeedit 16/4/3 3 4 1 0 3 4 1 1 3 4 1 2 3 4 1 3 10 10 10 10 set P sharpness\n" +"t vertexedit 16/4/3 3 4 1 0 3 4 1 1 3 4 1 2 3 4 1 3 10 10 10 10 set P sharpness\n" +;