Embed OpenSubdiv code to TinyUSDZ.

This commit is contained in:
Syoyo Fujita
2020-04-20 18:09:59 +09:00
parent a0ada52ddf
commit 5fdb1e1295
216 changed files with 86416 additions and 8 deletions

View File

@@ -10,6 +10,9 @@ option(TINYUSDZ_USE_CCACHE "Use ccache for faster recompile." ON)
option(TINYUSDZ_BUILD_TESTS "Build tests" ON)
option(TINYUSDZ_BUILD_EXAMPLES "Build examples(but not all examples area built in `examples` folder)" ON)
option(TINYUSDZ_WITH_OPENSUBDIV "Build with OpenSubdiv(osdCPU. Set `osd_DIR` to specify the path to OpenSubdiv)" OFF)
# Use embedded version of OpenSubdiv code by default
set(osd_DIR ${PROJECT_SOURCE_DIR}/src/osd)
option(TINYUSDZ_WITH_AUDIO "Build with Audio support(WAV and MP3)" ON)
option(TINYUSDZ_WITH_EXR "Build with EXR HDR texture support" ON)

View File

@@ -34,7 +34,10 @@ TinyUSDZ is currently in alpha stage. Not usable.
## Requirements
* C++11
* C++11 compiler
* [x] gcc 4.8.5(CentOS 7 default) or later
* [x] Visual Studio 2017 or later(2015 may OK)
* [x] clang 3.8 or later
## USDZ file format
@@ -67,14 +70,10 @@ $ make
* `TINYUSDZ_BUILD_TESTS` : Build tests
* `TINYUSDZ_BUILD_EXAMPLES` : Build examples(note that not all examples in `examples` folder are built)
* `TINYUSDZ_WITH_OPENSUBDIV` : Use OpenSubviv to tessellate subdivision surface.
* `osd_DIR` to specify the path to OpenSubdiv repo(I recommend to use https://github.com/syoyo/OpenSubdiv-aarch64 )
* OpenSubdiv code is included in TinyUSDZ repo. If you want to use external OpenSubdiv repo, specity the path to OpenSubdiv using `osd_DIR` cmake environment variable.
* `TINYUSDZ_WITH_AUDIO` : Support loading audio(mp3 and wav).
* `TINYUSDZ_WITH_EXR` : Support loading EXR format HDR texture through TinyEXR.
#### Build with OpenSubdiv(optional)
Recommended way is to run `scripts/clone_osd.sh` to clone OpenSubdiv-aarch64 repo to `deps/OpenSudiv`, then run `scripts/bootstrap-cmake-linux-with-osd.sh`.
### Meson
Meson build is provided for compile tests.
@@ -98,6 +97,8 @@ See [prim_format.md](doc/prim_format.md) and [preview_surface.md](doc/preview_su
## TODO
* [ ] Subdivision surface using OpenSubdiv.
* [ ] Replace OpenSubdiv with our own subdiv library or embree3's one.
* [ ] USDA(USD Ascii) support
* [ ] Write our own USDA parser with PEG.
* [ ] Animation
@@ -113,7 +114,6 @@ See [prim_format.md](doc/prim_format.md) and [preview_surface.md](doc/preview_su
* [ ] iOS example?
* [ ] CPU raytracer viewer
* [ ] Viewer with Vulkan API.
* [ ] Replace OpenSubdiv with our own subdiv library or embree3's one.
* [ ] Read USD data with bounded memory size. This feature is especially useful for mobile platform(e.g. in terms of security, memory consumption, etc)
* [ ] USDZ saver
* [ ] Support Nested USDZ

View File

@@ -12,6 +12,5 @@ mkdir ${builddir}
cd ${builddir} && cmake \
-DCMAKE_VERBOSE_MAKEFILE=1 \
-DTINYUSDZ_WITH_OPENSUBDIV=On \
-Dosd_DIR=${osd_path} \
..

848
src/osd/CMakeLists.txt Normal file
View File

@@ -0,0 +1,848 @@
#
# Copyright 2013 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
project(OpenSubdiv)
cmake_minimum_required(VERSION 2.8.6)
# Turn on folder support
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
#-------------------------------------------------------------------------------
# Obtain OpenSubdiv API version from version.h file
if(EXISTS "${OpenSubdiv_SOURCE_DIR}/opensubdiv/version.h")
file(STRINGS "${OpenSubdiv_SOURCE_DIR}/opensubdiv/version.h"
OpenSubdiv_VERSION REGEX "^#define OPENSUBDIV_VERSION .*$")
string(REPLACE "#define OPENSUBDIV_VERSION " "" OpenSubdiv_VERSION ${OpenSubdiv_VERSION})
else()
message(FATAL_ERROR, "Cannot locate opensubdiv/version.h in ${OpenSubdiv_SOURCE_DIR}")
endif()
# Evaluate 'soname' from OSD version
# replace '_' with '.'
string(REGEX REPLACE "(_)" "." OSD_SONAME ${OpenSubdiv_VERSION})
# remove starting 'v' character
string(REGEX REPLACE "^v" "" OSD_SONAME ${OSD_SONAME})
add_definitions(
-DOPENSUBDIV_VERSION_STRING="${OSD_SONAME}"
)
#-------------------------------------------------------------------------------
message(STATUS "Compiling ${CMAKE_PROJECT_NAME} version ${OpenSubdiv_VERSION}")
message(STATUS "Using cmake version ${CMAKE_VERSION}")
#-------------------------------------------------------------------------------
# Specify the default install path
if (NOT DEFINED CMAKE_INSTALL_PREFIX)
SET( CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/" )
endif()
if (NOT DEFINED CMAKE_INCDIR_BASE)
set( CMAKE_INCDIR_BASE include/opensubdiv )
endif()
if (NOT DEFINED CMAKE_BINDIR_BASE)
set( CMAKE_BINDIR_BASE bin )
endif()
if (NOT DEFINED CMAKE_LIBDIR_BASE)
set( CMAKE_LIBDIR_BASE lib )
endif()
if (NOT DEFINED CMAKE_FRAMEWORKDIR_BASE)
set( CMAKE_FRAMEWORKDIR_BASE Frameworks )
endif()
if (NOT DEFINED CMAKE_PLUGINDIR_BASE)
set( CMAKE_PLUGINDIR_BASE plugin )
endif()
if (NOT DEFINED CMAKE_DOCDIR_BASE)
set( CMAKE_DOCDIR_BASE share/doc/opensubdiv )
else()
if (IS_ABSOLUTE ${CMAKE_DOCDIR_BASE})
set( CMAKE_DOCDIR_BASE "${CMAKE_DOCDIR_BASE}" )
else()
set( CMAKE_DOCDIR_BASE "${CMAKE_INSTALL_PREFIX}/${CMAKE_DOCDIR_BASE}" )
endif()
endif()
# Allow install path to be overridden for cross-compile builds
if(LIBRARY_OUTPUT_PATH_ROOT)
SET( CMAKE_INSTALL_PREFIX "${LIBRARY_OUTPUT_PATH_ROOT}/" )
endif()
# Set the directory where the executables will be stored.
set(EXECUTABLE_OUTPUT_PATH
"${PROJECT_BINARY_DIR}/bin"
CACHE PATH
"Directory where executables will be stored"
)
# Set the directory where the libraries will be stored.
set(LIBRARY_OUTPUT_PATH
"${PROJECT_BINARY_DIR}/lib"
CACHE PATH
"Directory where all libraries will be stored"
)
# Specify the list of directories to search for cmake modules.
set(CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
)
#-------------------------------------------------------------------------------
# OpenSubdiv trips bugs in some older gcc versions
if (CMAKE_COMPILER_IS_GNUCC)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
message(WARNING "g++ 4.8 or newer recommended")
endif()
endif()
# Detect Clang (until a cmake version provides built-in variables)
if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
set(CMAKE_COMPILER_IS_CLANGCC 1)
elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Intel")
set(CMAKE_COMPILER_IS_ICC 1)
endif()
if (NOT CMAKE_COMPILER_IS_ICC)
# Currently icc has a bug that asserts when linking rpaths containing long
# sequences of ':' that this command causes. The consequence is that examples
# built and installed using icc will not have an rpath pointing to the built
# OSD library which they depend on and will have to set LD_LIBRARY_PATH instead.
list(APPEND CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
endif()
# add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# ensure that ARC is shown as enabled in the Xcode UI
if(CMAKE_GENERATOR STREQUAL "Xcode")
set (CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "YES")
endif()
set(OSD_COMPILER_FLAGS)
# Disable spurious warnings in gcc builds and clang
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANGCC OR CMAKE_COMPILER_IS_ICC )
# Turn on all warnings
if(CMAKE_COMPILER_IS_ICC)
list(APPEND OSD_COMPILER_FLAGS -w2 -wd1572 -wd1418 -wd981 -wd383 -wd193 -wd444)
else()
list(APPEND OSD_COMPILER_FLAGS -Wall -Wextra)
endif()
# HBR uses the offsetof macro on a templated struct, which appears
# to spuriously set off this warning in both gcc and Clang
list(APPEND OSD_COMPILER_FLAGS -Wno-invalid-offsetof)
# HBR uses unions as an optimization for its memory allocation.
# Type casting between union members breaks strict aliasing rules from
# gcc 4.4.1 versions onwards. We disable the warning but keep aliasing
# optimization.
list(APPEND OSD_COMPILER_FLAGS -Wno-strict-aliasing)
# FAR and OSD have templated virtual function implementations that trigger
# a lot of hidden virtual function overloads (some of them spurious).
# Disable those for now in Clang.
if(CMAKE_COMPILER_IS_CLANGCC)
list(APPEND OSD_COMPILER_FLAGS -Wno-overloaded-virtual)
endif()
# Intel's icc compiler requires some libraries linked
if(CMAKE_COMPILER_IS_ICC)
foreach (ICC_LIB iomp5 irng intlc)
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
list(APPEND ICC_LIB_ARCH "intel64")
elseif(CMAKE_SIZEOF_VOID_P MATCHES "4")
list(APPEND ICC_LIB_ARCH "ia32")
endif()
find_library( ICC_${ICC_LIB}
NAMES
${ICC_LIB}
HINTS
${ICC_LOCATION}
PATHS
/opt/intel/lib/
PATH_SUFFIXES
${ICC_LIB_ARCH}
lib/${ICC_LIB_ARCH}
)
if (ICC_${ICC_LIB})
list(APPEND ICC_LIBRARIES ${ICC_${ICC_LIB}})
else()
message( FATAL_ERROR "${ICC_${ICC_LIB}} library not found - required by icc" )
endif()
endforeach()
endif()
elseif(MSVC)
# Turn on all warnings
list(APPEND OSD_COMPILER_FLAGS /Wall)
list(APPEND OSD_COMPILER_FLAGS
/W3 # Use warning level recommended for production purposes.
# Disable since OSD fails to compile on VS2019.
#/WX # Treat all compiler warnings as errors.
# warning C4005: macro redefinition
/wd4005
# these warnings are being triggered from inside VC's header files
# warning C4350: behavior change: 'member1' called instead of 'member2'
/wd4350
# warning C4548: expression before comma has no effect; expected expression with side-effect
/wd4548
# Make sure WinDef.h does not define min and max macros which
# will conflict with std::min() and std::max().
/DNOMINMAX
# Make sure the constants in <math.h> get defined.
/D_USE_MATH_DEFINES
# Do not enforce MSVC's safe CRT replacements.
/D_CRT_SECURE_NO_WARNINGS
# Disable checked iterators and iterator debugging. Visual Studio
# 2008 does not implement std::vector::data(), so we need to take the
# address of std::vector::operator[](0) to get the memory location of
# a vector's underlying data storage. This does not work for an empty
# vector if checked iterators or iterator debugging is enabled.
# XXXX manuelk : we can't force SECURE_SCL to 0 or client code has
# problems linking against OSD if their build is not also
# overriding SSCL to the same value.
# See : http://msdn.microsoft.com/en-us/library/vstudio/hh697468.aspx
#/D_SECURE_SCL=0
#/D_HAS_ITERATOR_DEBUGGING=0
)
option(MSVC_STATIC_CRT "Statically link MSVC CRT" OFF)
if(MSVC_STATIC_CRT)
message(STATUS "Using static MSVC CRT")
# http://stackoverflow.com/a/32128977/486990
add_compile_options(
"$<$<CONFIG:Debug>:/MTd>"
"$<$<CONFIG:RelWithDebInfo>:/MT>"
"$<$<CONFIG:Release>:/MT>"
"$<$<CONFIG:MinSizeRel>:/MT>"
)
else()
# Turn off a duplicate LIBCMT linker warning
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:libcmt.lib")
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:libcmt.lib")
endif()
endif()
if(${SIMD} MATCHES "AVX")
list(APPEND OSD_COMPILER_FLAGS -xAVX)
endif()
add_definitions(${OSD_COMPILER_FLAGS})
#-------------------------------------------------------------------------------
# Ignore rules that will re-run cmake (this will avoid constant
# reloading of the generated Visual Studio project).
set(CMAKE_SUPPRESS_REGENERATION TRUE)
option(PTEX_LOCATION "Path to Ptex" "")
option(GLEW_LOCATION "Path to GLEW" "")
option(GLFW_LOCATION "Path to GLFW" "")
option(NO_LIB "Disable the opensubdiv libs build (caveat emptor)" OFF)
option(NO_EXAMPLES "Disable examples build" OFF)
option(NO_TUTORIALS "Disable tutorials build" OFF)
option(NO_REGRESSION "Disable regression tests build" OFF)
option(NO_PTEX "Disable PTex support" OFF)
option(NO_DOC "Disable documentation build" OFF)
option(NO_OMP "Disable OpenMP backend" OFF)
option(NO_TBB "Disable TBB backend" OFF)
option(NO_CUDA "Disable CUDA backend" OFF)
option(NO_OPENCL "Disable OpenCL backend" OFF)
option(NO_CLEW "Disable CLEW wrapper library" OFF)
option(NO_OPENGL "Disable OpenGL support")
option(NO_GLES "Disable GLES support" ON)
option(NO_METAL "Disable Metal support" OFF)
option(NO_DX "Disable DirectX support")
option(NO_TESTS "Disable all tests")
option(NO_GLTESTS "Disable GL tests")
option(NO_GLEW "Disable use of GLEW" ON)
option(NO_GLFW "Disable components depending on GLFW" OFF)
option(NO_GLFW_X11 "Disable GLFW components depending on X11" OFF)
option(OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES "Enable true derivative evaluation for Gregory basis patches" OFF)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
# Save the current value of BUILD_SHARED_LIBS and restore it after
# processing Find* modules, since some of the Find* modules invoked
# below may wind up stomping over this value.
set(build_shared_libs "${BUILD_SHARED_LIBS}")
set(OSD_GPU FALSE)
# Check for dependencies
if(NOT NO_OMP)
find_package(OpenMP)
endif()
if(NOT NO_TBB)
find_package(TBB 4.0)
endif()
if (NOT NO_OPENGL)
find_package(OpenGL)
endif()
#find_package(OpenGLES)
if(NOT NO_OPENCL)
if(NOT NO_CLEW)
find_package(CLEW)
endif()
if (NOT CLEW_FOUND)
find_package(OpenCL 1.1)
else()
set(OPENCL_FOUND TRUE)
endif()
endif()
if(NOT NO_CUDA)
find_package(CUDA 4.0)
endif()
if(NOT NO_GLFW AND NOT NO_OPENGL AND NOT ANDROID AND NOT IOS)
find_package(GLFW 3.0.0)
endif()
if(NOT NO_PTEX)
find_package(PTex 2.0)
find_package(ZLIB 1.2)
endif()
if(APPLE AND NOT NO_METAL)
find_package(Metal)
endif()
# Win32 may report OPENGL_FOUND even if `NO_OPENGL` is set
if (OPENGL_FOUND AND NOT NO_OPENGL AND NOT IOS)
add_definitions(
-DOPENSUBDIV_HAS_OPENGL
)
if (NOT NO_GLEW)
if (APPLE)
find_package(GLEW)
else()
find_package(GLEW REQUIRED)
endif()
endif()
if(GLEW_FOUND)
add_definitions( -DOSD_USES_GLEW )
else()
add_definitions( -DOSD_USES_INTERNAL_GLAPILOADER )
endif()
endif()
if (WIN32 AND NOT NO_DX)
find_package(DXSDK)
endif()
if (NOT NO_DOC)
find_package(Doxygen 1.8.4)
find_package(Docutils 0.9)
else()
set(DOXYGEN_EXECUTABLE )
endif()
set(BUILD_SHARED_LIBS "${build_shared_libs}")
# 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}
)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_CXX_FLAGS}")
else()
if (NOT NO_OMP)
message(WARNING
"OpenMP was not found : support for OMP parallel compute kernels "
"will be disabled in Osd. If your compiler supports OpenMP "
"directives, please refer to the FindOpenMP.cmake shared module "
"in your cmake installation.")
endif()
endif()
if(TBB_FOUND)
add_definitions(
-DOPENSUBDIV_HAS_TBB
${TBB_CXX_FLAGS}
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TBB_CXX_FLAGS}")
else()
if (NOT NO_TBB)
message(WARNING
"TBB was not found : support for TBB parallel compute kernels "
"will be disabled in Osd. If your compiler supports TBB "
"directives, please refer to the FindTBB.cmake shared module "
"in your cmake installation.")
endif()
endif()
if( METAL_FOUND AND NOT NO_METAL)
set(OSD_GPU TRUE)
if (CMAKE_COMPILER_IS_CLANGCC)
# When building with Metal support enabled, we need to explicitly
# request that the C++ code be compiled with C++11 since some example
# code for Metal relies on functionality only available in C++11.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
endif()
if( OPENGL_FOUND AND NOT NO_OPENGL)
set(OSD_GPU TRUE)
endif()
if(GLFW_FOUND AND (GLFW_VERSION VERSION_EQUAL 3.0 OR GLFW_VERSION VERSION_GREATER 3.0))
add_definitions( -DGLFW_VERSION_3 )
endif()
macro(osd_detect_gl_version header)
if (EXISTS "${header}")
file(STRINGS "${header}" VERSION_4_2 REGEX "^#define GL_VERSION_4_2.*$")
if (VERSION_4_2)
set(OPENGL_4_2_FOUND TRUE)
else ()
message(WARNING "OpenGL 4.2 dependent features not enabled")
endif ()
file(STRINGS "${header}" VERSION_4_3 REGEX "^#define GL_VERSION_4_3.*$")
if (VERSION_4_3)
SET(OPENGL_4_3_FOUND TRUE)
else ()
message(WARNING "OpenGL 4.3 dependent features not enabled")
endif ()
endif ()
endmacro()
if(GLEW_FOUND AND GLEW_INCLUDE_DIR)
osd_detect_gl_version(${GLEW_INCLUDE_DIR}/GL/glew.h)
set(OPENGL_LOADER_INCLUDE_DIRS
${GLEW_INCLUDE_DIR}
${PROJECT_SOURCE_DIR}/glLoader)
set(OPENGL_LOADER_LIBRARIES
${GLEW_LIBRARY}
${OPENGL_gl_LIBRARY})
elseif(OPENGL_FOUND)
osd_detect_gl_version(${PROJECT_SOURCE_DIR}/glLoader/glApi.h)
set(OPENGL_LOADER_INCLUDE_DIRS
${OPENGL_INCLUDE_DIR}
${PROJECT_SOURCE_DIR}/glLoader)
set(OPENGL_LOADER_LIBRARIES
${OPENGL_gl_LIBRARY})
endif()
# note : (GLSL transform feedback kernels require GL 4.2)
if(OPENGL_4_2_FOUND)
add_definitions(
-DOPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
)
else()
if (NOT NO_OPENGL)
message(WARNING
"OpenGL 4.2 was not found : support for GLSL transform feedback 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()
endif()
# note : (GLSL compute shader kernels require GL 4.3)
if(OPENGL_4_3_FOUND)
add_definitions(
-DOPENSUBDIV_HAS_GLSL_COMPUTE
)
else()
if (NOT NO_OPENGL)
message(WARNING
"OpenGL 4.3 was not found : support for GLSL compute shader kernels "
"will be disabled in Osd. If you have an OpenGL SDK installed "
"(version 4.3 or above), please refer to the FindOpenGL.cmake "
"shared module in your cmake installation.")
endif()
endif()
if(OPENGLES_FOUND AND NOT NO_GLES)
add_definitions(
-DOPENSUBDIV_HAS_OPENGLES
)
set(OSD_GPU TRUE)
endif()
if(OPENCL_FOUND)
add_definitions(
-DOPENSUBDIV_HAS_OPENCL
)
if(CLEW_FOUND)
add_definitions(
-DOPENSUBDIV_HAS_CLEW
)
set(OPENCL_INCLUDE_DIRS ${CLEW_INCLUDE_DIR})
set(OPENCL_LIBRARIES ${CLEW_LIBRARY} ${CMAKE_DL_LIBS})
else()
if (NOT NO_CLEW)
message(WARNING
"OpenCL was found, but CLEW was not. "
"Building with OpenCL support enabled, but the built binary "
"will not be portable to systems without OpenCL installed.")
endif()
endif()
if (DXSDK_FOUND AND NOT NO_DX)
if (OPENCL_CL_D3D11_H_FOUND)
set(OPENCL_D3D11_INTEROP_FOUND "YES")
add_definitions(
-DOPENSUBDIV_HAS_OPENCL_DX_INTEROP
-DOPENSUBDIV_HAS_CL_D3D11_H
)
endif()
if (OPENCL_CL_D3D11_EXT_H_FOUND)
set(OPENCL_D3D11_INTEROP_FOUND "YES")
add_definitions(
-DOPENSUBDIV_HAS_OPENCL_DX_INTEROP
-DOPENSUBDIV_HAS_CL_D3D11_EXT_H
)
endif()
endif()
set(OSD_GPU TRUE)
else()
if (NOT NO_OPENCL)
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()
endif()
if(CUDA_FOUND)
add_definitions(
-DOPENSUBDIV_HAS_CUDA
-DCUDA_ENABLE_DEPRECATED=0
)
set(OSD_GPU TRUE)
if (UNIX)
list( APPEND CUDA_NVCC_FLAGS -Xcompiler -fPIC )
if (NOT DEFINED OSD_CUDA_NVCC_FLAGS)
if (CUDA_VERSION_MAJOR LESS 6)
set( OSD_CUDA_NVCC_FLAGS --gpu-architecture compute_11 )
else()
set( OSD_CUDA_NVCC_FLAGS --gpu-architecture compute_20 )
endif()
endif()
endif()
if (DEFINED OSD_CUDA_NVCC_FLAGS)
list( APPEND CUDA_NVCC_FLAGS ${OSD_CUDA_NVCC_FLAGS})
endif()
else()
if (NOT NO_CUDA)
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()
endif()
if(PTEX_FOUND)
add_definitions(
-DOPENSUBDIV_HAS_PTEX
-DPTEX_STATIC
)
else()
if(NOT NO_PTEX)
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()
endif()
if( OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES )
add_definitions(-DOPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES)
endif()
# Link examples & regressions against Osd
if( BUILD_SHARED_LIBS )
if( OSD_GPU )
set( OSD_LINK_TARGET osd_dynamic_gpu osd_dynamic_cpu )
else()
set( OSD_LINK_TARGET osd_dynamic_cpu )
endif()
else()
if( OSD_GPU )
set( OSD_LINK_TARGET osd_static_gpu osd_static_cpu )
else()
set( OSD_LINK_TARGET osd_static_cpu )
endif()
endif()
if (WIN32)
if ("${GLEW_LIBRARY}" MATCHES "glew32s(d|)")
# Link against the static version of GLEW
add_definitions(
-DGLEW_STATIC
)
endif()
if (DXSDK_FOUND AND NOT NO_DX)
add_definitions(
-DOPENSUBDIV_HAS_DX11SDK
)
set(OSD_GPU TRUE)
elseif(NOT NO_DX)
message(WARNING
"DirectX11 SDK was not found. "
"If you do have DXSDK installed and see this message, "
"please add your sdk path to FindDirectX.cmake in "
"${PROJECT_SOURCE_DIR}/cmake or set it through the "
"DXSDK_LOCATION cmake command line argument or "
"environment variable."
)
endif()
# Link examples & regressions statically against Osd for
# Windows until all the kinks can be worked out.
if( OSD_GPU )
set( OSD_LINK_TARGET osd_static_cpu osd_static_gpu )
else()
set( OSD_LINK_TARGET osd_static_cpu )
endif()
endif()
#-------------------------------------------------------------------------------
# General-use macros
# Macro for processing public headers into the build area for doxygen processing
add_custom_target( public_headers )
macro(osd_add_doxy_headers headers)
if (NOT NO_DOC AND DOXYGEN_FOUND)
file(RELATIVE_PATH path "${OpenSubdiv_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" )
string(REPLACE "/" "_" targetpath ${path})
foreach (header ${headers})
set(infile "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
set(outfile "${OpenSubdiv_BINARY_DIR}/public_headers/${path}/${header}")
set(targetname "${targetpath}_${header}")
add_custom_command(
OUTPUT
"${outfile}"
COMMAND
${CMAKE_COMMAND}
ARGS
-E copy ${infile} ${outfile}
DEPENDS
${infile}
)
add_custom_target(${targetname} DEPENDS "${outfile}")
list(APPEND headerfiles ${targetname} )
endforeach()
add_dependencies( public_headers DEPENDS ${headerfiles} )
endif()
endmacro()
# Kernel Stringification
# We want to use preprocessor include directives to include GLSL and OpenCL
# kernel 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.
function(osd_stringify src_files varname)
set(inc_files "")
foreach(src_file ${src_files})
string(REGEX REPLACE ".*[.](.*)" "\\1" extension "${src_file}")
string(REGEX REPLACE "(.*)[.].*" "\\1.gen.h" inc_file "${src_file}")
list(APPEND inc_files "${CMAKE_CURRENT_BINARY_DIR}/${inc_file}")
add_custom_command(
OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/${inc_file}"
COMMAND
stringify "${CMAKE_CURRENT_SOURCE_DIR}/${src_file}" "${CMAKE_CURRENT_BINARY_DIR}/${inc_file}"
DEPENDS
stringify "${CMAKE_CURRENT_SOURCE_DIR}/${src_file}"
)
endforeach()
set(${varname} ${inc_files} PARENT_SCOPE)
endfunction()
# Macro wrapper for adding a non-cuda dependent executable
macro(osd_add_executable target folder)
add_executable(${target} ${ARGN})
set_target_properties(${target} PROPERTIES FOLDER ${folder})
if(CMAKE_COMPILER_IS_ICC)
target_link_libraries(${target} ${ICC_LIBRARIES})
endif()
if(APPLE)
set_property (TARGET ${target} APPEND_STRING PROPERTY
COMPILE_FLAGS " -fobjc-arc ")
endif()
endmacro()
# Macro for adding a cuda executable if cuda is found and a regular
# executable otherwise.
macro(osd_add_possibly_cuda_executable target folder)
if(CUDA_FOUND)
cuda_add_executable(${target} ${ARGN})
else()
add_executable(${target} ${ARGN})
endif()
set_target_properties(${target} PROPERTIES FOLDER ${folder})
if(CMAKE_COMPILER_IS_ICC)
target_link_libraries(${target} ${ICC_LIBRARIES})
endif()
if(APPLE)
set_property (TARGET ${target} APPEND_STRING PROPERTY
COMPILE_FLAGS " -fobjc-arc ")
endif()
endmacro()
# Macro for adding a cuda library if cuda is found and a regular
# library otherwise.
macro(osd_add_possibly_cuda_library target folder)
if(CUDA_FOUND)
cuda_add_library(${target} ${ARGN})
else()
add_library(${target} ${ARGN})
endif()
set_target_properties(${target} PROPERTIES FOLDER ${folder})
if(APPLE)
set_property (TARGET ${target} APPEND_STRING PROPERTY
COMPILE_FLAGS " -fobjc-arc ")
endif()
endmacro()
# Macro for adding a (potentially cuda) GLFW executable.
macro(osd_add_glfw_executable target folder)
osd_add_possibly_cuda_executable(${target} ${folder} ${ARGN})
if(APPLE)
set_property (TARGET ${target} APPEND_STRING PROPERTY
COMPILE_FLAGS " -fobjc-arc ")
endif()
endmacro()
#-------------------------------------------------------------------------------
# Build targets
# if you want to build examples against installed OpenSubdiv header files,
# use OPENSUBDIV_INCLUDE_DIR.
# example: if you have already installed opensubdiv libs in this cmake setup,
# set (OPENSUBDIV_INCLUDE_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INCDIR_BASE})
if (NOT OPENSUBDIV_INCLUDE_DIR)
set(OPENSUBDIV_INCLUDE_DIR "${PROJECT_SOURCE_DIR}")
endif()
#if (NOT NO_TESTS)
# enable_testing()
#endif()
#
#if (NOT NO_OPENGL)
# add_subdirectory(glLoader)
#endif()
add_subdirectory(opensubdiv)
#if (NOT ANDROID) # XXXdyu
# add_subdirectory(regression)
#endif()
#if (NOT NO_EXAMPLES)
# add_subdirectory(examples)
#endif()
#
#if (NOT NO_TUTORIALS)
# add_subdirectory(tutorials)
#endif()
#
#if (NOT NO_DOC)
# add_subdirectory(documentation)
#endif()

174
src/osd/LICENSE.txt Normal file
View File

@@ -0,0 +1,174 @@
Modified Apache 2.0 License
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor
and its affiliates, except as required to comply with Section 4(c) of
the License and to reproduce the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

3
src/osd/README.md Normal file
View File

@@ -0,0 +1,3 @@
Code grabbed from OpenSubdiv-aarch64(v3.4.3(2020 April))
https://github.com/syoyo/OpenSubdiv-aarch64

View File

@@ -0,0 +1,358 @@
#
# Copyright 2013 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
if (NOT NO_LIB)
#---------------------------------------------------------------------------
include_directories(
"${PROJECT_SOURCE_DIR}/opensubdiv"
)
#---------------------------------------------------------------------------
# platform dependent tweaks
if(APPLE OR UNIX)
set(PLATFORM_COMPILE_FLAGS -fPIC)
elseif(WIN32)
endif()
add_definitions(
${PLATFORM_COMPILE_FLAGS}
)
#---------------------------------------------------------------------------
if( OPENMP_FOUND )
if (CMAKE_COMPILER_IS_GNUCXX)
list(APPEND PLATFORM_CPU_LIBRARIES gomp)
endif()
endif()
if( TBB_FOUND )
include_directories("${TBB_INCLUDE_DIR}")
list(APPEND PLATFORM_CPU_LIBRARIES
${TBB_LIBRARIES}
)
endif()
if(OPENGL_FOUND OR OPENCL_FOUND OR DXSDK_FOUND OR METAL_FOUND)
add_subdirectory(tools/stringify)
endif()
if( OPENGL_FOUND )
include_directories(${OPENGL_LOADER_INCLUDE_DIRS})
list(APPEND PLATFORM_GPU_LIBRARIES
${OPENGL_LOADER_LIBRARIES}
)
set(OPENGL_LOADER_OBJS $<TARGET_OBJECTS:glLoader_obj>)
elseif( OPENGLES_FOUND )
include_directories("${OPENGLES_INCLUDE_DIR}")
list(APPEND PLATFORM_GPU_LIBRARIES
${OPENGLES_LIBRARIES}
)
endif()
if( DXSDK_FOUND )
include_directories( "${DXSDK_INCLUDE_DIR}" )
list(APPEND PLATFORM_GPU_LIBRARIES
${DXSDK_LIBRARIES}
)
endif()
if( METAL_FOUND )
include_directories( "${METAL_INCLUDE_DIR}" )
list(APPEND PLATFORM_GPU_LIBRARIES
${METAL_LIBRARIES}
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
endif()
if ( OPENCL_FOUND )
include_directories( "${OPENCL_INCLUDE_DIRS}" )
list(APPEND PLATFORM_GPU_LIBRARIES
${OPENCL_LIBRARIES}
)
endif()
if( CUDA_FOUND )
include_directories( "${CUDA_INCLUDE_DIRS}" )
endif()
#---------------------------------------------------------------------------
add_subdirectory(hbr)
add_subdirectory(sdc)
add_subdirectory(vtr)
add_subdirectory(far)
add_subdirectory(osd)
#---------------------------------------------------------------------------
install( FILES version.h
DESTINATION
"${CMAKE_INCDIR_BASE}"
PERMISSIONS
OWNER_READ
GROUP_READ
WORLD_READ )
#---------------------------------------------------------------------------
# Build static libs ------------------------------------
add_library(osd_static_cpu
STATIC
version.cpp
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:osd_cpu_obj>
)
set_target_properties(osd_static_cpu
PROPERTIES
OUTPUT_NAME osdCPU
CLEAN_DIRECT_OUTPUT 1
FOLDER "opensubdiv"
)
target_link_libraries(osd_static_cpu
${PLATFORM_CPU_LIBRARIES}
)
install( TARGETS osd_static_cpu DESTINATION "${CMAKE_LIBDIR_BASE}" )
if( OSD_GPU )
# this macro uses FindCUDA.cmake to compile .cu kernel files
# the target then adds the other obj dependencies and include files
osd_add_possibly_cuda_library(osd_static_gpu "opensubdiv"
STATIC
version.cpp
$<TARGET_OBJECTS:osd_gpu_obj>
${OPENGL_LOADER_OBJS}
${CUDA_KERNEL_FILES}
)
set_target_properties(osd_static_gpu PROPERTIES OUTPUT_NAME osdGPU CLEAN_DIRECT_OUTPUT 1)
target_link_libraries(osd_static_gpu
${PLATFORM_CPU_LIBRARIES} ${PLATFORM_GPU_LIBRARIES}
)
install( TARGETS osd_static_gpu DESTINATION "${CMAKE_LIBDIR_BASE}" )
endif()
# Build dynamic libs ----------------------------------
if (BUILD_SHARED_LIBS AND NOT WIN32 AND NOT IOS)
# generate dynamic-link targets
#---------------------------------------------------
add_library(osd_dynamic_cpu
SHARED
version.cpp
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:osd_cpu_obj>
)
if (NOT ANDROID)
set_target_properties(osd_dynamic_cpu
PROPERTIES
OUTPUT_NAME osdCPU
CLEAN_DIRECT_OUTPUT 1
SOVERSION ${OSD_SONAME}
)
else()
set_target_properties(osd_dynamic_cpu
PROPERTIES
OUTPUT_NAME osdCPU
CLEAN_DIRECT_OUTPUT 1
)
endif()
target_link_libraries(osd_dynamic_cpu
${PLATFORM_CPU_LIBRARIES}
)
install( TARGETS osd_dynamic_cpu LIBRARY DESTINATION "${CMAKE_LIBDIR_BASE}" )
#---------------------------------------------------
if( OSD_GPU )
osd_add_possibly_cuda_library(osd_dynamic_gpu "opensubdiv"
SHARED
version.cpp
$<TARGET_OBJECTS:osd_gpu_obj>
${OPENGL_LOADER_OBJS}
${CUDA_KERNEL_FILES}
)
if (NOT ANDROID)
set_target_properties(osd_dynamic_gpu
PROPERTIES
OUTPUT_NAME osdGPU
CLEAN_DIRECT_OUTPUT 1
SOVERSION ${OSD_SONAME}
)
else()
set_target_properties(osd_dynamic_gpu
PROPERTIES
OUTPUT_NAME osdGPU
CLEAN_DIRECT_OUTPUT 1
)
endif()
target_link_libraries(osd_dynamic_gpu
osd_dynamic_cpu
${PLATFORM_CPU_LIBRARIES} ${PLATFORM_GPU_LIBRARIES}
)
install( TARGETS osd_dynamic_gpu LIBRARY DESTINATION "${CMAKE_LIBDIR_BASE}" )
endif()
endif()
# Build frameworks ----------------------------------
if(APPLE)
get_directory_property(OSD_HEADER_FILES DIRECTORY ${CMAKE_SOURCE_DIR}/opensubdiv/osd DEFINITION PUBLIC_HEADER_FILES)
get_directory_property(FAR_HEADER_FILES DIRECTORY ${CMAKE_SOURCE_DIR}/opensubdiv/far DEFINITION PUBLIC_HEADER_FILES)
get_directory_property(SDC_HEADER_FILES DIRECTORY ${CMAKE_SOURCE_DIR}/opensubdiv/sdc DEFINITION PUBLIC_HEADER_FILES)
get_directory_property(HBR_HEADER_FILES DIRECTORY ${CMAKE_SOURCE_DIR}/opensubdiv/hbr DEFINITION PUBLIC_HEADER_FILES)
get_directory_property(VTR_HEADER_FILES DIRECTORY ${CMAKE_SOURCE_DIR}/opensubdiv/vtr DEFINITION PUBLIC_HEADER_FILES)
foreach(file ${OSD_HEADER_FILES})
list(APPEND PUBLIC_HEADER_FILES "osd/${file}")
endforeach(file)
foreach(file ${FAR_HEADER_FILES})
list(APPEND PUBLIC_HEADER_FILES "far/${file}")
endforeach(file)
foreach(file ${SDC_HEADER_FILES})
list(APPEND PUBLIC_HEADER_FILES "sdc/${file}")
endforeach(file)
foreach(file ${HBR_HEADER_FILES})
list(APPEND PUBLIC_HEADER_FILES "hbr/${file}")
endforeach(file)
foreach(file ${VTR_HEADER_FILES})
list(APPEND PUBLIC_HEADER_FILES "vtr/${file}")
endforeach(file)
list(APPEND PUBLIC_HEADER_FILES "version.h")
#static framework
add_library(osd_static_framework
STATIC
version.cpp
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:osd_cpu_obj>
$<TARGET_OBJECTS:osd_gpu_obj>
${OPENGL_LOADER_OBJS}
)
set_target_properties(
osd_static_framework
PROPERTIES
FRAMEWORK true
INSTALL_NAME_DIR "@rpath/OpenSubdiv.framework/OpenSubdiv"
INSTALL_RPATH "@executable_path/Frameworks;@loader_path/Frameworks"
OUTPUT_NAME OpenSubdiv
CLEAN_DIRECT_OUTPUT true
)
target_link_libraries(osd_static_framework
${PLATFORM_CPU_LIBRARIES} ${PLATFORM_GPU_LIBRARIES}
)
install( TARGETS osd_static_framework
LIBRARY DESTINATION "${CMAKE_LIBDIR_BASE}"
FRAMEWORK DESTINATION "${CMAKE_LIBDIR_BASE}"
PUBLIC_HEADER DESTINATION "${CMAKE_INCDIR_BASE}"
ARCHIVE DESTINATION "${CMAKE_LIBDIR_BASE}")
#shared framework
if (BUILD_SHARED_LIBS)
add_library(osd_dynamic_framework
SHARED
version.cpp
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:osd_cpu_obj>
$<TARGET_OBJECTS:osd_gpu_obj>
${OPENGL_LOADER_OBJS}
)
set_target_properties(
osd_dynamic_framework
PROPERTIES
RPATH true
FRAMEWORK true
INSTALL_NAME_DIR "@rpath/OpenSubdiv.framework/OpenSubdiv"
INSTALL_RPATH "@executable_path/Frameworks;@loader_path/Frameworks"
OUTPUT_NAME OpenSubdiv
CLEAN_DIRECT_OUTPUT true
)
target_link_libraries(osd_dynamic_framework
${PLATFORM_CPU_LIBRARIES} ${PLATFORM_GPU_LIBRARIES}
)
install( TARGETS osd_dynamic_framework
FRAMEWORK DESTINATION "${CMAKE_LIBDIR_BASE}"
LIBRARY DESTINATION "${CMAKE_LIBDIR_BASE}"
PUBLIC_HEADER DESTINATION "${CMAKE_INCDIR_BASE}"
PRIVATE_HEADER DESTINATION "${CMAKE_INCDIR_BASE}"
)
foreach(file ${PUBLIC_HEADER_FILES})
add_custom_command(TARGET osd_dynamic_framework POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_SOURCE_DIR}/opensubdiv/${file}"
"$<TARGET_FILE_DIR:osd_dynamic_framework>/Headers/${file}"
)
endforeach(file)
add_custom_command(TARGET osd_dynamic_framework POST_BUILD
COMMAND ln -sf
"Versions/Current/Headers"
"$<TARGET_FILE_DIR:osd_dynamic_framework>/../../Headers"
)
endif()
endif()
endif()
#-------------------------------------------------------------------------------

View File

@@ -0,0 +1,108 @@
#
# Copyright 2013 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
#-------------------------------------------------------------------------------
# source & headers
set(SOURCE_FILES
bilinearPatchBuilder.cpp
catmarkPatchBuilder.cpp
error.cpp
loopPatchBuilder.cpp
patchBasis.cpp
patchBuilder.cpp
patchDescriptor.cpp
patchMap.cpp
patchTable.cpp
patchTableFactory.cpp
ptexIndices.cpp
stencilTable.cpp
stencilTableFactory.cpp
stencilBuilder.cpp
topologyDescriptor.cpp
topologyRefiner.cpp
topologyRefinerFactory.cpp
)
set(PRIVATE_HEADER_FILES
bilinearPatchBuilder.h
catmarkPatchBuilder.h
loopPatchBuilder.h
patchBasis.h
patchBuilder.h
sparseMatrix.h
stencilBuilder.h
)
set(PUBLIC_HEADER_FILES
error.h
patchDescriptor.h
patchParam.h
patchMap.h
patchTable.h
patchTableFactory.h
primvarRefiner.h
ptexIndices.h
stencilTable.h
stencilTableFactory.h
topologyDescriptor.h
topologyLevel.h
topologyRefiner.h
topologyRefinerFactory.h
types.h
)
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
#-------------------------------------------------------------------------------
if (NOT NO_LIB)
# Compile objs first for both the CPU and GPU libs -----
add_library(far_obj
OBJECT
${SOURCE_FILES}
${PRIVATE_HEADER_FILES}
${PUBLIC_HEADER_FILES}
)
set_target_properties(far_obj
PROPERTIES
FOLDER "opensubdiv"
)
endif()
#-------------------------------------------------------------------------------
osd_add_doxy_headers( "${DOXY_HEADER_FILES}" )
install(
FILES
${PUBLIC_HEADER_FILES}
DESTINATION
"${CMAKE_INCDIR_BASE}/far"
PERMISSIONS
OWNER_READ
GROUP_READ
WORLD_READ )
#-------------------------------------------------------------------------------

View File

@@ -0,0 +1,107 @@
//
// Copyright 2018 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/bilinearPatchBuilder.h"
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
using Vtr::internal::Level;
using Vtr::internal::FVarLevel;
using Vtr::internal::Refinement;
namespace Far {
namespace {
//
// The patch type associated with each basis for Bilinear -- quickly indexed
// from an array. The patch type here is essentially the quad form of each
// basis.
//
PatchDescriptor::Type patchTypeFromBasisArray[] = {
PatchDescriptor::NON_PATCH, // undefined
PatchDescriptor::QUADS, // regular
PatchDescriptor::GREGORY_BASIS, // Gregory
PatchDescriptor::QUADS, // linear
PatchDescriptor::NON_PATCH }; // Bezier -- for future use
};
BilinearPatchBuilder::BilinearPatchBuilder(
TopologyRefiner const& refiner, Options const& options) :
PatchBuilder(refiner, options) {
_regPatchType = patchTypeFromBasisArray[_options.regBasisType];
_irregPatchType = (_options.irregBasisType == BASIS_UNSPECIFIED)
? _regPatchType
: patchTypeFromBasisArray[_options.irregBasisType];
_nativePatchType = PatchDescriptor::QUADS;
_linearPatchType = PatchDescriptor::QUADS;
}
BilinearPatchBuilder::~BilinearPatchBuilder() {
}
PatchDescriptor::Type
BilinearPatchBuilder::patchTypeFromBasis(BasisType basis) const {
return patchTypeFromBasisArray[(int)basis];
}
template <typename REAL>
int
BilinearPatchBuilder::convertSourcePatch(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<REAL> & matrix) const {
assert("Conversion from Bilinear patches to other bases not yet supported" == 0);
// For suppressing warnings until implemented...
if (sourcePatch.GetNumSourcePoints() == 0) return -1;
if (patchType == PatchDescriptor::NON_PATCH) return -1;
if (matrix.GetNumRows() <= 0) return -1;
return -1;
}
int
BilinearPatchBuilder::convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const {
return convertSourcePatch(sourcePatch, patchType, matrix);
}
int
BilinearPatchBuilder::convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const {
return convertSourcePatch(sourcePatch, patchType, matrix);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,73 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_BILINEAR_PATCH_BUILDER_H
#define OPENSUBDIV3_FAR_BILINEAR_PATCH_BUILDER_H
#include "../version.h"
#include "../far/patchBuilder.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// BilinearPatchBuilder
//
// Declaration of PatchBuilder subclass supporting Sdc::SCHEME_BILINEAR.
// Required virtual methods are included, along with any customizations
// local to their implementation.
//
class BilinearPatchBuilder : public PatchBuilder {
public:
BilinearPatchBuilder(TopologyRefiner const& refiner, Options const& options);
virtual ~BilinearPatchBuilder();
protected:
virtual PatchDescriptor::Type patchTypeFromBasis(BasisType basis) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const;
private:
template <typename REAL>
int convertSourcePatch(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<REAL> & matrix) const;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_BILINEAR_PATCH_BUILDER_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_CATMARK_PATCH_BUILDER_H
#define OPENSUBDIV3_FAR_CATMARK_PATCH_BUILDER_H
#include "../version.h"
#include "../far/patchBuilder.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// CatmarkPatchBuilder
//
// Declaration of PatchBuilder subclass supporting Sdc::SCHEME_CATMARK.
// Required virtual methods are included, along with any customizations
// local to their implementation.
//
class CatmarkPatchBuilder : public PatchBuilder {
public:
CatmarkPatchBuilder(TopologyRefiner const& refiner, Options const& options);
virtual ~CatmarkPatchBuilder();
protected:
virtual PatchDescriptor::Type patchTypeFromBasis(BasisType basis) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const;
private:
typedef SparseMatrix<float> ConversionMatrix;
template <typename REAL>
int convertSourcePatch(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<REAL> & matrix) const;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_CATMARK_PATCH_BUILDER_H */

View File

@@ -0,0 +1,107 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/error.h"
#include <cassert>
#include <cstdarg>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Statics for the publicly assignable callbacks and the methods to
// assign them (disable static assignment warnings when doing so):
//
static ErrorCallbackFunc errorFunc = 0;
static WarningCallbackFunc warningFunc = 0;
#ifdef __INTEL_COMPILER
#pragma warning disable 1711
#endif
void SetErrorCallback(ErrorCallbackFunc func) {
errorFunc = func;
}
void SetWarningCallback(WarningCallbackFunc func) {
warningFunc = func;
}
#ifdef __INTEL_COMPILER
#pragma warning enable 1711
#endif
//
// The default error and warning callbacks eventually belong in the
// internal namespace:
//
void Error(ErrorType err, const char *format, ...) {
static char const * errorTypeLabel[] = {
"No Error",
"Fatal Error",
"Coding Error (internal)",
"Coding Error",
"Error"
};
assert(err!=FAR_NO_ERROR);
char message[10240];
va_list argptr;
va_start(argptr, format);
vsnprintf(message, 10240, format, argptr);
va_end(argptr);
if (errorFunc) {
errorFunc(err, message);
} else {
printf("%s: %s\n", errorTypeLabel[err], message);
}
}
void Warning(const char *format, ...) {
char message[10240];
va_list argptr;
va_start(argptr, format);
vsnprintf(message, 10240, format, argptr);
va_end(argptr);
if (warningFunc) {
warningFunc(message);
} else {
fprintf(stdout, "Warning: %s\n", message);
}
}
} // end namespace
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,95 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_ERROR_H
#define OPENSUBDIV3_FAR_ERROR_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
typedef enum {
FAR_NO_ERROR, ///< No error. Move along.
FAR_FATAL_ERROR, ///< Issue a fatal error and end the program.
FAR_INTERNAL_CODING_ERROR, ///< Issue an internal programming error, but continue execution.
FAR_CODING_ERROR, ///< Issue a generic programming error, but continue execution.
FAR_RUNTIME_ERROR ///< Issue a generic runtime error, but continue execution.
} ErrorType;
/// \brief The error callback function type (default is "printf")
typedef void (*ErrorCallbackFunc)(ErrorType err, const char *message);
/// \brief Sets the error callback function (default is "printf")
///
/// \note This function is not thread-safe !
///
/// @param func function pointer to the callback function
///
void SetErrorCallback(ErrorCallbackFunc func);
/// \brief The warning callback function type (default is "printf")
typedef void (*WarningCallbackFunc)(const char *message);
/// \brief Sets the warning callback function (default is "printf")
///
/// \note This function is not thread-safe !
///
/// @param func function pointer to the callback function
///
void SetWarningCallback(WarningCallbackFunc func);
//
// The following are intended for internal use only (and will eventually
// be moved within namespace internal)
//
/// \brief Sends an OSD error with a message (internal use only)
///
/// @param err the error type
///
/// @param format the format of the message (followed by arguments)
///
void Error(ErrorType err, const char *format, ...);
/// \brief Sends an OSD warning message (internal use only)
///
/// @param format the format of the message (followed by arguments)
///
void Warning(const char *format, ...);
} // end namespace
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_FAR_ERROR_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_LOOP_PATCH_BUILDER_H
#define OPENSUBDIV3_FAR_LOOP_PATCH_BUILDER_H
#include "../version.h"
#include "../far/patchBuilder.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// LoopPatchBuilder
//
// Declaration of PatchBuilder subclass supporting Sdc::SCHEME_LOOP.
// Required virtual methods are included, along with any customizations
// local to their implementation.
//
class LoopPatchBuilder : public PatchBuilder {
public:
LoopPatchBuilder(TopologyRefiner const& refiner, Options const& options);
virtual ~LoopPatchBuilder();
protected:
virtual PatchDescriptor::Type patchTypeFromBasis(BasisType basis) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const;
private:
template <typename REAL>
int convertSourcePatch(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<REAL> & matrix) const;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_LOOP_PATCH_BUILDER_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_PATCH_BASIS_H
#define OPENSUBDIV3_FAR_PATCH_BASIS_H
#include "../version.h"
#include "../far/patchParam.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
//
// XXXX barfowl: These functions are being kept internal while more complete
// underlying support for all patch types is being worked out. The set of
// bases supported here is actually larger than PatchDescriptor::Type -- with
// Bezier available for internal use. A new and more complete set of types
// is warranted (without the non-patch types associated with PatchDescriptor)
// along with an interface to query properties associated with them.
//
// Note that with the high-level functions here that operate on all patch
// types, it is not strictly necessary to expose the low-level methods in
// currant usage.
//
//
// Low-level basis evaluation (normalized, unscaled) for quad patch types:
//
template <typename REAL>
int EvalBasisLinear(REAL s, REAL t,
REAL wP[4], REAL wDs[4] = 0, REAL wDt[4] = 0, REAL wDss[4] = 0, REAL wDst[4] = 0, REAL wDtt[4] = 0);
template <typename REAL>
int EvalBasisBezier(REAL s, REAL t,
REAL wP[16], REAL wDs[16] = 0, REAL wDt[16] = 0, REAL wDss[16] = 0, REAL wDst[16] = 0, REAL wDtt[16] = 0);
template <typename REAL>
int EvalBasisBSpline(REAL s, REAL t,
REAL wP[16], REAL wDs[16] = 0, REAL wDt[16] = 0, REAL wDss[16] = 0, REAL wDst[16] = 0, REAL wDtt[16] = 0);
template <typename REAL>
int EvalBasisGregory(REAL s, REAL t,
REAL wP[20], REAL wDs[20] = 0, REAL wDt[20] = 0, REAL wDss[20] = 0, REAL wDst[20] = 0, REAL wDtt[20] = 0);
//
// Low-level basis evaluation (normalized, unscaled) for triangular patch types:
//
template <typename REAL>
int EvalBasisLinearTri(REAL s, REAL t,
REAL wP[3], REAL wDs[3] = 0, REAL wDt[3] = 0, REAL wDss[3] = 0, REAL wDst[3] = 0, REAL wDtt[3] = 0);
template <typename REAL>
int EvalBasisBezierTri(REAL s, REAL t,
REAL wP[15], REAL wDs[15] = 0, REAL wDt[15] = 0, REAL wDss[15] = 0, REAL wDst[15] = 0, REAL wDtt[15] = 0);
template <typename REAL>
int EvalBasisBoxSplineTri(REAL s, REAL t,
REAL wP[12], REAL wDs[12] = 0, REAL wDt[12] = 0, REAL wDss[12] = 0, REAL wDst[12] = 0, REAL wDtt[12] = 0);
template <typename REAL>
int EvalBasisGregoryTri(REAL s, REAL t,
REAL wP[18], REAL wDs[18] = 0, REAL wDt[18] = 0, REAL wDss[18] = 0, REAL wDst[18] = 0, REAL wDtt[18] = 0);
//
// High-level basis evaluation for all types using PatchParam:
//
template <typename REAL>
int EvaluatePatchBasisNormalized(int patchType, PatchParam const & param, REAL s, REAL t,
REAL wP[], REAL wDs[] = 0, REAL wDt[] = 0, REAL wDss[] = 0, REAL wDst[] = 0, REAL wDtt[] = 0);
template <typename REAL>
int EvaluatePatchBasis(int patchType, PatchParam const & param, REAL s, REAL t,
REAL wP[], REAL wDs[] = 0, REAL wDt[] = 0, REAL wDss[] = 0, REAL wDst[] = 0, REAL wDtt[] = 0);
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_BASIS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,332 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_PATCH_BUILDER_H
#define OPENSUBDIV3_FAR_PATCH_BUILDER_H
#include "../version.h"
#include "../sdc/types.h"
#include "../far/types.h"
#include "../far/topologyRefiner.h"
#include "../far/patchDescriptor.h"
#include "../far/patchParam.h"
#include "../far/ptexIndices.h"
#include "../far/sparseMatrix.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// SourcePatch
//
// This is a local utility class that captures the full local topology of an
// arbitrarily irregular patch, i.e. a patch which may have one or all corners
// irregular. Given the topology at each corner the entire collection of
// points involved is identified and oriented consistently.
//
// Note (barfowl):
// This was originally a class internal to PatchBuilder, but there is some
// redundancy between it and the Level::VSpan used more publicly to identify
// irregular corner topology. Replacing VSpan with SourcePatch is now under
// consideration, and doing so will impact its public/private interface (which
// was left public to give PatchBuilder access).
// A simpler constructor to initialize an instance given a set of Corners
// would also be preferable if made more public (i.e. public for use within
// the library, not exported to clients) -- eliminating the need for the
// explicit initialization and required call to the Finalize() method that
// the PatchBuilder currently performs internally.
//
class SourcePatch {
public:
struct Corner {
Corner() { std::memset(this, 0, sizeof(Corner)); }
LocalIndex _numFaces; // valence of corner vertex
LocalIndex _patchFace; // location of patch within incident faces
unsigned short _boundary : 1;
unsigned short _sharp : 1;
unsigned short _dart : 1;
// For internal bookkeeping -- consider hiding or moving elsewhere
unsigned short _sharesWithPrev : 1;
unsigned short _sharesWithNext : 1;
unsigned short _val2Interior : 1;
unsigned short _val2Adjacent : 1;
};
public:
SourcePatch() { std::memset(this, 0, sizeof(SourcePatch)); }
~SourcePatch() { }
// To be called after all Corners have been initialized (hope to
// replace this with alternative constructor at some point)
void Finalize(int size3or4);
int GetNumSourcePoints() const { return _numSourcePoints; }
int GetMaxValence() const { return _maxValence; }
int GetMaxRingSize() const { return _maxRingSize; }
int GetCornerRingSize(int corner) const { return _ringSizes[corner]; }
int GetCornerRingPoints(int corner, int points[]) const;
// public/private access needs to be reviewed when/if used more publicly
//private:
public:
// The SourcePatch is fully defined by its Corner members
Corner _corners[4];
int _numCorners;
// Additional members (derived from Corners) to help assemble corner rings:
int _numSourcePoints;
int _maxValence;
int _maxRingSize;
int _ringSizes[4];
int _localRingSizes[4];
int _localRingOffsets[4];
};
//
// PatchBuilder
//
// This is the main class to assist the identification of limit surface
// patches from faces in a TopologyRefiner for assembly into other, larger
// datatypes.
//
// The PatchBuilder takes a const reference to a refiner and supports
// arbitrarily refined hierarchies, i.e. it is not restricted to uniform or
// adaptive refinement strategies and does not include any logic relating
// to the origin of the hierarchy. It can associate a patch with any face
// in the hierarchy (subject to a few minimum requirements) -- leaving the
// decision as to which faces/patches are appropriate to its client.
//
// PatchBuilder is an abstract base class with a subclass derived to support
// each subdivision scheme -- as such, construction relies on a factory
// method to create an instance of the appropriate subclass. Only two pure
// virtual methods are required (other than the required destructor):
//
// - determine the patch type for a subdivision scheme given a more
// general basis specification (e.g. Bezier, Gregory, Linear, etc)
//
// - convert the vertices in the subdivision hierarchy into points of a
// specified patch type, using computations specific to that scheme
//
// The base class handles the more general topological analysis that
// determines the nature of a patch associated with each face -- providing
// both queries to the client, along with more involved methods to extract
// or convert data associated with the patches. There is no concrete "Patch"
// class to which all clients would be required to conform. The queries and
// data returned are provided for clients to assemble into patches or other
// aggregates as they see fit.
//
// This is intended as an internal/private class for use within the library
// for now -- possibly to be exported for use by clients when/if its
// interface is appropriate and stable.
//
class PatchBuilder {
public:
//
// A PatchBuilder is constructed given a patch "basis" rather than a
// "type" to use with the subdivision scheme involved. The relevant
// explicit patch types will be determined from the basis and scheme:
//
enum BasisType {
BASIS_UNSPECIFIED,
BASIS_REGULAR,
BASIS_GREGORY,
BASIS_LINEAR,
BASIS_BEZIER // to be supported in future
};
//
// Required Options specify a patch basis to use for both regular and
// irregular patches -- sparing the client the need to repeatedly
// specify these for each face considered. Other options are included
// to support legacy approximations:
//
struct Options {
Options() : regBasisType(BASIS_UNSPECIFIED),
irregBasisType(BASIS_UNSPECIFIED),
fillMissingBoundaryPoints(false),
approxInfSharpWithSmooth(false),
approxSmoothCornerWithSharp(false) { }
BasisType regBasisType;
BasisType irregBasisType;
bool fillMissingBoundaryPoints;
bool approxInfSharpWithSmooth;
bool approxSmoothCornerWithSharp;
};
public:
//
// Public construction (via factory method) and destruction:
//
static PatchBuilder* Create(TopologyRefiner const& refiner,
Options const& options);
virtual ~PatchBuilder();
//
// High-level queries related to the subdivision scheme of the refiner, the
// patch types associated with it and those chosen to represent its faces:
//
int GetRegularFaceSize() const { return _schemeRegFaceSize; }
BasisType GetRegularBasisType() const { return _options.regBasisType; }
BasisType GetIrregularBasisType() const { return _options.irregBasisType; }
PatchDescriptor::Type GetRegularPatchType() const { return _regPatchType; }
PatchDescriptor::Type GetIrregularPatchType() const { return _irregPatchType; }
PatchDescriptor::Type GetNativePatchType() const { return _nativePatchType; }
PatchDescriptor::Type GetLinearPatchType() const { return _linearPatchType; }
//
// Face-level queries to determine presence of patches:
//
bool IsFaceAPatch(int level, Index face) const;
bool IsFaceALeaf(int level, Index face) const;
//
// Patch-level topological queries:
//
bool IsPatchRegular(int level, Index face, int fvc = -1) const;
int GetRegularPatchBoundaryMask(int level, Index face, int fvc = -1) const;
void GetIrregularPatchCornerSpans(int level, Index face,
Vtr::internal::Level::VSpan cornerSpans[4], int fvc = -1) const;
bool DoesFaceVaryingPatchMatch(int level, Index face, int fvc) const {
return _refiner.getLevel(level).doesFaceFVarTopologyMatch(face, fvc);
}
//
// Patch-level control point retrieval and methods for converting source
// points to a set of local points in a different basis
//
int GetRegularPatchPoints(int level, Index face,
int regBoundaryMask, // compute internally when < 0
Index patchPoints[],
int fvc = -1) const;
template <typename REAL>
int GetIrregularPatchConversionMatrix(int level, Index face,
Vtr::internal::Level::VSpan const cornerSpans[],
SparseMatrix<REAL> & matrix) const;
int GetIrregularPatchSourcePoints(int level, Index face,
Vtr::internal::Level::VSpan const cornerSpans[],
Index sourcePoints[],
int fvc = -1) const;
//
// Queries related to "single-crease" patches -- currently a subset of
// regular interior patches:
//
struct SingleCreaseInfo {
int creaseEdgeInFace;
float creaseSharpness;
};
bool IsRegularSingleCreasePatch(int level, Index face,
SingleCreaseInfo & info) const;
//
// Computing the PatchParam -- note the regrettable dependency on
// PtexIndices but PatchParam is essentially tied to it indefinitely.
// Better to pass it in than have the PatchBuilder build its own
// PtexIndices.
//
// Consider creating a PatchParamFactory which can manage the PtexIndices
// along with this method. It will then be able to generate additional
// data to accelerate these computations.
//
PatchParam ComputePatchParam(int level, Index face,
PtexIndices const& ptexIndices, bool isRegular = true,
int boundaryMask = 0, bool computeTransitionMask = false) const;
protected:
PatchBuilder(TopologyRefiner const& refiner, Options const& options);
// Internal methods supporting topology queries:
int getRegularFacePoints(int level, Index face,
Index patchPoints[], int fvc) const;
int getQuadRegularPatchPoints(int level, Index face,
int regBoundaryMask, Index patchPoints[], int fvc) const;
int getTriRegularPatchPoints(int level, Index face,
int regBoundaryMask, Index patchPoints[], int fvc) const;
// Internal methods using the SourcePatch:
int assembleIrregularSourcePatch(int level, Index face,
Vtr::internal::Level::VSpan const cornerSpans[],
SourcePatch & sourcePatch) const;
int gatherIrregularSourcePoints(int level, Index face,
Vtr::internal::Level::VSpan const cornerSpans[],
SourcePatch & sourcePatch,
Index patchPoints[], int fvc) const;
protected:
//
// Virtual methods to be provided by subclass for each scheme:
//
virtual PatchDescriptor::Type patchTypeFromBasis(BasisType basis) const = 0;
// Note overloading of the conversion for SparseMatrix<REAL>:
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const = 0;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const = 0;
protected:
TopologyRefiner const& _refiner;
Options const _options;
Sdc::SchemeType _schemeType;
int _schemeRegFaceSize;
bool _schemeIsLinear;
PatchDescriptor::Type _regPatchType;
PatchDescriptor::Type _irregPatchType;
PatchDescriptor::Type _nativePatchType;
PatchDescriptor::Type _linearPatchType;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_BUILDER_H */

View File

@@ -0,0 +1,92 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/patchDescriptor.h"
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Lists of valid patch Descriptors for each subdivision scheme
//
// Historically this has only included the non-linear patch types, though
// it is possible for linear patches to represent irregularities for both
// Catmark and Loop, and the Bilinear scheme is adaptively refined into
// linear quads (e.g. a pentagon becoming five quads).
//
ConstPatchDescriptorArray
PatchDescriptor::GetAdaptivePatchDescriptors(Sdc::SchemeType type) {
static PatchDescriptor _loopDescriptors[] = {
PatchDescriptor(LOOP),
PatchDescriptor(GREGORY_TRIANGLE),
};
static PatchDescriptor _catmarkDescriptors[] = {
PatchDescriptor(REGULAR),
PatchDescriptor(GREGORY),
PatchDescriptor(GREGORY_BOUNDARY),
PatchDescriptor(GREGORY_BASIS),
};
switch (type) {
case Sdc::SCHEME_BILINEAR :
return ConstPatchDescriptorArray(0, 0);
case Sdc::SCHEME_CATMARK :
return ConstPatchDescriptorArray(_catmarkDescriptors,
(int)(sizeof(_catmarkDescriptors)/sizeof(PatchDescriptor)));
case Sdc::SCHEME_LOOP :
return ConstPatchDescriptorArray(_loopDescriptors,
(int)(sizeof(_loopDescriptors)/sizeof(PatchDescriptor)));
default:
assert(0);
}
return ConstPatchDescriptorArray(0, 0);;
}
void
PatchDescriptor::print() const {
static char const * types[13] = {
"NON_PATCH", "POINTS", "LINES", "QUADS", "TRIANGLES", "LOOP",
"REGULAR", "GREGORY", "GREGORY_BOUNDARY", "GREGORY_BASIS",
"GREGORY_TRIANGLE"};
printf(" type %s\n",
types[_type]);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,187 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_PATCH_DESCRIPTOR_H
#define OPENSUBDIV3_FAR_PATCH_DESCRIPTOR_H
#include "../version.h"
#include "../far/types.h"
#include "../sdc/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief Describes the type of a patch
///
/// Uniquely identifies all the different types of patches
///
class PatchDescriptor {
public:
enum Type {
NON_PATCH = 0, ///< undefined
POINTS, ///< points (useful for cage drawing)
LINES, ///< lines (useful for cage drawing)
QUADS, ///< 4-sided quadrilateral (bilinear)
TRIANGLES, ///< 3-sided triangle
LOOP, ///< regular triangular patch for the Loop scheme
REGULAR, ///< regular B-Spline patch for the Catmark scheme
GREGORY,
GREGORY_BOUNDARY,
GREGORY_BASIS,
GREGORY_TRIANGLE
};
public:
/// \brief Default constructor.
PatchDescriptor() :
_type(NON_PATCH) { }
/// \brief Constructor
PatchDescriptor(int type) :
_type(type) { }
/// \brief Copy Constructor
PatchDescriptor( PatchDescriptor const & d ) :
_type(d.GetType()) { }
/// \brief Returns the type of the patch
Type GetType() const {
return (Type)_type;
}
/// \brief Returns true if the type is an adaptive (non-linear) patch
static inline bool IsAdaptive(Type type) {
return type > TRIANGLES;
}
/// \brief Returns true if the type is an adaptive patch
bool IsAdaptive() const {
return IsAdaptive( this->GetType() );
}
/// \brief Returns the number of control vertices expected for a patch of the
/// type described
static inline short GetNumControlVertices( Type t );
/// \brief Deprecated @see PatchDescriptor#GetNumControlVertices
static inline short GetNumFVarControlVertices( Type t );
/// \brief Returns the number of control vertices expected for a patch of the
/// type described
short GetNumControlVertices() const {
return GetNumControlVertices( this->GetType() );
}
/// \brief Deprecated @see PatchDescriptor#GetNumControlVertices
short GetNumFVarControlVertices() const {
return GetNumFVarControlVertices( this->GetType() );
}
/// \brief Number of control vertices of Regular Patches in table.
static short GetRegularPatchSize() { return 16; }
/// \brief Number of control vertices of Gregory (and Gregory Boundary) Patches in table.
static short GetGregoryPatchSize() { return 4; }
/// \brief Number of control vertices of Gregory patch basis (20)
static short GetGregoryBasisPatchSize() { return 20; }
/// \brief Returns a vector of all the legal patch descriptors for the
/// given adaptive subdivision scheme
static Vtr::ConstArray<PatchDescriptor> GetAdaptivePatchDescriptors(Sdc::SchemeType type);
/// \brief Allows ordering of patches by type
inline bool operator < ( PatchDescriptor const other ) const;
/// \brief True if the descriptors are identical
inline bool operator == ( PatchDescriptor const other ) const;
// debug helper
void print() const;
private:
unsigned int _type;
};
typedef Vtr::ConstArray<PatchDescriptor> ConstPatchDescriptorArray;
// Returns the number of control vertices expected for a patch of this type
inline short
PatchDescriptor::GetNumControlVertices( Type type ) {
switch (type) {
case REGULAR : return GetRegularPatchSize();
case LOOP : return 12;
case QUADS : return 4;
case GREGORY :
case GREGORY_BOUNDARY : return GetGregoryPatchSize();
case GREGORY_BASIS : return GetGregoryBasisPatchSize();
case GREGORY_TRIANGLE : return 18;
case TRIANGLES : return 3;
case LINES : return 2;
case POINTS : return 1;
default : return -1;
}
}
// Returns the number of face-varying control vertices expected for a patch of this type
inline short
PatchDescriptor::GetNumFVarControlVertices( Type type ) {
return PatchDescriptor::GetNumControlVertices(type);
}
// Allows ordering of patches by type
inline bool
PatchDescriptor::operator < ( PatchDescriptor const other ) const {
return (_type < other._type);
}
// True if the descriptors are identical
inline bool
PatchDescriptor::operator == ( PatchDescriptor const other ) const {
return _type == other._type;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_DESCRIPTOR_H */

View File

@@ -0,0 +1,207 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/patchMap.h"
#include <algorithm>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Inline quadtree assembly methods used by the constructor:
//
// sets all the children to point to the patch of given index
inline void
PatchMap::QuadNode::SetChildren(int index) {
for (int i=0; i<4; ++i) {
children[i].isSet = true;
children[i].isLeaf = true;
children[i].index = index;
}
}
// sets the child in "quadrant" to point to the node or patch of the given index
inline void
PatchMap::QuadNode::SetChild(int quadrant, int index, bool isLeaf) {
assert(!children[quadrant].isSet);
children[quadrant].isSet = true;
children[quadrant].isLeaf = isLeaf;
children[quadrant].index = index;
}
inline void
PatchMap::assignRootNode(QuadNode * node, int index) {
// Assign the given index to all children of the node (all leaves)
node->SetChildren(index);
}
inline PatchMap::QuadNode *
PatchMap::assignLeafOrChildNode(QuadNode * node, bool isLeaf, int quadrant, int index) {
// Assign the node given if it is a leaf node, otherwise traverse
// the node -- creating/assigning a new child node if needed
if (isLeaf) {
node->SetChild(quadrant, index, true);
return node;
}
if (node->children[quadrant].isSet) {
return &_quadtree[node->children[quadrant].index];
} else {
int newChildNodeIndex = (int)_quadtree.size();
_quadtree.push_back(QuadNode());
node->SetChild(quadrant, newChildNodeIndex, false);
return &_quadtree[newChildNodeIndex];
}
}
//
// Constructor and initialization methods for the handles and quadtree:
//
PatchMap::PatchMap(PatchTable const & patchTable) :
_minPatchFace(-1), _maxPatchFace(-1), _maxDepth(0) {
_patchesAreTriangular =
patchTable.GetVaryingPatchDescriptor().GetNumControlVertices() == 3;
if (patchTable.GetNumPatchesTotal() > 0) {
initializeHandles(patchTable);
initializeQuadtree(patchTable);
}
}
void
PatchMap::initializeHandles(PatchTable const & patchTable) {
//
// Populate the vector of patch Handles. Keep track of the min and max
// face indices to allocate resources accordingly and limit queries:
//
_minPatchFace = (int) patchTable.GetPatchParamTable()[0].GetFaceId();
_maxPatchFace = _minPatchFace;
int numArrays = (int) patchTable.GetNumPatchArrays();
int numPatches = (int) patchTable.GetNumPatchesTotal();
_handles.resize(numPatches);
for (int pArray = 0, handleIndex = 0; pArray < numArrays; ++pArray) {
ConstPatchParamArray params = patchTable.GetPatchParams(pArray);
int patchSize = patchTable.GetPatchArrayDescriptor(pArray).GetNumControlVertices();
for (Index j=0; j < patchTable.GetNumPatches(pArray); ++j, ++handleIndex) {
Handle & h = _handles[handleIndex];
h.arrayIndex = pArray;
h.patchIndex = handleIndex;
h.vertIndex = j * patchSize;
int patchFaceId = params[j].GetFaceId();
_minPatchFace = std::min(_minPatchFace, patchFaceId);
_maxPatchFace = std::max(_maxPatchFace, patchFaceId);
}
}
}
void
PatchMap::initializeQuadtree(PatchTable const & patchTable) {
//
// Reserve quadtree nodes for the worst case and prune later. Set the
// initial size to accomodate the root node of each patch face:
//
int nPatchFaces = (_maxPatchFace - _minPatchFace) + 1;
int nHandles = (int)_handles.size();
_quadtree.reserve(nPatchFaces + nHandles);
_quadtree.resize(nPatchFaces);
PatchParamTable const & params = patchTable.GetPatchParamTable();
for (int handle = 0; handle < nHandles; ++handle) {
PatchParam const & param = params[handle];
int depth = param.GetDepth();
int rootDepth = param.NonQuadRoot();
_maxDepth = std::max(_maxDepth, depth);
QuadNode * node = &_quadtree[param.GetFaceId() - _minPatchFace];
if (depth == rootDepth) {
assignRootNode(node, handle);
continue;
}
if (!_patchesAreTriangular) {
// Use the UV bits of the PatchParam directly for quad patches:
int u = param.GetU();
int v = param.GetV();
for (int j = rootDepth + 1; j <= depth; ++j) {
int uBit = (u >> (depth - j)) & 1;
int vBit = (v >> (depth - j)) & 1;
int quadrant = (vBit << 1) | uBit;
node = assignLeafOrChildNode(node, (j == depth), quadrant, handle);
}
} else {
// Use an interior UV point of triangles to identify quadrants:
double u = 0.25;
double v = 0.25;
param.UnnormalizeTriangle(u, v);
double median = 0.5;
bool triRotated = false;
for (int j = rootDepth + 1; j <= depth; ++j, median *= 0.5) {
int quadrant = transformUVToTriQuadrant(median, u, v, triRotated);
node = assignLeafOrChildNode(node, (j == depth), quadrant, handle);
}
}
}
// Swap the Node vector with a copy to reduce worst case memory allocation:
QuadTree tmpTree = _quadtree;
_quadtree.swap(tmpTree);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,240 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_PATCH_MAP_H
#define OPENSUBDIV3_FAR_PATCH_MAP_H
#include "../version.h"
#include "../far/patchTable.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief An quadtree-based map connecting coarse faces to their sub-patches
///
/// PatchTable::PatchArrays contain lists of patches that represent the limit
/// surface of a mesh, sorted by their topological type. These arrays break the
/// connection between coarse faces and their sub-patches.
///
/// The PatchMap provides a quad-tree based lookup structure that, given a singular
/// parametric location, can efficiently return a handle to the sub-patch that
/// contains this location.
///
class PatchMap {
public:
typedef PatchTable::PatchHandle Handle;
/// \brief Constructor
///
/// @param patchTable A valid PatchTable
///
PatchMap( PatchTable const & patchTable );
/// \brief Returns a handle to the sub-patch of the face at the given (u,v).
/// Note that the patch face ID corresponds to potentially quadrangulated
/// face indices and not the base face indices (see Far::PtexIndices for more
/// details).
///
/// @param patchFaceId The index of the patch (Ptex) face
///
/// @param u Local u parameter
///
/// @param v Local v parameter
///
/// @return A patch handle or 0 if the face is not supported (index
/// out of bounds) or is tagged as a hole
///
Handle const * FindPatch( int patchFaceId, double u, double v ) const;
private:
void initializeHandles(PatchTable const & patchTable);
void initializeQuadtree(PatchTable const & patchTable);
private:
// Quadtree node with 4 children, tree is just a vector of nodes
struct QuadNode {
QuadNode() { std::memset(this, 0, sizeof(QuadNode)); }
struct Child {
unsigned int isSet : 1; // true if the child has been set
unsigned int isLeaf : 1; // true if the child is a QuadNode
unsigned int index : 30; // child index (either QuadNode or Handle)
};
// sets all the children to point to the patch of given index
void SetChildren(int index);
// sets the child in "quadrant" to point to the node or patch of the given index
void SetChild(int quadrant, int index, bool isLeaf);
Child children[4];
};
typedef std::vector<QuadNode> QuadTree;
// Internal methods supporting quadtree construction and queries
void assignRootNode(QuadNode * node, int index);
QuadNode * assignLeafOrChildNode(QuadNode * node, bool isLeaf, int quad, int index);
template <class T>
static int transformUVToQuadQuadrant(T const & median, T & u, T & v);
template <class T>
static int transformUVToTriQuadrant(T const & median, T & u, T & v, bool & rotated);
private:
bool _patchesAreTriangular; // tri and quad assembly and search requirements differ
int _minPatchFace; // minimum patch face index supported by the map
int _maxPatchFace; // maximum patch face index supported by the map
int _maxDepth; // maximum depth of a patch in the tree
std::vector<Handle> _handles; // all the patches in the PatchTable
std::vector<QuadNode> _quadtree; // quadtree nodes
};
//
// Given a median value for both U and V, these methods transform a (u,v) pair
// into the quadrant that contains them and returns the quadrant index.
//
// Quadrant indexing for tri and quad patches -- consistent with PatchParam's
// usage of UV bits:
//
// (0,1) o-----o-----o (1,1) (0,1) o (1,0) o-----o-----o (0,0)
// | | | |\ \ 1 |\ 0 |
// | 2 | 3 | | \ \ | \ |
// | | | | 2 \ \| 3 \|
// o-----o-----o o-----o o-----o
// | | | |\ 3 |\ \ 2 |
// | 0 | 1 | | \ | \ \ |
// | | | | 0 \| 1 \ \|
// (0,0) o-----o-----o (1,0) (0,0) o-----o-----o (1,0) o (0,1)
//
// The triangular case also takes and returns/affects the rotation of the
// quadrant being searched and identified (quadrant 3 imparts a rotation).
//
template <class T>
inline int
PatchMap::transformUVToQuadQuadrant(T const & median, T & u, T & v) {
int uHalf = (u >= median);
if (uHalf) u -= median;
int vHalf = (v >= median);
if (vHalf) v -= median;
return (vHalf << 1) | uHalf;
}
template <class T>
int inline
PatchMap::transformUVToTriQuadrant(T const & median, T & u, T & v, bool & rotated) {
if (!rotated) {
if (u >= median) {
u -= median;
return 1;
}
if (v >= median) {
v -= median;
return 2;
}
if ((u + v) >= median) {
rotated = true;
return 3;
}
return 0;
} else {
if (u < median) {
v -= median;
return 1;
}
if (v < median) {
u -= median;
return 2;
}
u -= median;
v -= median;
if ((u + v) < median) {
rotated = false;
return 3;
}
return 0;
}
}
/// Returns a handle to the sub-patch of the face at the given (u,v).
inline PatchMap::Handle const *
PatchMap::FindPatch( int faceid, double u, double v ) const {
//
// Reject patch faces not supported by this map, or those corresponding
// to holes or otherwise unassigned (the root node for a patch will
// have all or no quadrants set):
//
if ((faceid < _minPatchFace) || (faceid > _maxPatchFace)) return 0;
QuadNode const * node = &_quadtree[faceid - _minPatchFace];
if (!node->children[0].isSet) return 0;
//
// Search the tree for the sub-patch containing the given (u,v)
//
assert( (u>=0.0) && (u<=1.0) && (v>=0.0) && (v<=1.0) );
double median = 0.5;
bool triRotated = false;
for (int depth = 0; depth <= _maxDepth; ++depth, median *= 0.5) {
int quadrant = _patchesAreTriangular
? transformUVToTriQuadrant(median, u, v, triRotated)
: transformUVToQuadQuadrant(median, u, v);
// holes should have been rejected at the root node of the face
assert(node->children[quadrant].isSet);
if (node->children[quadrant].isLeaf) {
return &_handles[node->children[quadrant].index];
} else {
node = &_quadtree[node->children[quadrant].index];
}
}
assert(0);
return 0;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_PARAM */

View File

@@ -0,0 +1,332 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_PATCH_PARAM_H
#define OPENSUBDIV3_FAR_PATCH_PARAM_H
#include "../version.h"
#include "../far/types.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief Patch parameterization
///
/// Topological refinement splits coarse mesh faces into refined faces.
///
/// This patch parameterzation describes the relationship between one
/// of these refined faces and its corresponding coarse face. It is used
/// both for refined faces that are represented as full limit surface
/// parametric patches as well as for refined faces represented as simple
/// triangles or quads. This parameterization is needed to interpolate
/// primvar data across a refined face.
///
/// The U,V and refinement level parameters describe the scale and offset
/// needed to map a location on the patch between levels of refinement.
/// The encoding of these values exploits the quad-tree organization of
/// the faces produced by subdivision. We encode the U,V origin of the
/// patch using two 10-bit integer values and the refinement level as
/// a 4-bit integer. This is sufficient to represent up through 10 levels
/// of refinement.
///
/// Special consideration must be given to the refined faces resulting from
/// irregular coarse faces. We adopt a convention similar to Ptex texture
/// mapping and define the parameterization for these faces in terms of the
/// regular faces resulting from the first topological splitting of the
/// irregular coarse face.
///
/// When computing the basis functions needed to evaluate the limit surface
/// parametric patch representing a refined face, we also need to know which
/// edges of the patch are interpolated boundaries. These edges are encoded
/// as a boundary bitmask identifying the boundary edges of the patch in
/// sequential order starting from the first vertex of the refined face.
///
/// A sparse topological refinement (like feature adaptive refinement) can
/// produce refined faces that are adjacent to faces at the next level of
/// subdivision. We identify these transitional edges with a transition
/// bitmask using the same encoding as the boundary bitmask.
///
/// For triangular subdivision schemes we specify the parameterization using
/// a similar method. Alternate triangles at a given level of refinement
/// are parameterized from their opposite corners and encoded as occupying
/// the opposite diagonal of the quad-tree hierarchy. The third barycentric
/// coordinate is dependent on and can be derived from the other two
/// coordinates. This encoding also takes inspiration from the Ptex
/// texture mapping specification.
///
/// Bitfield layout :
///
/// Field0 | Bits | Content
/// -----------|:----:|------------------------------------------------------
/// faceId | 28 | the faceId of the patch
/// transition | 4 | transition edge mask encoding
///
/// Field1 | Bits | Content
/// -----------|:----:|------------------------------------------------------
/// level | 4 | the subdivision level of the patch
/// nonquad | 1 | whether patch is refined from a non-quad face
/// regular | 1 | whether patch is regular
/// unused | 1 | unused
/// boundary | 5 | boundary edge mask encoding
/// v | 10 | log2 value of u parameter at first patch corner
/// u | 10 | log2 value of v parameter at first patch corner
///
/// Note : the bitfield is not expanded in the struct due to differences in how
/// GPU & CPU compilers pack bit-fields and endian-ness.
///
/*!
\verbatim
Quad Patch Parameterization
(0,1) (1,1)
+-------+-------+---------------+
| | | |
| L2 | L2 | |
|0,3 |1,3 | |
+-------+-------+ L1 |
| | | |
| L2 | L2 | |
|0,2 |1,2 |1,1 |
+-------+-------+---------------+
| | |
| | |
| | |
| L1 | L1 |
| | |
| | |
|0,0 |1,0 |
+---------------+---------------+
(0,0) (1,0)
\endverbatim
*/
/*!
\verbatim
Triangle Patch Parameterization
(0,1) (1,1) (0,1,0)
+-------+-------+---------------+ +
| \ | \ | \ | | \
|L2 \ |L2 \ | \ | | \
|0,3 \ |1,3 \ | \ | | L2 \
+-------+-------+ \ | +-------+
| \ | \ | L1 \ | | \ L2 | \
|L2 \ |L2 \ | \ | | \ | \
|0,2 \ |1,2 \ |1,1 \ | | L2 \ | L2 \
+-------+-------+---------------+ +-------+-------+
| \ | \ | | \ | \
| \ | \ | | \ | \
| \ | \ | | \ L1 | \
| \ | \ | | \ | \
| L1 \ | L1 \ | | L1 \ | L1 \
| \ | \ | | \ | \
|0,0 \ |1,0 \ | | \ | \
+---------------+---------------+ +---------------+---------------+
(0,0) (1,0) (0,0,1) (1,0,0)
\endverbatim
*/
struct PatchParam {
/// \brief Sets the values of the bit fields
///
/// @param faceid face index
///
/// @param u value of the u parameter for the first corner of the face
/// @param v value of the v parameter for the first corner of the face
///
/// @param depth subdivision level of the patch
/// @param nonquad true if the root face is not a quad
///
/// @param boundary 5-bits identifying boundary edges (and verts for tris)
/// @param transition 4-bits identifying transition edges
///
/// @param regular whether the patch is regular
///
void Set(Index faceid, short u, short v,
unsigned short depth, bool nonquad,
unsigned short boundary, unsigned short transition,
bool regular = false);
/// \brief Resets everything to 0
void Clear() { field0 = field1 = 0; }
/// \brief Returns the faceid
Index GetFaceId() const { return Index(unpack(field0,28,0)); }
/// \brief Returns the log2 value of the u parameter at
/// the first corner of the patch
unsigned short GetU() const { return (unsigned short)unpack(field1,10,22); }
/// \brief Returns the log2 value of the v parameter at
/// the first corner of the patch
unsigned short GetV() const { return (unsigned short)unpack(field1,10,12); }
/// \brief Returns the transition edge encoding for the patch.
unsigned short GetTransition() const { return (unsigned short)unpack(field0,4,28); }
/// \brief Returns the boundary edge encoding for the patch.
unsigned short GetBoundary() const { return (unsigned short)unpack(field1,5,7); }
/// \brief True if the parent base face is a non-quad
bool NonQuadRoot() const { return (unpack(field1,1,4) != 0); }
/// \brief Returns the level of subdivision of the patch
unsigned short GetDepth() const { return (unsigned short)unpack(field1,4,0); }
/// \brief Returns the fraction of unit parametric space covered by this face.
float GetParamFraction() const;
/// \brief A (u,v) pair in the fraction of parametric space covered by this
/// face is mapped into a normalized parametric space.
///
/// @param u u parameter
/// @param v v parameter
///
template <typename REAL>
void Normalize( REAL & u, REAL & v ) const;
template <typename REAL>
void NormalizeTriangle( REAL & u, REAL & v ) const;
/// \brief A (u,v) pair in a normalized parametric space is mapped back into the
/// fraction of parametric space covered by this face.
///
/// @param u u parameter
/// @param v v parameter
///
template <typename REAL>
void Unnormalize( REAL & u, REAL & v ) const;
template <typename REAL>
void UnnormalizeTriangle( REAL & u, REAL & v ) const;
/// \brief Returns if a triangular patch is parametrically rotated 180 degrees
bool IsTriangleRotated() const;
/// \brief Returns whether the patch is regular
bool IsRegular() const { return (unpack(field1,1,5) != 0); }
unsigned int field0:32;
unsigned int field1:32;
private:
unsigned int pack(unsigned int value, int width, int offset) const {
return (unsigned int)((value & ((1<<width)-1)) << offset);
}
unsigned int unpack(unsigned int value, int width, int offset) const {
return (unsigned int)((value >> offset) & ((1<<width)-1));
}
};
typedef std::vector<PatchParam> PatchParamTable;
typedef Vtr::Array<PatchParam> PatchParamArray;
typedef Vtr::ConstArray<PatchParam> ConstPatchParamArray;
inline void
PatchParam::Set(Index faceid, short u, short v,
unsigned short depth, bool nonquad,
unsigned short boundary, unsigned short transition,
bool regular) {
field0 = pack(faceid, 28, 0) |
pack(transition, 4, 28);
field1 = pack(u, 10, 22) |
pack(v, 10, 12) |
pack(boundary, 5, 7) |
pack(regular, 1, 5) |
pack(nonquad, 1, 4) |
pack(depth, 4, 0);
}
inline float
PatchParam::GetParamFraction( ) const {
return 1.0f / (float)(1 << (GetDepth() - NonQuadRoot()));
}
template <typename REAL>
inline void
PatchParam::Normalize( REAL & u, REAL & v ) const {
REAL fracInv = (REAL)(1.0f / GetParamFraction());
u = u * fracInv - (REAL)GetU();
v = v * fracInv - (REAL)GetV();
}
template <typename REAL>
inline void
PatchParam::Unnormalize( REAL & u, REAL & v ) const {
REAL frac = (REAL)GetParamFraction();
u = (u + (REAL)GetU()) * frac;
v = (v + (REAL)GetV()) * frac;
}
inline bool
PatchParam::IsTriangleRotated() const {
return (GetU() + GetV()) >= (1 << GetDepth());
}
template <typename REAL>
inline void
PatchParam::NormalizeTriangle( REAL & u, REAL & v ) const {
if (IsTriangleRotated()) {
REAL fracInv = (REAL)(1.0f / GetParamFraction());
int depthFactor = 1 << GetDepth();
u = (REAL)(depthFactor - GetU()) - (u * fracInv);
v = (REAL)(depthFactor - GetV()) - (v * fracInv);
} else {
Normalize(u, v);
}
}
template <typename REAL>
inline void
PatchParam::UnnormalizeTriangle( REAL & u, REAL & v ) const {
if (IsTriangleRotated()) {
REAL frac = GetParamFraction();
int depthFactor = 1 << GetDepth();
u = ((REAL)(depthFactor - GetU()) - u) * frac;
v = ((REAL)(depthFactor - GetV()) - v) * frac;
} else {
Unnormalize(u, v);
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_PARAM */

View File

@@ -0,0 +1,708 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/patchTable.h"
#include "../far/patchBasis.h"
#include <algorithm>
#include <cstring>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
PatchTable::PatchTable(int maxvalence) :
_maxValence(maxvalence),
_localPointStencils(),
_localPointVaryingStencils(),
_varyingDesc(Far::PatchDescriptor::QUADS),
_isUniformLinear(false),
_vertexPrecisionIsDouble(false),
_varyingPrecisionIsDouble(false),
_faceVaryingPrecisionIsDouble(false) {
}
// Copy constructor
// XXXX manuelk we need to eliminate this constructor (C++11 smart pointers)
PatchTable::PatchTable(PatchTable const & src) :
_maxValence(src._maxValence),
_numPtexFaces(src._numPtexFaces),
_patchArrays(src._patchArrays),
_patchVerts(src._patchVerts),
_paramTable(src._paramTable),
_quadOffsetsTable(src._quadOffsetsTable),
_vertexValenceTable(src._vertexValenceTable),
_localPointStencils(src._localPointStencils),
_localPointVaryingStencils(src._localPointVaryingStencils),
_varyingDesc(src._varyingDesc),
_fvarChannels(src._fvarChannels),
_sharpnessIndices(src._sharpnessIndices),
_sharpnessValues(src._sharpnessValues),
_isUniformLinear(src._isUniformLinear),
_vertexPrecisionIsDouble(src._vertexPrecisionIsDouble),
_varyingPrecisionIsDouble(src._varyingPrecisionIsDouble),
_faceVaryingPrecisionIsDouble(src._faceVaryingPrecisionIsDouble) {
if (src._localPointStencils) {
if (src._vertexPrecisionIsDouble) {
_localPointStencils.Set(
new StencilTableReal<double>(*src._localPointStencils.Get<double>()));
} else {
_localPointStencils.Set(
new StencilTableReal<float>(*src._localPointStencils.Get<float>()));
}
}
if (src._localPointVaryingStencils) {
if (src._varyingPrecisionIsDouble) {
_localPointVaryingStencils.Set(
new StencilTableReal<double>(*src._localPointVaryingStencils.Get<double>()));
} else {
_localPointVaryingStencils.Set(
new StencilTableReal<float>(*src._localPointVaryingStencils.Get<float>()));
}
}
if (! src._localPointFaceVaryingStencils.empty()) {
_localPointFaceVaryingStencils.resize(src._localPointFaceVaryingStencils.size());
for (int fvc=0; fvc<(int)_localPointFaceVaryingStencils.size(); ++fvc) {
if (src._localPointFaceVaryingStencils[fvc]) {
if (src._faceVaryingPrecisionIsDouble) {
_localPointFaceVaryingStencils[fvc].Set(new StencilTableReal<double>(
*src._localPointFaceVaryingStencils[fvc].Get<double>()));
} else {
_localPointFaceVaryingStencils[fvc].Set(new StencilTableReal<float>(
*src._localPointFaceVaryingStencils[fvc].Get<float>()));
}
}
}
}
}
PatchTable::~PatchTable() {
if (_vertexPrecisionIsDouble) {
delete _localPointStencils.Get<double>();
} else {
delete _localPointStencils.Get<float>();
}
if (_varyingPrecisionIsDouble) {
delete _localPointVaryingStencils.Get<double>();
} else {
delete _localPointVaryingStencils.Get<float>();
}
for (int fvc=0; fvc<(int)_localPointFaceVaryingStencils.size(); ++fvc) {
if (_faceVaryingPrecisionIsDouble) {
delete _localPointFaceVaryingStencils[fvc].Get<double>();
} else {
delete _localPointFaceVaryingStencils[fvc].Get<float>();
}
}
}
//
// PatchArrays
//
struct PatchTable::PatchArray {
PatchArray(PatchDescriptor d, int np, Index v, Index p, Index qo) :
desc(d), numPatches(np), vertIndex(v),
patchIndex(p), quadOffsetIndex (qo) { }
void print() const;
PatchDescriptor desc; // type of patches in the array
int numPatches; // number of patches in the array
Index vertIndex, // index to the first control vertex
patchIndex, // absolute index of the first patch in the array
quadOffsetIndex; // index of the first quad offset entry
};
// debug helper
void
PatchTable::PatchArray::print() const {
desc.print();
printf(" numPatches=%d vertIndex=%d patchIndex=%d "
"quadOffsetIndex=%d\n", numPatches, vertIndex, patchIndex,
quadOffsetIndex);
}
inline PatchTable::PatchArray &
PatchTable::getPatchArray(Index arrayIndex) {
assert(arrayIndex<(Index)GetNumPatchArrays());
return _patchArrays[arrayIndex];
}
inline PatchTable::PatchArray const &
PatchTable::getPatchArray(Index arrayIndex) const {
assert(arrayIndex<(Index)GetNumPatchArrays());
return _patchArrays[arrayIndex];
}
void
PatchTable::reservePatchArrays(int numPatchArrays) {
_patchArrays.reserve(numPatchArrays);
}
//
// FVarPatchChannel
//
// Stores a record for each patch in the primitive :
//
// - Each patch in the PatchTable has a corresponding patch in each
// face-varying patch channel. Patch vertex indices are sorted in the same
// patch-type order as PatchTable::PTables. Face-varying data for a patch
// can therefore be quickly accessed by using the patch primitive ID as
// index into patchValueOffsets to locate the face-varying control vertex
// indices.
//
// - Face-varying channels can have a different interpolation modes
//
// - Unlike "vertex" patches, there are no transition masks required
// for face-varying patches.
//
// - Face-varying patches still require boundary edge masks.
//
// - currently most patches with sharp boundaries but smooth interiors have
// to be isolated to level 10 : we need a special type of bicubic patch
// similar to single-crease to resolve this condition without requiring
// isolation if possible
//
struct PatchTable::FVarPatchChannel {
Sdc::Options::FVarLinearInterpolation interpolation;
PatchDescriptor regDesc;
PatchDescriptor irregDesc;
int stride;
std::vector<Index> patchValues;
std::vector<PatchParam> patchParam;
};
void
PatchTable::allocateVaryingVertices(
PatchDescriptor desc, int numPatches) {
_varyingDesc = desc;
_varyingVerts.resize(numPatches*desc.GetNumControlVertices());
}
inline PatchTable::FVarPatchChannel &
PatchTable::getFVarPatchChannel(int channel) {
assert(channel>=0 && channel<(int)_fvarChannels.size());
return _fvarChannels[channel];
}
inline PatchTable::FVarPatchChannel const &
PatchTable::getFVarPatchChannel(int channel) const {
assert(channel>=0 && channel<(int)_fvarChannels.size());
return _fvarChannels[channel];
}
void
PatchTable::allocateFVarPatchChannels(int numChannels) {
_fvarChannels.resize(numChannels);
}
void
PatchTable::allocateFVarPatchChannelValues(
PatchDescriptor regDesc, PatchDescriptor irregDesc,
int numPatches, int channel) {
FVarPatchChannel & c = getFVarPatchChannel(channel);
c.regDesc = regDesc;
c.irregDesc = irregDesc;
c.stride = std::max(regDesc.GetNumControlVertices(),
irregDesc.GetNumControlVertices());
c.patchValues.resize(numPatches * c.stride);
c.patchParam.resize(numPatches);
}
void
PatchTable::setFVarPatchChannelLinearInterpolation(
Sdc::Options::FVarLinearInterpolation interpolation, int channel) {
FVarPatchChannel & c = getFVarPatchChannel(channel);
c.interpolation = interpolation;
}
//
// PatchTable
//
inline int
getPatchSize(PatchDescriptor desc) {
return desc.GetNumControlVertices();
}
void
PatchTable::pushPatchArray(PatchDescriptor desc, int npatches,
Index * vidx, Index * pidx, Index * qoidx) {
if (npatches>0) {
_patchArrays.push_back(PatchArray(
desc, npatches, *vidx, *pidx, qoidx ? *qoidx : 0));
int nverts = getPatchSize(desc);
*vidx += npatches * nverts;
*pidx += npatches;
if (qoidx) {
*qoidx += (desc.GetType() == PatchDescriptor::GREGORY) ?
npatches*nverts : 0;
}
}
}
int
PatchTable::getPatchIndex(int arrayIndex, int patchIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
assert(patchIndex<pa.numPatches);
return pa.patchIndex + patchIndex;
}
Index *
PatchTable::getSharpnessIndices(int arrayIndex) {
return &_sharpnessIndices[getPatchArray(arrayIndex).patchIndex];
}
float *
PatchTable::getSharpnessValues(int arrayIndex) {
return &_sharpnessValues[getPatchArray(arrayIndex).patchIndex];
}
PatchDescriptor
PatchTable::GetPatchDescriptor(PatchHandle const & handle) const {
return getPatchArray(handle.arrayIndex).desc;
}
PatchDescriptor
PatchTable::GetPatchArrayDescriptor(int arrayIndex) const {
return getPatchArray(arrayIndex).desc;
}
int
PatchTable::GetNumPatchArrays() const {
return (int)_patchArrays.size();
}
int
PatchTable::GetNumPatches(int arrayIndex) const {
return getPatchArray(arrayIndex).numPatches;
}
int
PatchTable::GetNumPatchesTotal() const {
// there is one PatchParam record for each patch in the mesh
return (int)_paramTable.size();
}
int
PatchTable::GetNumControlVertices(int arrayIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
return pa.numPatches * getPatchSize(pa.desc);
}
Index
PatchTable::findPatchArray(PatchDescriptor desc) {
for (int i=0; i<(int)_patchArrays.size(); ++i) {
if (_patchArrays[i].desc==desc)
return i;
}
return Vtr::INDEX_INVALID;
}
IndexArray
PatchTable::getPatchArrayVertices(int arrayIndex) {
PatchArray const & pa = getPatchArray(arrayIndex);
int size = getPatchSize(pa.desc);
assert(pa.vertIndex<(Index)_patchVerts.size());
return IndexArray(&_patchVerts[pa.vertIndex], pa.numPatches * size);
}
ConstIndexArray
PatchTable::GetPatchArrayVertices(int arrayIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
int size = getPatchSize(pa.desc);
assert(pa.vertIndex<(Index)_patchVerts.size());
return ConstIndexArray(&_patchVerts[pa.vertIndex], pa.numPatches * size);
}
ConstIndexArray
PatchTable::GetPatchVertices(PatchHandle const & handle) const {
PatchArray const & pa = getPatchArray(handle.arrayIndex);
Index vert = pa.vertIndex + handle.vertIndex;
return ConstIndexArray(&_patchVerts[vert], getPatchSize(pa.desc));
}
ConstIndexArray
PatchTable::GetPatchVertices(int arrayIndex, int patchIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
int size = getPatchSize(pa.desc);
assert((pa.vertIndex + patchIndex*size)<(Index)_patchVerts.size());
return ConstIndexArray(&_patchVerts[pa.vertIndex + patchIndex*size], size);
}
PatchParam
PatchTable::GetPatchParam(PatchHandle const & handle) const {
assert(handle.patchIndex < (Index)_paramTable.size());
return _paramTable[handle.patchIndex];
}
PatchParam
PatchTable::GetPatchParam(int arrayIndex, int patchIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
assert((pa.patchIndex + patchIndex) < (int)_paramTable.size());
return _paramTable[pa.patchIndex + patchIndex];
}
PatchParamArray
PatchTable::getPatchParams(int arrayIndex) {
PatchArray const & pa = getPatchArray(arrayIndex);
return PatchParamArray(&_paramTable[pa.patchIndex], pa.numPatches);
}
ConstPatchParamArray const
PatchTable::GetPatchParams(int arrayIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
return ConstPatchParamArray(&_paramTable[pa.patchIndex], pa.numPatches);
}
float
PatchTable::GetSingleCreasePatchSharpnessValue(PatchHandle const & handle) const {
assert((handle.patchIndex) < (int)_sharpnessIndices.size());
Index index = _sharpnessIndices[handle.patchIndex];
if (index == Vtr::INDEX_INVALID) {
return 0.0f;
}
assert(index < (Index)_sharpnessValues.size());
return _sharpnessValues[index];
}
float
PatchTable::GetSingleCreasePatchSharpnessValue(int arrayIndex, int patchIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
assert((pa.patchIndex + patchIndex) < (int)_sharpnessIndices.size());
Index index = _sharpnessIndices[pa.patchIndex + patchIndex];
if (index == Vtr::INDEX_INVALID) {
return 0.0f;
}
assert(index < (Index)_sharpnessValues.size());
return _sharpnessValues[index];
}
int
PatchTable::GetNumLocalPoints() const {
if (!_localPointStencils) return 0;
return _vertexPrecisionIsDouble
? _localPointStencils.Get<double>()->GetNumStencils()
: _localPointStencils.Get<float>()->GetNumStencils();
}
int
PatchTable::GetNumLocalPointsVarying() const {
if (!_localPointVaryingStencils) return 0;
return _varyingPrecisionIsDouble
? _localPointVaryingStencils.Get<double>()->GetNumStencils()
: _localPointVaryingStencils.Get<float>()->GetNumStencils();
}
int
PatchTable::GetNumLocalPointsFaceVarying(int channel) const {
if (channel>=0 && channel<(int)_localPointFaceVaryingStencils.size()) {
if (!_localPointFaceVaryingStencils[channel]) return 0;
return _faceVaryingPrecisionIsDouble
? _localPointFaceVaryingStencils[channel].Get<double>()->GetNumStencils()
: _localPointFaceVaryingStencils[channel].Get<float>()->GetNumStencils();
}
return 0;
}
PatchTable::ConstQuadOffsetsArray
PatchTable::GetPatchQuadOffsets(PatchHandle const & handle) const {
PatchArray const & pa = getPatchArray(handle.arrayIndex);
return Vtr::ConstArray<unsigned int>(&_quadOffsetsTable[pa.quadOffsetIndex + handle.vertIndex], 4);
}
bool
PatchTable::IsFeatureAdaptive() const {
return !_isUniformLinear;
}
PatchDescriptor
PatchTable::GetVaryingPatchDescriptor() const {
return _varyingDesc;
}
ConstIndexArray
PatchTable::GetPatchVaryingVertices(PatchHandle const & handle) const {
if (_varyingVerts.empty()) {
return ConstIndexArray();
}
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
Index start = handle.patchIndex * numVaryingCVs;
return ConstIndexArray(&_varyingVerts[start], numVaryingCVs);
}
ConstIndexArray
PatchTable::GetPatchVaryingVertices(int array, int patch) const {
if (_varyingVerts.empty()) {
return ConstIndexArray();
}
PatchArray const & pa = getPatchArray(array);
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
Index start = (pa.patchIndex + patch) * numVaryingCVs;
return ConstIndexArray(&_varyingVerts[start], numVaryingCVs);
}
ConstIndexArray
PatchTable::GetPatchArrayVaryingVertices(int array) const {
if (_varyingVerts.empty()) {
return ConstIndexArray();
}
PatchArray const & pa = getPatchArray(array);
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
Index start = pa.patchIndex * numVaryingCVs;
Index count = pa.numPatches * numVaryingCVs;
return ConstIndexArray(&_varyingVerts[start], count);
}
ConstIndexArray
PatchTable::GetVaryingVertices() const {
if (_varyingVerts.empty()) {
return ConstIndexArray();
}
return ConstIndexArray(&_varyingVerts[0], (int)_varyingVerts.size());
}
IndexArray
PatchTable::getPatchArrayVaryingVertices(int arrayIndex) {
PatchArray const & pa = getPatchArray(arrayIndex);
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
Index start = pa.patchIndex * numVaryingCVs;
return IndexArray(&_varyingVerts[start], pa.numPatches * numVaryingCVs);
}
void
PatchTable::populateVaryingVertices() {
// In order to support evaluation of varying data we need to access
// the varying values indexed by the zero ring vertices of the vertex
// patch. This indexing is redundant for triangles and quads and
// could be made redunant for other patch types if we reorganized
// the vertex patch indices so that the zero ring indices always occured
// first. This will also need to be updated when we add support for
// triangle patches.
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
for (int arrayIndex=0; arrayIndex<(int)_patchArrays.size(); ++arrayIndex) {
PatchArray const & pa = getPatchArray(arrayIndex);
PatchDescriptor::Type patchType = pa.desc.GetType();
for (int patch=0; patch<pa.numPatches; ++patch) {
ConstIndexArray vertexCVs = GetPatchVertices(arrayIndex, patch);
int start = (pa.patchIndex + patch) * numVaryingCVs;
if (patchType == PatchDescriptor::REGULAR) {
_varyingVerts[start+0] = vertexCVs[5];
_varyingVerts[start+1] = vertexCVs[6];
_varyingVerts[start+2] = vertexCVs[10];
_varyingVerts[start+3] = vertexCVs[9];
} else if (patchType == PatchDescriptor::GREGORY_BASIS) {
_varyingVerts[start+0] = vertexCVs[0];
_varyingVerts[start+1] = vertexCVs[5];
_varyingVerts[start+2] = vertexCVs[10];
_varyingVerts[start+3] = vertexCVs[15];
} else if (patchType == PatchDescriptor::QUADS) {
_varyingVerts[start+0] = vertexCVs[0];
_varyingVerts[start+1] = vertexCVs[1];
_varyingVerts[start+2] = vertexCVs[2];
_varyingVerts[start+3] = vertexCVs[3];
} else if (patchType == PatchDescriptor::TRIANGLES) {
_varyingVerts[start+0] = vertexCVs[0];
_varyingVerts[start+1] = vertexCVs[1];
_varyingVerts[start+2] = vertexCVs[2];
}
}
}
}
int
PatchTable::GetNumFVarChannels() const {
return (int)_fvarChannels.size();
}
Sdc::Options::FVarLinearInterpolation
PatchTable::GetFVarChannelLinearInterpolation(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.interpolation;
}
PatchDescriptor
PatchTable::GetFVarPatchDescriptorRegular(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.regDesc;
}
PatchDescriptor
PatchTable::GetFVarPatchDescriptorIrregular(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.irregDesc;
}
PatchDescriptor
PatchTable::GetFVarPatchDescriptor(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.irregDesc;
}
ConstIndexArray
PatchTable::GetFVarValues(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return ConstIndexArray(&c.patchValues[0], (int)c.patchValues.size());
}
int
PatchTable::GetFVarValueStride(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.stride;
}
IndexArray
PatchTable::getFVarValues(int channel) {
FVarPatchChannel & c = getFVarPatchChannel(channel);
return IndexArray(&c.patchValues[0], (int)c.patchValues.size());
}
ConstIndexArray
PatchTable::getPatchFVarValues(int patch, int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
int ncvsThisPatch = c.patchParam[patch].IsRegular()
? c.regDesc.GetNumControlVertices()
: c.irregDesc.GetNumControlVertices();
return ConstIndexArray(&c.patchValues[patch * c.stride], ncvsThisPatch);
}
ConstIndexArray
PatchTable::GetPatchFVarValues(PatchHandle const & handle, int channel) const {
return getPatchFVarValues(handle.patchIndex, channel);
}
ConstIndexArray
PatchTable::GetPatchFVarValues(int arrayIndex, int patchIndex, int channel) const {
return getPatchFVarValues(getPatchIndex(arrayIndex, patchIndex), channel);
}
ConstIndexArray
PatchTable::GetPatchArrayFVarValues(int array, int channel) const {
PatchArray const & pa = getPatchArray(array);
FVarPatchChannel const & c = getFVarPatchChannel(channel);
int ncvs = c.stride;
int start = pa.patchIndex * ncvs;
int count = pa.numPatches * ncvs;
return ConstIndexArray(&c.patchValues[start], count);
}
PatchParam
PatchTable::getPatchFVarPatchParam(int patch, int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.patchParam[patch];
}
PatchParam
PatchTable::GetPatchFVarPatchParam(PatchHandle const & handle, int channel) const {
return getPatchFVarPatchParam(handle.patchIndex, channel);
}
PatchParam
PatchTable::GetPatchFVarPatchParam(int arrayIndex, int patchIndex, int channel) const {
return getPatchFVarPatchParam(getPatchIndex(arrayIndex, patchIndex), channel);
}
ConstPatchParamArray
PatchTable::GetPatchArrayFVarPatchParams(int array, int channel) const {
PatchArray const & pa = getPatchArray(array);
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return ConstPatchParamArray(&c.patchParam[pa.patchIndex], pa.numPatches);
}
ConstPatchParamArray
PatchTable::GetFVarPatchParams(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return ConstPatchParamArray(&c.patchParam[0], (int)c.patchParam.size());
}
PatchParamArray
PatchTable::getFVarPatchParams(int channel) {
FVarPatchChannel & c = getFVarPatchChannel(channel);
return PatchParamArray(&c.patchParam[0], (int)c.patchParam.size());
}
void
PatchTable::print() const {
printf("patchTable (0x%p)\n", this);
printf(" numPatches = %d\n", GetNumPatchesTotal());
for (int i=0; i<GetNumPatchArrays(); ++i) {
printf(" patchArray %d:\n", i);
PatchArray const & pa = getPatchArray(i);
pa.print();
}
}
//
// Evaluate basis functions for vertex and derivatives at (s,t):
//
template <typename REAL>
void
PatchTable::EvaluateBasis(
PatchHandle const & handle, REAL s, REAL t,
REAL wP[], REAL wDs[], REAL wDt[],
REAL wDss[], REAL wDst[], REAL wDtt[]) const {
PatchParam const & param = _paramTable[handle.patchIndex];
PatchDescriptor::Type patchType = GetPatchArrayDescriptor(handle.arrayIndex).GetType();
internal::EvaluatePatchBasis(patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
}
//
// Evaluate basis functions for varying and derivatives at (s,t):
//
template <typename REAL>
void
PatchTable::EvaluateBasisVarying(
PatchHandle const & handle, REAL s, REAL t,
REAL wP[], REAL wDs[], REAL wDt[],
REAL wDss[], REAL wDst[], REAL wDtt[]) const {
PatchParam const & param = _paramTable[handle.patchIndex];
PatchDescriptor::Type patchType = GetVaryingPatchDescriptor().GetType();
internal::EvaluatePatchBasis(patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
}
//
// Evaluate basis functions for face-varying and derivatives at (s,t):
//
template <typename REAL>
void
PatchTable::EvaluateBasisFaceVarying(
PatchHandle const & handle, REAL s, REAL t,
REAL wP[], REAL wDs[], REAL wDt[],
REAL wDss[], REAL wDst[], REAL wDtt[],
int channel) const {
PatchParam param = getPatchFVarPatchParam(handle.patchIndex, channel);
PatchDescriptor::Type patchType = param.IsRegular()
? GetFVarPatchDescriptorRegular(channel).GetType()
: GetFVarPatchDescriptorIrregular(channel).GetType();
internal::EvaluatePatchBasis(patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
}
//
// Explicit instantiation of EvaluateBasis...() methods for float and double:
//
template void PatchTable::EvaluateBasis<float>(PatchHandle const & handle,
float s, float t, float wP[], float wDs[], float wDt[],
float wDss[], float wDst[], float wDtt[]) const;
template void PatchTable::EvaluateBasisVarying<float>(PatchHandle const & handle,
float s, float t, float wP[], float wDs[], float wDt[],
float wDss[], float wDst[], float wDtt[]) const;
template void PatchTable::EvaluateBasisFaceVarying<float>(PatchHandle const & handle,
float s, float t, float wP[], float wDs[], float wDt[],
float wDss[], float wDst[], float wDtt[], int channel) const;
template void PatchTable::EvaluateBasis<double>(PatchHandle const & handle,
double s, double t, double wP[], double wDs[], double wDt[],
double wDss[], double wDst[], double wDtt[]) const;
template void PatchTable::EvaluateBasisVarying<double>(PatchHandle const & handle,
double s, double t, double wP[], double wDs[], double wDt[],
double wDss[], double wDst[], double wDtt[]) const;
template void PatchTable::EvaluateBasisFaceVarying<double>(PatchHandle const & handle,
double s, double t, double wP[], double wDs[], double wDt[],
double wDss[], double wDst[], double wDtt[], int channel) const;
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,861 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_PATCH_TABLE_H
#define OPENSUBDIV3_FAR_PATCH_TABLE_H
#include "../version.h"
#include "../far/patchDescriptor.h"
#include "../far/patchParam.h"
#include "../far/stencilTable.h"
#include "../sdc/options.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief Container for arrays of parametric patches
///
/// PatchTable contains topology and parametric information about the patches
/// generated by the Refinement process. Patches in the table are sorted into
/// arrays based on their PatchDescriptor Type.
///
/// Note : PatchTable can be accessed either using a PatchHandle or a
/// combination of array and patch indices.
///
/// XXXX manuelk we should add a PatchIterator that can dereference into
/// a PatchHandle for fast linear traversal of the table
///
class PatchTable {
public:
/// \brief Handle that can be used as unique patch identifier within PatchTable
class PatchHandle {
// XXXX manuelk members will eventually be made private
public:
friend class PatchTable;
friend class PatchMap;
Index arrayIndex, // Array index of the patch
patchIndex, // Absolute Index of the patch
vertIndex; // Relative offset to the first CV of the patch in array
};
public:
/// \brief Copy constructor
PatchTable(PatchTable const & src);
/// \brief Destructor
~PatchTable();
/// \brief True if the patches are of feature adaptive types
bool IsFeatureAdaptive() const;
/// \brief Returns the total number of control vertex indices in the table
int GetNumControlVerticesTotal() const {
return (int)_patchVerts.size();
}
/// \brief Returns the total number of patches stored in the table
int GetNumPatchesTotal() const;
/// \brief Returns max vertex valence
int GetMaxValence() const { return _maxValence; }
/// \brief Returns the total number of ptex faces in the mesh
int GetNumPtexFaces() const { return _numPtexFaces; }
//@{
/// @name Individual patches
///
/// \anchor individual_patches
///
/// \brief Accessors for individual patches
///
/// \brief Returns the PatchDescriptor for the patch identified by \p handle
PatchDescriptor GetPatchDescriptor(PatchHandle const & handle) const;
/// \brief Returns the control vertex indices for the patch identified by \p handle
ConstIndexArray GetPatchVertices(PatchHandle const & handle) const;
/// \brief Returns a PatchParam for the patch identified by \p handle
PatchParam GetPatchParam(PatchHandle const & handle) const;
/// \brief Returns the control vertex indices for \p patch in \p array
ConstIndexArray GetPatchVertices(int array, int patch) const;
/// \brief Returns the PatchParam for \p patch in \p array
PatchParam GetPatchParam(int array, int patch) const;
//@}
//@{
/// @name Arrays of patches
///
/// \anchor arrays_of_patches
///
/// \brief Accessors for arrays of patches of the same type
///
/// \brief Returns the number of patch arrays in the table
int GetNumPatchArrays() const;
/// \brief Returns the number of patches in \p array
int GetNumPatches(int array) const;
/// \brief Returns the number of control vertices in \p array
int GetNumControlVertices(int array) const;
/// \brief Returns the PatchDescriptor for the patches in \p array
PatchDescriptor GetPatchArrayDescriptor(int array) const;
/// \brief Returns the control vertex indices for the patches in \p array
ConstIndexArray GetPatchArrayVertices(int array) const;
/// \brief Returns the PatchParams for the patches in \p array
ConstPatchParamArray const GetPatchParams(int array) const;
//@}
//@{
/// @name Change of basis patches
///
/// \anchor change_of_basis_patches
///
/// \brief Accessors for change of basis patches
///
///
/// \brief Returns the number of local vertex points.
int GetNumLocalPoints() const;
/// \brief Returns the stencil table to compute local point vertex values
StencilTable const *GetLocalPointStencilTable() const;
/// \brief Returns the stencil table to compute local point vertex values
template <typename REAL>
StencilTableReal<REAL> const *GetLocalPointStencilTable() const;
/// \brief Tests if the precision of the stencil table to compute local point
/// vertex values matches the given floating point type \<REAL\>.
template <typename REAL> bool LocalPointStencilPrecisionMatchesType() const;
/// \brief Updates local point vertex values.
///
/// @param src Buffer with primvar data for the base and refined
/// vertex values
///
/// @param dst Destination buffer for the computed local point
/// vertex values
///
/// For more flexibility computing local vertex points, retrieval of
/// the local point stencil table and use of its public methods is
/// recommended or often required.
///
template <class T> void
ComputeLocalPointValues(T const *src, T *dst) const;
/// \brief Returns the number of local varying points.
int GetNumLocalPointsVarying() const;
/// \brief Returns the stencil table to compute local point varying values
StencilTable const *GetLocalPointVaryingStencilTable() const;
/// \brief Returns the stencil table to compute local point varying values
template <typename REAL>
StencilTableReal<REAL> const *GetLocalPointVaryingStencilTable() const;
/// \brief Tests if the precision of the stencil table to compute local point
/// varying values matches the given floating point type \<REAL\>.
template <typename REAL> bool LocalPointVaryingStencilPrecisionMatchesType() const;
/// \brief Updates local point varying values.
///
/// @param src Buffer with primvar data for the base and refined
/// varying values
///
/// @param dst Destination buffer for the computed local point
/// varying values
///
/// For more flexibility computing local varying points, retrieval of
/// the local point varying stencil table and use of its public methods
/// is recommended or often required.
///
template <class T> void
ComputeLocalPointValuesVarying(T const *src, T *dst) const;
/// \brief Returns the number of local face-varying points for \p channel
int GetNumLocalPointsFaceVarying(int channel = 0) const;
/// \brief Returns the stencil table to compute local point face-varying values
StencilTable const *GetLocalPointFaceVaryingStencilTable(int channel = 0) const;
/// \brief Returns the stencil table to compute local point face-varying values
template <typename REAL>
StencilTableReal<REAL> const * GetLocalPointFaceVaryingStencilTable(int channel = 0) const;
/// \brief Tests if the precision of the stencil table to compute local point
/// face-varying values matches the given floating point type \<REAL\>.
template <typename REAL> bool LocalPointFaceVaryingStencilPrecisionMatchesType() const;
/// \brief Updates local point face-varying values.
///
/// @param src Buffer with primvar data for the base and refined
/// face-varying values
///
/// @param dst Destination buffer for the computed local point
/// face-varying values
///
/// @param channel face-varying channel
///
/// For more flexibility computing local face-varying points, retrieval
/// of the local point face-varying stencil table and use of its public
/// methods is recommended or often required.
///
template <class T> void
ComputeLocalPointValuesFaceVarying(T const *src, T *dst, int channel = 0) const;
//@}
//@{
/// @name Legacy gregory patch evaluation buffers
/// \brief Accessors for the gregory patch evaluation buffers.
/// These methods will be deprecated.
///
typedef Vtr::ConstArray<unsigned int> ConstQuadOffsetsArray;
/// \brief Returns the 'QuadOffsets' for the Gregory patch identified by \p handle
ConstQuadOffsetsArray GetPatchQuadOffsets(PatchHandle const & handle) const;
typedef std::vector<Index> VertexValenceTable;
/// \brief Returns the 'VertexValences' table (vertex neighborhoods table)
VertexValenceTable const & GetVertexValenceTable() const {
return _vertexValenceTable;
}
//@}
//@{
/// @name Single-crease patches
///
/// \anchor single_crease_patches
///
/// \brief Accessors for single-crease patch edge sharpness
///
/// \brief Returns the crease sharpness for the patch identified by \p handle
/// if it is a single-crease patch, or 0.0f
float GetSingleCreasePatchSharpnessValue(PatchHandle const & handle) const;
/// \brief Returns the crease sharpness for the \p patch in \p array
/// if it is a single-crease patch, or 0.0f
float GetSingleCreasePatchSharpnessValue(int array, int patch) const;
//@}
//@{
/// @name Varying data
///
/// \anchor varying_data
///
/// \brief Accessors for varying data
///
/// \brief Returns the varying patch descriptor
PatchDescriptor GetVaryingPatchDescriptor() const;
/// \brief Returns the varying vertex indices for a given patch
ConstIndexArray GetPatchVaryingVertices(PatchHandle const & handle) const;
/// \brief Returns the varying vertex indices for a given patch
ConstIndexArray GetPatchVaryingVertices(int array, int patch) const;
/// \brief Returns the varying vertex indices for the patches in \p array
ConstIndexArray GetPatchArrayVaryingVertices(int array) const;
/// \brief Returns an array of varying vertex indices for the patches.
ConstIndexArray GetVaryingVertices() const;
//@}
//@{
/// @name Face-varying channels
///
/// \anchor face_varying_channels
///
/// \brief Accessors for face-varying channels
///
/// \brief Returns the number of face-varying channels
int GetNumFVarChannels() const;
/// \brief Returns the regular patch descriptor for \p channel
PatchDescriptor GetFVarPatchDescriptorRegular(int channel = 0) const;
/// \brief Returns the irregular patch descriptor for \p channel
PatchDescriptor GetFVarPatchDescriptorIrregular(int channel = 0) const;
/// \brief Returns the default/irregular patch descriptor for \p channel
PatchDescriptor GetFVarPatchDescriptor(int channel = 0) const;
/// \brief Returns the value indices for a given patch in \p channel
ConstIndexArray GetPatchFVarValues(PatchHandle const & handle, int channel = 0) const;
/// \brief Returns the value indices for a given patch in \p channel
ConstIndexArray GetPatchFVarValues(int array, int patch, int channel = 0) const;
/// \brief Returns the value indices for the patches in \p array in \p channel
ConstIndexArray GetPatchArrayFVarValues(int array, int channel = 0) const;
/// \brief Returns an array of value indices for the patches in \p channel
ConstIndexArray GetFVarValues(int channel = 0) const;
/// \brief Returns the stride between patches in the value index array of \p channel
int GetFVarValueStride(int channel = 0) const;
/// \brief Returns the value indices for a given patch in \p channel
PatchParam GetPatchFVarPatchParam(PatchHandle const & handle, int channel = 0) const;
/// \brief Returns the face-varying params for a given patch \p channel
PatchParam GetPatchFVarPatchParam(int array, int patch, int channel = 0) const;
/// \brief Returns the face-varying for a given patch in \p array in \p channel
ConstPatchParamArray GetPatchArrayFVarPatchParams(int array, int channel = 0) const;
/// \brief Returns an array of face-varying patch param for \p channel
ConstPatchParamArray GetFVarPatchParams(int channel = 0) const;
/// \brief Deprecated @see PatchTable#GetFVarPatchDescriptor
Sdc::Options::FVarLinearInterpolation GetFVarChannelLinearInterpolation(int channel = 0) const;
//@}
//@{
/// @name Direct accessors
///
/// \warning These direct accessors are left for convenience, but they are
/// likely going to be deprecated in future releases
///
typedef std::vector<Index> PatchVertsTable;
/// \brief Get the table of patch control vertices
PatchVertsTable const & GetPatchControlVerticesTable() const { return _patchVerts; }
/// \brief Returns the PatchParamTable (PatchParams order matches patch array sorting)
PatchParamTable const & GetPatchParamTable() const { return _paramTable; }
/// \brief Returns a sharpness index table for each patch (if exists)
std::vector<Index> const &GetSharpnessIndexTable() const { return _sharpnessIndices; }
/// \brief Returns sharpness values table
std::vector<float> const &GetSharpnessValues() const { return _sharpnessValues; }
typedef std::vector<unsigned int> QuadOffsetsTable;
/// \brief Returns the quad-offsets table
QuadOffsetsTable const & GetQuadOffsetsTable() const {
return _quadOffsetsTable;
}
//@}
/// debug helper
void print() const;
public:
//@{
/// @name Evaluation methods
///
/// \brief Evaluate basis functions for position and derivatives at a
/// given (u,v) parametric location of a patch.
///
/// @param handle A patch handle identifying the sub-patch containing the
/// (u,v) location
///
/// @param u Patch coordinate (in base face normalized space)
///
/// @param v Patch coordinate (in base face normalized space)
///
/// @param wP Weights (evaluated basis functions) for the position
///
/// @param wDu Weights (evaluated basis functions) for derivative wrt u
///
/// @param wDv Weights (evaluated basis functions) for derivative wrt v
///
/// @param wDuu Weights (evaluated basis functions) for 2nd derivative wrt u
///
/// @param wDuv Weights (evaluated basis functions) for 2nd derivative wrt u and v
///
/// @param wDvv Weights (evaluated basis functions) for 2nd derivative wrt v
///
template <typename REAL>
void EvaluateBasis(PatchHandle const & handle, REAL u, REAL v,
REAL wP[], REAL wDu[] = 0, REAL wDv[] = 0,
REAL wDuu[] = 0, REAL wDuv[] = 0, REAL wDvv[] = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasis(PatchHandle const & handle, float u, float v,
float wP[], float wDu[] = 0, float wDv[] = 0,
float wDuu[] = 0, float wDuv[] = 0, float wDvv[] = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasis(PatchHandle const & handle, double u, double v,
double wP[], double wDu[] = 0, double wDv[] = 0,
double wDuu[] = 0, double wDuv[] = 0, double wDvv[] = 0) const;
/// \brief Evaluate basis functions for a varying value and
/// derivatives at a given (u,v) parametric location of a patch.
///
/// @param handle A patch handle identifying the sub-patch containing the
/// (u,v) location
///
/// @param u Patch coordinate (in base face normalized space)
///
/// @param v Patch coordinate (in base face normalized space)
///
/// @param wP Weights (evaluated basis functions) for the position
///
/// @param wDu Weights (evaluated basis functions) for derivative wrt u
///
/// @param wDv Weights (evaluated basis functions) for derivative wrt v
///
/// @param wDuu Weights (evaluated basis functions) for 2nd derivative wrt u
///
/// @param wDuv Weights (evaluated basis functions) for 2nd derivative wrt u and v
///
/// @param wDvv Weights (evaluated basis functions) for 2nd derivative wrt v
///
template <typename REAL>
void EvaluateBasisVarying(PatchHandle const & handle, REAL u, REAL v,
REAL wP[], REAL wDu[] = 0, REAL wDv[] = 0,
REAL wDuu[] = 0, REAL wDuv[] = 0, REAL wDvv[] = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasisVarying(PatchHandle const & handle, float u, float v,
float wP[], float wDu[] = 0, float wDv[] = 0,
float wDuu[] = 0, float wDuv[] = 0, float wDvv[] = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasisVarying(PatchHandle const & handle, double u, double v,
double wP[], double wDu[] = 0, double wDv[] = 0,
double wDuu[] = 0, double wDuv[] = 0, double wDvv[] = 0) const;
/// \brief Evaluate basis functions for a face-varying value and
/// derivatives at a given (u,v) parametric location of a patch.
///
/// @param handle A patch handle identifying the sub-patch containing the
/// (u,v) location
///
/// @param u Patch coordinate (in base face normalized space)
///
/// @param v Patch coordinate (in base face normalized space)
///
/// @param wP Weights (evaluated basis functions) for the position
///
/// @param wDu Weights (evaluated basis functions) for derivative wrt u
///
/// @param wDv Weights (evaluated basis functions) for derivative wrt v
///
/// @param wDuu Weights (evaluated basis functions) for 2nd derivative wrt u
///
/// @param wDuv Weights (evaluated basis functions) for 2nd derivative wrt u and v
///
/// @param wDvv Weights (evaluated basis functions) for 2nd derivative wrt v
///
/// @param channel face-varying channel
///
template <typename REAL>
void EvaluateBasisFaceVarying(PatchHandle const & handle, REAL u, REAL v,
REAL wP[], REAL wDu[] = 0, REAL wDv[] = 0,
REAL wDuu[] = 0, REAL wDuv[] = 0, REAL wDvv[] = 0,
int channel = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasisFaceVarying(PatchHandle const & handle, float u, float v,
float wP[], float wDu[] = 0, float wDv[] = 0,
float wDuu[] = 0, float wDuv[] = 0, float wDvv[] = 0,
int channel = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasisFaceVarying(PatchHandle const & handle, double u, double v,
double wP[], double wDu[] = 0, double wDv[] = 0,
double wDuu[] = 0, double wDuv[] = 0, double wDvv[] = 0,
int channel = 0) const;
//@}
protected:
friend class PatchTableBuilder;
// Factory constructor
PatchTable(int maxvalence);
Index getPatchIndex(int array, int patch) const;
PatchParamArray getPatchParams(int arrayIndex);
Index * getSharpnessIndices(Index arrayIndex);
float * getSharpnessValues(Index arrayIndex);
private:
//
// Patch arrays
//
struct PatchArray;
typedef std::vector<PatchArray> PatchArrayVector;
PatchArray & getPatchArray(Index arrayIndex);
PatchArray const & getPatchArray(Index arrayIndex) const;
void reservePatchArrays(int numPatchArrays);
void pushPatchArray(PatchDescriptor desc, int npatches,
Index * vidx, Index * pidx, Index * qoidx=0);
IndexArray getPatchArrayVertices(int arrayIndex);
Index findPatchArray(PatchDescriptor desc);
//
// Varying patch arrays
//
IndexArray getPatchArrayVaryingVertices(int arrayIndex);
void allocateVaryingVertices(
PatchDescriptor desc, int numPatches);
void populateVaryingVertices();
//
// Face-varying patch channels
//
struct FVarPatchChannel;
typedef std::vector<FVarPatchChannel> FVarPatchChannelVector;
FVarPatchChannel & getFVarPatchChannel(int channel);
FVarPatchChannel const & getFVarPatchChannel(int channel) const;
void allocateFVarPatchChannels(int numChannels);
void allocateFVarPatchChannelValues(
PatchDescriptor regDesc, PatchDescriptor irregDesc,
int numPatches, int channel);
// deprecated
void setFVarPatchChannelLinearInterpolation(
Sdc::Options::FVarLinearInterpolation interpolation, int channel);
IndexArray getFVarValues(int channel);
ConstIndexArray getPatchFVarValues(int patch, int channel) const;
PatchParamArray getFVarPatchParams(int channel);
PatchParam getPatchFVarPatchParam(int patch, int channel) const;
private:
//
// Simple private class to hold stencil table pointers of varying precision,
// where the discriminant of the precision is external.
//
// NOTE that this is a simple pointer container and NOT a smart pointer that
// manages the ownership of the object referred to by it.
//
class StencilTablePtr {
private:
typedef StencilTableReal<float> float_type;
typedef StencilTableReal<double> double_type;
union {
float_type * _fPtr;
double_type * _dPtr;
};
public:
StencilTablePtr() { _fPtr = 0; }
StencilTablePtr(float_type * ptr) { _fPtr = ptr; }
StencilTablePtr(double_type * ptr) { _dPtr = ptr; }
operator bool() const { return _fPtr != 0; }
void Set() { _fPtr = 0; }
void Set(float_type * ptr) { _fPtr = ptr; }
void Set(double_type * ptr) { _dPtr = ptr; }
template <typename REAL> StencilTableReal<REAL> * Get() const;
};
private:
//
// Topology
//
int _maxValence, // highest vertex valence found in the mesh
_numPtexFaces; // total number of ptex faces
PatchArrayVector _patchArrays; // Vector of descriptors for arrays of patches
std::vector<Index> _patchVerts; // Indices of the control vertices of the patches
PatchParamTable _paramTable; // PatchParam bitfields (one per patch)
//
// Extraordinary vertex closed-form evaluation / endcap basis conversion
//
// XXXtakahito: these data will probably be replaced with mask coefficient or something
// SchemeWorker populates.
//
QuadOffsetsTable _quadOffsetsTable; // Quad offsets (for Gregory patches)
VertexValenceTable _vertexValenceTable; // Vertex valence table (for Gregory patches)
StencilTablePtr _localPointStencils; // local point conversion stencils
StencilTablePtr _localPointVaryingStencils; // local point varying stencils
//
// Varying data
//
PatchDescriptor _varyingDesc;
std::vector<Index> _varyingVerts;
//
// Face-varying data
//
FVarPatchChannelVector _fvarChannels;
std::vector<StencilTablePtr> _localPointFaceVaryingStencils;
//
// 'single-crease' patch sharpness tables
//
std::vector<Index> _sharpnessIndices; // Indices of single-crease sharpness (one per patch)
std::vector<float> _sharpnessValues; // Sharpness values.
//
// Construction history -- relevant to at least one public query:
//
unsigned int _isUniformLinear : 1;
//
// Precision -- only applies to local-point stencil tables
//
unsigned int _vertexPrecisionIsDouble : 1;
unsigned int _varyingPrecisionIsDouble : 1;
unsigned int _faceVaryingPrecisionIsDouble : 1;
};
//
// Template specializations for float/double -- to be defined before used:
//
template <> inline StencilTableReal<float> *
PatchTable::StencilTablePtr::Get<float>() const { return _fPtr; }
template <> inline StencilTableReal<double> *
PatchTable::StencilTablePtr::Get<double>() const { return _dPtr; }
template <> inline bool
PatchTable::LocalPointStencilPrecisionMatchesType<float>() const {
return !_vertexPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointVaryingStencilPrecisionMatchesType<float>() const {
return !_varyingPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointFaceVaryingStencilPrecisionMatchesType<float>() const {
return !_faceVaryingPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointStencilPrecisionMatchesType<double>() const {
return _vertexPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointVaryingStencilPrecisionMatchesType<double>() const {
return _varyingPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointFaceVaryingStencilPrecisionMatchesType<double>() const {
return _faceVaryingPrecisionIsDouble;
}
//
// StencilTable access -- backward compatible and generic:
//
inline StencilTable const *
PatchTable::GetLocalPointStencilTable() const {
assert(LocalPointStencilPrecisionMatchesType<float>());
return static_cast<StencilTable const *>(_localPointStencils.Get<float>());
}
inline StencilTable const *
PatchTable::GetLocalPointVaryingStencilTable() const {
assert(LocalPointVaryingStencilPrecisionMatchesType<float>());
return static_cast<StencilTable const *>(
_localPointVaryingStencils.Get<float>());
}
inline StencilTable const *
PatchTable::GetLocalPointFaceVaryingStencilTable(int channel) const {
assert(LocalPointFaceVaryingStencilPrecisionMatchesType<float>());
if (channel >= 0 && channel < (int)_localPointFaceVaryingStencils.size()) {
return static_cast<StencilTable const *>(
_localPointFaceVaryingStencils[channel].Get<float>());
}
return NULL;
}
template <typename REAL>
inline StencilTableReal<REAL> const *
PatchTable::GetLocalPointStencilTable() const {
assert(LocalPointStencilPrecisionMatchesType<REAL>());
return _localPointStencils.Get<REAL>();
}
template <typename REAL>
inline StencilTableReal<REAL> const *
PatchTable::GetLocalPointVaryingStencilTable() const {
assert(LocalPointVaryingStencilPrecisionMatchesType<REAL>());
return _localPointVaryingStencils.Get<REAL>();
}
template <typename REAL>
inline StencilTableReal<REAL> const *
PatchTable::GetLocalPointFaceVaryingStencilTable(int channel) const {
assert(LocalPointFaceVaryingStencilPrecisionMatchesType<REAL>());
if (channel >= 0 && channel < (int)_localPointFaceVaryingStencils.size()) {
return _localPointFaceVaryingStencils[channel].Get<REAL>();
}
return NULL;
}
//
// Computation of local point values:
//
template <class T>
inline void
PatchTable::ComputeLocalPointValues(T const *src, T *dst) const {
assert(LocalPointStencilPrecisionMatchesType<float>());
if (_localPointStencils) {
_localPointStencils.Get<float>()->UpdateValues(src, dst);
}
}
template <class T>
inline void
PatchTable::ComputeLocalPointValuesVarying(T const *src, T *dst) const {
assert(LocalPointVaryingStencilPrecisionMatchesType<float>());
if (_localPointVaryingStencils) {
_localPointVaryingStencils.Get<float>()->UpdateValues(src, dst);
}
}
template <class T>
inline void
PatchTable::ComputeLocalPointValuesFaceVarying(T const *src, T *dst, int channel) const {
assert(LocalPointFaceVaryingStencilPrecisionMatchesType<float>());
if (channel >= 0 && channel < (int)_localPointFaceVaryingStencils.size()) {
if (_localPointFaceVaryingStencils[channel]) {
_localPointFaceVaryingStencils[channel].Get<float>()->UpdateValues(src, dst);
}
}
}
//
// Basis evaluation overloads
//
inline void
PatchTable::EvaluateBasis(PatchHandle const & handle, float u, float v,
float wP[], float wDu[], float wDv[],
float wDuu[], float wDuv[], float wDvv[]) const {
EvaluateBasis<float>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
inline void
PatchTable::EvaluateBasis(PatchHandle const & handle, double u, double v,
double wP[], double wDu[], double wDv[],
double wDuu[], double wDuv[], double wDvv[]) const {
EvaluateBasis<double>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
inline void
PatchTable::EvaluateBasisVarying(PatchHandle const & handle, float u, float v,
float wP[], float wDu[], float wDv[],
float wDuu[], float wDuv[], float wDvv[]) const {
EvaluateBasisVarying<float>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
inline void
PatchTable::EvaluateBasisVarying(PatchHandle const & handle, double u, double v,
double wP[], double wDu[], double wDv[],
double wDuu[], double wDuv[], double wDvv[]) const {
EvaluateBasisVarying<double>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
inline void
PatchTable::EvaluateBasisFaceVarying(PatchHandle const & handle, float u, float v,
float wP[], float wDu[], float wDv[],
float wDuu[], float wDuv[], float wDvv[], int channel) const {
EvaluateBasisFaceVarying<float>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv, channel);
}
inline void
PatchTable::EvaluateBasisFaceVarying(PatchHandle const & handle, double u, double v,
double wP[], double wDu[], double wDv[],
double wDuu[], double wDuv[], double wDvv[], int channel) const {
EvaluateBasisFaceVarying<double>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv, channel);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_TABLE */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_PATCH_TABLE_FACTORY_H
#define OPENSUBDIV3_FAR_PATCH_TABLE_FACTORY_H
#include "../version.h"
#include "../far/topologyRefiner.h"
#include "../far/patchTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief Factory for constructing a PatchTable from a TopologyRefiner
///
class PatchTableFactory {
public:
/// \brief Public options for the PatchTable factory
///
struct Options {
/// \brief Choice for approximating irregular patches (end-caps)
///
/// This enum specifies how irregular patches (end-caps) are approximated.
/// A basis is chosen, rather than a specific patch type, and has a
/// corresponding patch type for each subdivision scheme, i.e. a quad and
/// triangular patch type exists for each basis. These choices provide a
/// trade-off between surface quality and performance.
///
enum EndCapType {
ENDCAP_NONE = 0, ///< unspecified
ENDCAP_BILINEAR_BASIS, ///< use linear patches (simple quads or tris)
ENDCAP_BSPLINE_BASIS, ///< use BSpline-like patches (same patch type as regular)
ENDCAP_GREGORY_BASIS, ///< use Gregory patches (highest quality, recommended default)
ENDCAP_LEGACY_GREGORY ///< legacy option for 2.x style Gregory patches (Catmark only)
};
Options(unsigned int maxIsolation=10) :
generateAllLevels(false),
includeBaseLevelIndices(true),
includeFVarBaseLevelIndices(false),
triangulateQuads(false),
useSingleCreasePatch(false),
useInfSharpPatch(false),
maxIsolationLevel(maxIsolation),
endCapType(ENDCAP_GREGORY_BASIS),
shareEndCapPatchPoints(true),
generateVaryingTables(true),
generateVaryingLocalPoints(true),
generateFVarTables(false),
patchPrecisionDouble(false),
fvarPatchPrecisionDouble(false),
generateFVarLegacyLinearPatches(true),
generateLegacySharpCornerPatches(true),
numFVarChannels(-1),
fvarChannelIndices(0)
{ }
/// \brief Get endcap basis type
EndCapType GetEndCapType() const { return (EndCapType)endCapType; }
/// \brief Set endcap basis type
void SetEndCapType(EndCapType e) { endCapType = e; }
/// \brief Set precision of vertex patches
template <typename REAL> void SetPatchPrecision();
/// \brief Set precision of face-varying patches
template <typename REAL> void SetFVarPatchPrecision();
/// \brief Determine adaptive refinement options to match assigned patch options
TopologyRefiner::AdaptiveOptions GetRefineAdaptiveOptions() const {
TopologyRefiner::AdaptiveOptions adaptiveOptions(maxIsolationLevel);
adaptiveOptions.useInfSharpPatch = useInfSharpPatch;
adaptiveOptions.useSingleCreasePatch = useSingleCreasePatch;
adaptiveOptions.considerFVarChannels = generateFVarTables &&
!generateFVarLegacyLinearPatches;
return adaptiveOptions;
}
unsigned int generateAllLevels : 1, ///< Generate levels from 'firstLevel' to 'maxLevel' (Uniform mode only)
includeBaseLevelIndices : 1, ///< Include base level in patch point indices (Uniform mode only)
includeFVarBaseLevelIndices : 1, ///< Include base level in face-varying patch point indices (Uniform mode only)
triangulateQuads : 1, ///< Triangulate 'QUADS' primitives (Uniform mode only)
useSingleCreasePatch : 1, ///< Use single crease patch
useInfSharpPatch : 1, ///< Use infinitely-sharp patch
maxIsolationLevel : 4, ///< Cap adaptive feature isolation to the given level (max. 10)
// end-capping
endCapType : 3, ///< EndCapType
shareEndCapPatchPoints : 1, ///< Share endcap patch points among adjacent endcap patches.
///< currently only work with GregoryBasis.
// varying
generateVaryingTables : 1, ///< Generate varying patch tables
generateVaryingLocalPoints : 1, ///< Generate local points with varying patches
// face-varying
generateFVarTables : 1, ///< Generate face-varying patch tables
// precision
patchPrecisionDouble : 1, ///< Generate double-precision stencils for vertex patches
fvarPatchPrecisionDouble : 1, ///< Generate double-precision stencils for face-varying patches
// legacy behaviors (default to true)
generateFVarLegacyLinearPatches : 1, ///< Generate all linear face-varying patches (legacy)
generateLegacySharpCornerPatches : 1; ///< Generate sharp regular patches at smooth corners (legacy)
int numFVarChannels; ///< Number of channel indices and interpolation modes passed
int const * fvarChannelIndices; ///< List containing the indices of the channels selected for the factory
};
/// \brief Instantiates a PatchTable from a client-provided TopologyRefiner.
///
/// A PatchTable can be constructed from a TopologyRefiner that has been
/// either adaptively or uniformly refined. In both cases, the resulting
/// patches reference vertices in the various refined levels by index,
/// and those indices accumulate with the levels in different ways.
///
/// For adaptively refined patches, patches are defined at different levels,
/// including the base level, so the indices of patch vertices include
/// vertices from all levels. A sparse set of patches can be created by
/// restricting the patches generated to those descending from a given set
/// of faces at the base level. This sparse set of base faces is expected
/// to be a subset of the faces that were adaptively refined in the given
/// TopologyRefiner, otherwise results are undefined.
///
/// For uniformly refined patches, all patches are completely defined within
/// the last level. There is often no use for intermediate levels and they
/// can usually be ignored. Indices of patch vertices might therefore be
/// expected to be defined solely within the last level. While this is true
/// for face-varying patches, for historical reasons it is not the case for
/// vertex and varying patches. Indices for vertex and varying patches include
/// the base level in addition to the last level while indices for face-varying
/// patches include only the last level.
///
/// @param refiner TopologyRefiner from which to generate patches
///
/// @param options Options controlling the creation of the table
///
/// @param selectedFaces Only create patches for the given set of base faces.
///
/// @return A new instance of PatchTable
///
static PatchTable * Create(TopologyRefiner const & refiner,
Options options = Options(),
ConstIndexArray selectedFaces = ConstIndexArray());
public:
// PatchFaceTag
//
// This simple struct was previously used within the factory to take inventory of
// various kinds of patches to fully allocate buffers prior to populating them. It
// was not intended to be exposed as part of the public interface.
//
// It is no longer used internally and is being kept here to respect preservation
// of the public interface, but it will be deprecated at the earliest opportunity.
//
/// \brief Obsolete internal struct not intended for public use -- due to
/// be deprecated.
//
struct PatchFaceTag {
public:
unsigned int _hasPatch : 1;
unsigned int _isRegular : 1;
unsigned int _transitionMask : 4;
unsigned int _boundaryMask : 4;
unsigned int _boundaryIndex : 2;
unsigned int _boundaryCount : 3;
unsigned int _hasBoundaryEdge : 3;
unsigned int _isSingleCrease : 1;
void clear();
void assignBoundaryPropertiesFromEdgeMask(int boundaryEdgeMask);
void assignBoundaryPropertiesFromVertexMask(int boundaryVertexMask);
void assignTransitionPropertiesFromEdgeMask(int boundaryVertexMask);
};
typedef std::vector<PatchFaceTag> PatchTagVector;
};
template <> inline void PatchTableFactory::Options::SetPatchPrecision<float>() {
patchPrecisionDouble = false;
}
template <> inline void PatchTableFactory::Options::SetFVarPatchPrecision<float>() {
fvarPatchPrecisionDouble = false;
}
template <> inline void PatchTableFactory::Options::SetPatchPrecision<double>() {
patchPrecisionDouble = true;
}
template <> inline void PatchTableFactory::Options::SetFVarPatchPrecision<double>() {
fvarPatchPrecisionDouble = true;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_TABLE_FACTORY_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/ptexIndices.h"
#include "../far/error.h"
#include "../vtr/level.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
PtexIndices::PtexIndices(TopologyRefiner const &refiner) {
initializePtexIndices(refiner);
}
PtexIndices::~PtexIndices() {
}
void
PtexIndices::initializePtexIndices(TopologyRefiner const &refiner) {
int regFaceSize = Sdc::SchemeTypeTraits::GetRegularFaceSize(
refiner.GetSchemeType());
Vtr::internal::Level const & coarseLevel = refiner.getLevel(0);
int nfaces = coarseLevel.getNumFaces();
_ptexIndices.resize(nfaces+1);
int ptexID=0;
for (int i = 0; i < nfaces; ++i) {
_ptexIndices[i] = ptexID;
Vtr::ConstIndexArray fverts = coarseLevel.getFaceVertices(i);
ptexID += fverts.size()==regFaceSize ? 1 : fverts.size();
}
// last entry contains the number of ptex texture faces
_ptexIndices[nfaces]=ptexID;
}
int
PtexIndices::GetNumFaces() const {
return _ptexIndices.back();
}
int
PtexIndices::GetFaceId(Index f) const {
assert(f<(int)_ptexIndices.size());
return _ptexIndices[f];
}
namespace {
// Returns the face adjacent to 'face' along edge 'edge'
inline Index
getAdjacentFace(Vtr::internal::Level const & level, Index edge, Index face) {
Far::ConstIndexArray adjFaces = level.getEdgeFaces(edge);
if (adjFaces.size()!=2) {
return -1;
}
return (adjFaces[0]==face) ? adjFaces[1] : adjFaces[0];
}
}
void
PtexIndices::GetAdjacency(
TopologyRefiner const &refiner,
int face, int quadrant,
int adjFaces[4], int adjEdges[4]) const {
int regFaceSize =
Sdc::SchemeTypeTraits::GetRegularFaceSize(refiner.GetSchemeType());
Vtr::internal::Level const & level = refiner.getLevel(0);
ConstIndexArray fedges = level.getFaceEdges(face);
if (fedges.size() == regFaceSize) {
// Regular ptex quad face
for (int i=0; i<regFaceSize; ++i) {
int edge = fedges[i];
Index adjface = getAdjacentFace(level, edge, face);
if (adjface==-1) {
adjFaces[i] = -1; // boundary or non-manifold
adjEdges[i] = 0;
} else {
ConstIndexArray aedges = level.getFaceEdges(adjface);
if (aedges.size()==regFaceSize) {
adjFaces[i] = _ptexIndices[adjface];
adjEdges[i] = aedges.FindIndex(edge);
assert(adjEdges[i]!=-1);
} else {
// neighbor is a sub-face
adjFaces[i] = _ptexIndices[adjface] +
(aedges.FindIndex(edge)+1)%aedges.size();
adjEdges[i] = 3;
}
assert(adjFaces[i]!=-1);
}
}
if (regFaceSize == 3) {
adjFaces[3] = -1;
adjEdges[3] = 0;
}
} else if (regFaceSize == 4) {
// Ptex sub-face 'quadrant' (non-quad)
//
// Ptex adjacency pattern for non-quads:
//
// v2
/* o
// / \
// / \
// /0 3\
// / \
// o_ 1 2 _o
// / -_ _- \
// / 2 -o- 1 \
// /3 | 0\
// / 1|2 \
// / 0 | 3 \
// o----------o----------o
// v0 v1
*/
assert(quadrant>=0 && quadrant<fedges.size());
int nextQuadrant = (quadrant+1) % fedges.size(),
prevQuadrant = (quadrant+fedges.size()-1) % fedges.size();
{ // resolve neighbors within the sub-face (edges 1 & 2)
adjFaces[1] = _ptexIndices[face] + nextQuadrant;
adjEdges[1] = 2;
adjFaces[2] = _ptexIndices[face] + prevQuadrant;
adjEdges[2] = 1;
}
{ // resolve neighbor outside the sub-face (edge 0)
int edge0 = fedges[quadrant];
Index adjface0 = getAdjacentFace(level, edge0, face);
if (adjface0==-1) {
adjFaces[0] = -1; // boundary or non-manifold
adjEdges[0] = 0;
} else {
ConstIndexArray afedges = level.getFaceEdges(adjface0);
if (afedges.size()==4) {
adjFaces[0] = _ptexIndices[adjface0];
adjEdges[0] = afedges.FindIndexIn4Tuple(edge0);
} else {
int subedge = (afedges.FindIndex(edge0)+1)%afedges.size();
adjFaces[0] = _ptexIndices[adjface0] + subedge;
adjEdges[0] = 3;
}
assert(adjFaces[0]!=-1);
}
// resolve neighbor outside the sub-face (edge 3)
int edge3 = fedges[prevQuadrant];
Index adjface3 = getAdjacentFace(level, edge3, face);
if (adjface3==-1) {
adjFaces[3]=-1; // boundary or non-manifold
adjEdges[3]=0;
} else {
ConstIndexArray afedges = level.getFaceEdges(adjface3);
if (afedges.size()==4) {
adjFaces[3] = _ptexIndices[adjface3];
adjEdges[3] = afedges.FindIndexIn4Tuple(edge3);
} else {
int subedge = afedges.FindIndex(edge3);
adjFaces[3] = _ptexIndices[adjface3] + subedge;
adjEdges[3] = 0;
}
assert(adjFaces[3]!=-1);
}
}
} else {
Far::Error(FAR_RUNTIME_ERROR,
"Failure in PtexIndices::GetAdjacency() -- "
"irregular faces only supported for quad schemes.");
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,106 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_PTEX_INDICES_H
#define OPENSUBDIV3_FAR_PTEX_INDICES_H
#include "../version.h"
#include "../far/topologyRefiner.h"
#include "../far/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
///
/// \brief Object used to compute and query ptex face indices.
///
/// Given a refiner, constructing a PtexIndices object builds the mapping
/// from coarse faces to ptex ids. Once built, the object can be used to
/// query the mapping.
///
class PtexIndices {
public:
/// \brief Constructor
PtexIndices(TopologyRefiner const &refiner);
/// \brief Destructor
~PtexIndices();
//@{
///
/// Ptex
///
/// \brief Returns the number of ptex faces in the mesh
///
int GetNumFaces() const;
/// \brief Returns the ptex face index given a coarse face 'f' or -1
///
int GetFaceId(Index f) const;
/// \brief Returns ptex face adjacency information for a given coarse face
///
/// @param refiner refiner used to build this PtexIndices object.
///
/// @param face coarse face index
///
/// @param quadrant quadrant index if 'face' is not a quad (the local ptex
/// sub-face index). Must be less than the number of face
/// vertices.
///
/// @param adjFaces ptex face indices of adjacent faces
///
/// @param adjEdges ptex edge indices of adjacent faces
///
void GetAdjacency(
TopologyRefiner const &refiner,
int face, int quadrant,
int adjFaces[4], int adjEdges[4]) const;
//@}
private:
void initializePtexIndices(TopologyRefiner const &refiner);
private:
std::vector<Index> _ptexIndices;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PTEX_INDICES_H */

View File

@@ -0,0 +1,199 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_SPARSE_MATRIX_H
#define OPENSUBDIV3_FAR_SPARSE_MATRIX_H
#include "../version.h"
#include "../vtr/array.h"
#include <algorithm>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// SparseMatrix
//
// The SparseMatrix class is used by the PatchBuilder to store coefficients
// for a set of patch points derived from some other set of points -- usually
// the refined points in a subdivision level. The compressed sparse row
// format (CSR) is used as it provides us with stencils for points that
// correspond to rows and so can be more directly and efficiently copied.
//
// It has potential for other uses and so may eventually warrant a seperate
// header file of its own. For now, in keeping with the trend of exposing
// classes only where used, it is defined with the PatchBuilder.
//
// We may also want to explore the possibility of being able to assign
// static buffers as members here -- allowing common matrices to be set
// directly rather than repeatedly replicated.
//
template <typename REAL>
class SparseMatrix {
public:
typedef int column_type;
typedef REAL element_type;
public:
// Declaration and access methods:
SparseMatrix() : _numRows(0), _numColumns(0), _numElements(0) { }
int GetNumRows() const { return _numRows; }
int GetNumColumns() const { return _numColumns; }
int GetNumElements() const { return _numElements; }
int GetCapacity() const;
int GetRowSize(int rowIndex) const {
return _rowOffsets[rowIndex + 1] - _rowOffsets[rowIndex];
}
Vtr::ConstArray<column_type> GetRowColumns( int rowIndex) const {
return Vtr::ConstArray<column_type>(&_columns[_rowOffsets[rowIndex]],
GetRowSize(rowIndex));
}
Vtr::ConstArray<element_type> GetRowElements(int rowIndex) const {
return Vtr::ConstArray<element_type>(&_elements[_rowOffsets[rowIndex]],
GetRowSize(rowIndex));
}
Vtr::ConstArray<column_type> GetColumns() const {
return Vtr::ConstArray<column_type>(&_columns[0], GetNumElements());
}
Vtr::ConstArray<element_type> GetElements() const {
return Vtr::ConstArray<element_type>(&_elements[0], GetNumElements());
}
public:
// Modification methods
void Resize(int numRows, int numColumns, int numNonZeroEntriesToReserve);
void Copy(SparseMatrix const & srcMatrix);
void Swap(SparseMatrix & otherMatrix);
void SetRowSize(int rowIndex, int size);
Vtr::Array<column_type> SetRowColumns( int rowIndex) {
return Vtr::Array<column_type>(&_columns[_rowOffsets[rowIndex]],
GetRowSize(rowIndex));
}
Vtr::Array<element_type> SetRowElements(int rowIndex) {
return Vtr::Array<element_type>(&_elements[_rowOffsets[rowIndex]],
GetRowSize(rowIndex));
}
private:
// Simple dimensions:
int _numRows;
int _numColumns;
int _numElements;
std::vector<int> _rowOffsets; // remember one more entry here than rows
// XXXX (barfowl) - Note that the use of std::vector for the columns and
// element arrays was causing performance issues in the incremental
// resizing of consecutive rows, so we've been exploring alternatives...
std::vector<column_type> _columns;
std::vector<element_type> _elements;
};
template <typename REAL>
inline int
SparseMatrix<REAL>::GetCapacity() const {
return (int) _elements.size();
}
template <typename REAL>
inline void
SparseMatrix<REAL>::Resize(int numRows, int numCols, int numElementsToReserve) {
_numRows = numRows;
_numColumns = numCols;
_numElements = 0;
_rowOffsets.resize(0);
_rowOffsets.resize(_numRows + 1, -1);
_rowOffsets[0] = 0;
if (numElementsToReserve > GetCapacity()) {
_columns.resize(numElementsToReserve);
_elements.resize(numElementsToReserve);
}
}
template <typename REAL>
inline void
SparseMatrix<REAL>::SetRowSize(int rowIndex, int rowSize) {
assert(_rowOffsets[rowIndex] == _numElements);
int & newVectorSize = _rowOffsets[rowIndex + 1];
newVectorSize = _rowOffsets[rowIndex] + rowSize;
_numElements = newVectorSize;
if (newVectorSize > GetCapacity()) {
_columns.resize(newVectorSize);
_elements.resize(newVectorSize);
}
}
template <typename REAL>
inline void
SparseMatrix<REAL>::Copy(SparseMatrix const & src) {
_numRows = src._numRows;
_numColumns = src._numColumns;
_rowOffsets = src._rowOffsets;
_numElements = src._numElements;
_columns = src._columns;
_elements = src._elements;
}
template <typename REAL>
inline void
SparseMatrix<REAL>::Swap(SparseMatrix & other) {
std::swap(_numRows, other._numRows);
std::swap(_numColumns, other._numColumns);
std::swap(_numElements, other._numElements);
_rowOffsets.swap(other._rowOffsets);
_columns.swap(other._columns);
_elements.swap(other._elements);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_SPARSE_MATRIX_H */

View File

@@ -0,0 +1,606 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/stencilBuilder.h"
#include "../far/topologyRefiner.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
namespace {
#ifdef __INTEL_COMPILER
#pragma warning (push)
#pragma warning disable 1572
#endif
template <typename REAL>
inline bool isWeightZero(REAL w) { return (w == (REAL)0.0); }
#ifdef __INTEL_COMPILER
#pragma warning (pop)
#endif
}
template <typename REAL>
struct Point1stDerivWeight {
REAL p;
REAL du;
REAL dv;
Point1stDerivWeight()
: p(0.0f), du(0.0f), dv(0.0f)
{ }
Point1stDerivWeight(REAL w)
: p(w), du(w), dv(w)
{ }
Point1stDerivWeight(REAL w, REAL wDu, REAL wDv)
: p(w), du(wDu), dv(wDv)
{ }
friend Point1stDerivWeight operator*(Point1stDerivWeight lhs,
Point1stDerivWeight const& rhs) {
lhs.p *= rhs.p;
lhs.du *= rhs.du;
lhs.dv *= rhs.dv;
return lhs;
}
Point1stDerivWeight& operator+=(Point1stDerivWeight const& rhs) {
p += rhs.p;
du += rhs.du;
dv += rhs.dv;
return *this;
}
};
template <typename REAL>
struct Point2ndDerivWeight {
REAL p;
REAL du;
REAL dv;
REAL duu;
REAL duv;
REAL dvv;
Point2ndDerivWeight()
: p(0.0f), du(0.0f), dv(0.0f), duu(0.0f), duv(0.0f), dvv(0.0f)
{ }
Point2ndDerivWeight(REAL w)
: p(w), du(w), dv(w), duu(w), duv(w), dvv(w)
{ }
Point2ndDerivWeight(REAL w, REAL wDu, REAL wDv,
REAL wDuu, REAL wDuv, REAL wDvv)
: p(w), du(wDu), dv(wDv), duu(wDuu), duv(wDuv), dvv(wDvv)
{ }
friend Point2ndDerivWeight operator*(Point2ndDerivWeight lhs,
Point2ndDerivWeight const& rhs) {
lhs.p *= rhs.p;
lhs.du *= rhs.du;
lhs.dv *= rhs.dv;
lhs.duu *= rhs.duu;
lhs.duv *= rhs.duv;
lhs.dvv *= rhs.dvv;
return lhs;
}
Point2ndDerivWeight& operator+=(Point2ndDerivWeight const& rhs) {
p += rhs.p;
du += rhs.du;
dv += rhs.dv;
duu += rhs.duu;
duv += rhs.duv;
dvv += rhs.dvv;
return *this;
}
};
/// Stencil table constructor set.
///
template <typename REAL>
class WeightTable {
public:
WeightTable(int coarseVerts,
bool genCtrlVertStencils,
bool compactWeights)
: _size(0)
, _lastOffset(0)
, _coarseVertCount(coarseVerts)
, _compactWeights(compactWeights)
{
// These numbers were chosen by profiling production assets at uniform
// level 3.
size_t n = std::max(coarseVerts,
std::min(int(5*1024*1024),
coarseVerts*2));
_dests.reserve(n);
_sources.reserve(n);
_weights.reserve(n);
if (!genCtrlVertStencils)
return;
// Generate trivial control vert stencils
_sources.resize(coarseVerts);
_weights.resize(coarseVerts);
_dests.resize(coarseVerts);
_indices.resize(coarseVerts);
_sizes.resize(coarseVerts);
for (int i = 0; i < coarseVerts; i++) {
_indices[i] = i;
_sizes[i] = 1;
_dests[i] = i;
_sources[i] = i;
_weights[i] = 1.0;
}
_size = static_cast<int>(_sources.size());
_lastOffset = _size - 1;
}
template <class W, class WACCUM>
void AddWithWeight(int src, int dest, W weight, WACCUM weights)
{
// Factorized stencils are expressed purely in terms of the control
// mesh verts. Without this flattening, level_i's weights would point
// to level_i-1, which would point to level_i-2, until the final level
// points to the control verts.
//
// So here, we check if the incoming vert (src) is in the control mesh,
// if it is, we can simply merge it without attempting to resolve it
// first.
if (src < _coarseVertCount) {
merge(src, dest, weight, W(1.0), _lastOffset, _size, weights);
return;
}
// src is not in the control mesh, so resolve all contributing coarse
// verts (src itself is made up of many control vert weights).
//
// Find the src stencil and number of contributing CVs.
int len = _sizes[src];
int start = _indices[src];
for (int i = start; i < start+len; i++) {
// Invariant: by processing each level in order and each vertex in
// dependent order, any src stencil vertex reference is guaranteed
// to consist only of coarse verts: therefore resolving src verts
// must yield verts in the coarse mesh.
assert(_sources[i] < _coarseVertCount);
// Merge each of src's contributing verts into this stencil.
merge(_sources[i], dest, weights.Get(i), weight,
_lastOffset, _size, weights);
}
}
class Point1stDerivAccumulator {
WeightTable* _tbl;
public:
Point1stDerivAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(Point1stDerivWeight<REAL> weight) {
_tbl->_weights.push_back(weight.p);
_tbl->_duWeights.push_back(weight.du);
_tbl->_dvWeights.push_back(weight.dv);
}
void Add(size_t i, Point1stDerivWeight<REAL> weight) {
_tbl->_weights[i] += weight.p;
_tbl->_duWeights[i] += weight.du;
_tbl->_dvWeights[i] += weight.dv;
}
Point1stDerivWeight<REAL> Get(size_t index) {
return Point1stDerivWeight<REAL>(_tbl->_weights[index],
_tbl->_duWeights[index],
_tbl->_dvWeights[index]);
}
};
Point1stDerivAccumulator GetPoint1stDerivAccumulator() {
return Point1stDerivAccumulator(this);
};
class Point2ndDerivAccumulator {
WeightTable* _tbl;
public:
Point2ndDerivAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(Point2ndDerivWeight<REAL> weight) {
_tbl->_weights.push_back(weight.p);
_tbl->_duWeights.push_back(weight.du);
_tbl->_dvWeights.push_back(weight.dv);
_tbl->_duuWeights.push_back(weight.duu);
_tbl->_duvWeights.push_back(weight.duv);
_tbl->_dvvWeights.push_back(weight.dvv);
}
void Add(size_t i, Point2ndDerivWeight<REAL> weight) {
_tbl->_weights[i] += weight.p;
_tbl->_duWeights[i] += weight.du;
_tbl->_dvWeights[i] += weight.dv;
_tbl->_duuWeights[i] += weight.duu;
_tbl->_duvWeights[i] += weight.duv;
_tbl->_dvvWeights[i] += weight.dvv;
}
Point2ndDerivWeight<REAL> Get(size_t index) {
return Point2ndDerivWeight<REAL>(_tbl->_weights[index],
_tbl->_duWeights[index],
_tbl->_dvWeights[index],
_tbl->_duuWeights[index],
_tbl->_duvWeights[index],
_tbl->_dvvWeights[index]);
}
};
Point2ndDerivAccumulator GetPoint2ndDerivAccumulator() {
return Point2ndDerivAccumulator(this);
};
class ScalarAccumulator {
WeightTable* _tbl;
public:
ScalarAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(REAL weight) {
_tbl->_weights.push_back(weight);
}
void Add(size_t i, REAL w) {
_tbl->_weights[i] += w;
}
REAL Get(size_t index) {
return _tbl->_weights[index];
}
};
ScalarAccumulator GetScalarAccumulator() {
return ScalarAccumulator(this);
};
std::vector<int> const&
GetOffsets() const { return _indices; }
std::vector<int> const&
GetSizes() const { return _sizes; }
std::vector<int> const&
GetSources() const { return _sources; }
std::vector<REAL> const&
GetWeights() const { return _weights; }
std::vector<REAL> const&
GetDuWeights() const { return _duWeights; }
std::vector<REAL> const&
GetDvWeights() const { return _dvWeights; }
std::vector<REAL> const&
GetDuuWeights() const { return _duuWeights; }
std::vector<REAL> const&
GetDuvWeights() const { return _duvWeights; }
std::vector<REAL> const&
GetDvvWeights() const { return _dvvWeights; }
void SetCoarseVertCount(int numVerts) {
_coarseVertCount = numVerts;
}
private:
// Merge a vertex weight into the stencil table, if there is an existing
// weight for a given source vertex it will be combined.
//
// PERFORMANCE: caution, this function is super hot.
template <class W, class WACCUM>
void merge(int src, int dst, W weight,
// Delaying weight*factor multiplication hides memory latency of
// accessing weight[i], yielding more stable performance.
W weightFactor,
// Similarly, passing offset & tableSize as params yields higher
// performance than accessing the class members directly.
int lastOffset, int tableSize, WACCUM weights)
{
// The lastOffset is the vertex we're currently processing, by
// leveraging this we need not lookup the dest stencil size or offset.
//
// Additionally, if the client does not want the resulting verts
// compacted, do not attempt to combine weights.
if (_compactWeights && !_dests.empty() && _dests[lastOffset] == dst) {
// tableSize is exactly _sources.size(), but using tableSize is
// significantly faster.
for (int i = lastOffset; i < tableSize; i++) {
// If we find an existing vertex that matches src, we need to
// combine the weights to avoid duplicate entries for src.
if (_sources[i] == src) {
weights.Add(i, weight*weightFactor);
return;
}
}
}
// We haven't seen src yet, insert it as a new vertex weight.
add(src, dst, weight*weightFactor, weights);
}
// Add a new vertex weight to the stencil table.
template <class W, class WACCUM>
void add(int src, int dst, W weight, WACCUM weights)
{
// The _dests array has num(weights) elements mapping each individual
// element back to a specific stencil. The array is constructed in such
// a way that the current stencil being built is always at the end of
// the array, so if the dests array is empty or back() doesn't match
// dst, then we just started building a new stencil.
if (_dests.empty() || dst != _dests.back()) {
// _indices and _sizes always have num(stencils) elements so that
// stencils can be directly looked up by their index in these
// arrays. So here, ensure that they are large enough to hold the
// new stencil about to be built.
if (dst+1 > (int)_indices.size()) {
_indices.resize(dst+1);
_sizes.resize(dst+1);
}
// Initialize the new stencil's meta-data (offset, size).
_indices[dst] = static_cast<int>(_sources.size());
_sizes[dst] = 0;
// Keep track of where the current stencil begins, which lets us
// avoid having to look it up later.
_lastOffset = static_cast<int>(_sources.size());
}
// Cache the number of elements as an optimization, it's faster than
// calling size() on any of the vectors.
_size++;
// Increment the current stencil element size.
_sizes[dst]++;
// Track this element as belonging to the stencil "dst".
_dests.push_back(dst);
// Store the actual stencil data.
_sources.push_back(src);
weights.PushBack(weight);
}
// The following vectors are explicitly stored as non-interleaved elements
// to reduce cache misses.
// Stencil to destination vertex map.
std::vector<int> _dests;
// The actual stencil data.
std::vector<int> _sources;
std::vector<REAL> _weights;
std::vector<REAL> _duWeights;
std::vector<REAL> _dvWeights;
std::vector<REAL> _duuWeights;
std::vector<REAL> _duvWeights;
std::vector<REAL> _dvvWeights;
// Index data used to recover stencil-to-vertex mapping.
std::vector<int> _indices;
std::vector<int> _sizes;
// Acceleration members to avoid pointer chasing and reverse loops.
int _size;
int _lastOffset;
int _coarseVertCount;
bool _compactWeights;
};
template <typename REAL>
StencilBuilder<REAL>::StencilBuilder(int coarseVertCount,
bool genCtrlVertStencils,
bool compactWeights)
: _weightTable(new WeightTable<REAL>(coarseVertCount,
genCtrlVertStencils,
compactWeights))
{
}
template <typename REAL>
StencilBuilder<REAL>::~StencilBuilder()
{
delete _weightTable;
}
template <typename REAL>
size_t
StencilBuilder<REAL>::GetNumVerticesTotal() const
{
return _weightTable->GetWeights().size();
}
template <typename REAL>
int
StencilBuilder<REAL>::GetNumVertsInStencil(size_t stencilIndex) const
{
if (stencilIndex > _weightTable->GetSizes().size() - 1)
return 0;
return (int)_weightTable->GetSizes()[stencilIndex];
}
template <typename REAL>
void
StencilBuilder<REAL>::SetCoarseVertCount(int numVerts)
{
_weightTable->SetCoarseVertCount(numVerts);
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilOffsets() const {
return _weightTable->GetOffsets();
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilSizes() const {
return _weightTable->GetSizes();
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilSources() const {
return _weightTable->GetSources();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilWeights() const {
return _weightTable->GetWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuWeights() const {
return _weightTable->GetDuWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDvWeights() const {
return _weightTable->GetDvWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuuWeights() const {
return _weightTable->GetDuuWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuvWeights() const {
return _weightTable->GetDuvWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDvvWeights() const {
return _weightTable->GetDvvWeights();
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(Index const & src, REAL weight)
{
// Ignore no-op weights.
if (isWeightZero(weight)) {
return;
}
_owner->_weightTable->AddWithWeight(src._index, _index, weight,
_owner->_weightTable->GetScalarAccumulator());
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src, REAL weight)
{
if (isWeightZero(weight)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
REAL wgt = weight * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetScalarAccumulator());
}
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv)
{
if (isWeightZero(weight) && isWeightZero(du) && isWeightZero(dv)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
Point1stDerivWeight<REAL> wgt = Point1stDerivWeight<REAL>(weight, du, dv) * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetPoint1stDerivAccumulator());
}
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv, REAL duu, REAL duv, REAL dvv)
{
if (isWeightZero(weight) && isWeightZero(du) && isWeightZero(dv) &&
isWeightZero(duu) && isWeightZero(duv) && isWeightZero(dvv)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
Point2ndDerivWeight<REAL> wgt = Point2ndDerivWeight<REAL>(weight, du, dv, duu, duv, dvv) * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetPoint2ndDerivAccumulator());
}
}
template class StencilBuilder<float>;
template class StencilBuilder<double>;
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,115 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_STENCILBUILDER_H
#define OPENSUBDIV3_FAR_STENCILBUILDER_H
#include <vector>
#include "../version.h"
#include "../far/stencilTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
template <typename REAL> class WeightTable;
template <typename REAL>
class StencilBuilder {
public:
StencilBuilder(int coarseVertCount,
bool genCtrlVertStencils=true,
bool compactWeights=true);
~StencilBuilder();
// TODO: noncopyable.
size_t GetNumVerticesTotal() const;
int GetNumVertsInStencil(size_t stencilIndex) const;
void SetCoarseVertCount(int numVerts);
// Mapping from stencil[i] to its starting offset in the sources[] and weights[] arrays;
std::vector<int> const& GetStencilOffsets() const;
// The number of contributing sources and weights in stencil[i]
std::vector<int> const& GetStencilSizes() const;
// The absolute source vertex offsets.
std::vector<int> const& GetStencilSources() const;
// The individual vertex weights, each weight is paired with one source.
std::vector<REAL> const& GetStencilWeights() const;
std::vector<REAL> const& GetStencilDuWeights() const;
std::vector<REAL> const& GetStencilDvWeights() const;
std::vector<REAL> const& GetStencilDuuWeights() const;
std::vector<REAL> const& GetStencilDuvWeights() const;
std::vector<REAL> const& GetStencilDvvWeights() const;
// Vertex Facade.
class Index {
public:
Index(StencilBuilder* owner, int index)
: _owner(owner)
, _index(index)
{}
// Add with point/vertex weight only.
void AddWithWeight(Index const & src, REAL weight);
void AddWithWeight(StencilReal<REAL> const& src, REAL weight);
// Add with first derivative.
void AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv);
// Add with first and second derivatives.
void AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv, REAL duu, REAL duv, REAL dvv);
Index operator[](int index) const {
return Index(_owner, index+_index);
}
int GetOffset() const { return _index; }
void Clear() {/*nothing to do here*/}
private:
StencilBuilder* _owner;
int _index;
};
private:
WeightTable<REAL>* _weightTable;
};
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv
#endif // FAR_STENCILBUILDER_H

View File

@@ -0,0 +1,234 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../version.h"
#include "../far/stencilTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace {
template <typename REAL>
void
copyStencilData(int numControlVerts,
bool includeCoarseVerts,
size_t firstOffset,
std::vector<int> const* offsets,
std::vector<int> * _offsets,
std::vector<int> const* sizes,
std::vector<int> * _sizes,
std::vector<int> const* sources,
std::vector<int> * _sources,
std::vector<REAL> const* weights,
std::vector<REAL> * _weights,
std::vector<REAL> const* duWeights=NULL,
std::vector<REAL> * _duWeights=NULL,
std::vector<REAL> const* dvWeights=NULL,
std::vector<REAL> * _dvWeights=NULL,
std::vector<REAL> const* duuWeights=NULL,
std::vector<REAL> * _duuWeights=NULL,
std::vector<REAL> const* duvWeights=NULL,
std::vector<REAL> * _duvWeights=NULL,
std::vector<REAL> const* dvvWeights=NULL,
std::vector<REAL> * _dvvWeights=NULL) {
size_t start = includeCoarseVerts ? 0 : firstOffset;
_offsets->resize(offsets->size());
_sizes->resize(sizes->size());
_sources->resize(sources->size());
_weights->resize(weights->size());
if (_duWeights)
_duWeights->resize(duWeights->size());
if (_dvWeights)
_dvWeights->resize(dvWeights->size());
if (_duuWeights)
_duuWeights->resize(duuWeights->size());
if (_duvWeights)
_duvWeights->resize(duvWeights->size());
if (_dvvWeights)
_dvvWeights->resize(dvvWeights->size());
// The stencils are probably not in order, so we must copy/sort them.
// Note here that loop index 'i' represents stencil_i for vertex_i.
int curOffset = 0;
size_t stencilCount = 0,
weightCount = 0;
for ( size_t i=start; i<offsets->size(); i++ ) {
// Once we've copied out all the control verts, jump to the offset
// where the actual stencils begin.
if (includeCoarseVerts && (int)i == numControlVerts)
i = firstOffset;
// Copy the stencil.
int sz = (*sizes)[i];
int off = (*offsets)[i];
(*_offsets)[stencilCount] = curOffset;
(*_sizes)[stencilCount] = sz;
std::memcpy(&(*_sources)[curOffset],
&(*sources)[off], sz*sizeof(int));
std::memcpy(&(*_weights)[curOffset],
&(*weights)[off], sz*sizeof(REAL));
if (_duWeights && !_duWeights->empty()) {
std::memcpy(&(*_duWeights)[curOffset],
&(*duWeights)[off], sz*sizeof(REAL));
}
if (_dvWeights && !_dvWeights->empty()) {
std::memcpy(&(*_dvWeights)[curOffset],
&(*dvWeights)[off], sz*sizeof(REAL));
}
if (_duuWeights && !_duuWeights->empty()) {
std::memcpy(&(*_duuWeights)[curOffset],
&(*duuWeights)[off], sz*sizeof(REAL));
}
if (_duvWeights && !_duvWeights->empty()) {
std::memcpy(&(*_duvWeights)[curOffset],
&(*duvWeights)[off], sz*sizeof(REAL));
}
if (_dvvWeights && !_dvvWeights->empty()) {
std::memcpy(&(*_dvvWeights)[curOffset],
&(*dvvWeights)[off], sz*sizeof(REAL));
}
curOffset += sz;
stencilCount++;
weightCount += sz;
}
_offsets->resize(stencilCount);
_sizes->resize(stencilCount);
_sources->resize(weightCount);
if (_duWeights && !_duWeights->empty())
_duWeights->resize(weightCount);
if (_dvWeights && !_dvWeights->empty())
_dvWeights->resize(weightCount);
if (_duuWeights && !_duuWeights->empty())
_duuWeights->resize(weightCount);
if (_duvWeights && !_duvWeights->empty())
_duvWeights->resize(weightCount);
if (_dvvWeights && !_dvvWeights->empty())
_dvvWeights->resize(weightCount);
}
};
template <typename REAL>
StencilTableReal<REAL>::StencilTableReal(int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<REAL> const& weights,
bool includeCoarseVerts,
size_t firstOffset)
: _numControlVertices(numControlVerts) {
copyStencilData(numControlVerts,
includeCoarseVerts,
firstOffset,
&offsets, &_offsets,
&sizes, &_sizes,
&sources, &_indices,
&weights, &_weights);
}
template <typename REAL>
void
StencilTableReal<REAL>::Clear() {
_numControlVertices=0;
_sizes.clear();
_offsets.clear();
_indices.clear();
_weights.clear();
}
template <typename REAL>
LimitStencilTableReal<REAL>::LimitStencilTableReal(
int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<REAL> const& weights,
std::vector<REAL> const& duWeights,
std::vector<REAL> const& dvWeights,
std::vector<REAL> const& duuWeights,
std::vector<REAL> const& duvWeights,
std::vector<REAL> const& dvvWeights,
bool includeCoarseVerts,
size_t firstOffset)
: StencilTableReal<REAL>(numControlVerts) {
copyStencilData(numControlVerts,
includeCoarseVerts,
firstOffset,
&offsets, &this->_offsets,
&sizes, &this->_sizes,
&sources, &this->_indices,
&weights, &this->_weights,
&duWeights, &_duWeights,
&dvWeights, &_dvWeights,
&duuWeights, &_duuWeights,
&duvWeights, &_duvWeights,
&dvvWeights, &_dvvWeights);
}
template <typename REAL>
void
LimitStencilTableReal<REAL>::Clear() {
StencilTableReal<REAL>::Clear();
_duWeights.clear();
_dvWeights.clear();
_duuWeights.clear();
_duvWeights.clear();
_dvvWeights.clear();
}
//
// Explicit instantiation for float and double:
//
template class StencilReal<float>;
template class StencilReal<double>;
template class LimitStencilReal<float>;
template class LimitStencilReal<double>;
template class StencilTableReal<float>;
template class StencilTableReal<double>;
template class LimitStencilTableReal<float>;
template class LimitStencilTableReal<double>;
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,793 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_STENCILTABLE_H
#define OPENSUBDIV3_FAR_STENCILTABLE_H
#include "../version.h"
#include "../far/types.h"
#include <cassert>
#include <cstring>
#include <vector>
#include <iostream>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
// Forward declarations for friends:
class PatchTableBuilder;
template <typename REAL> class StencilTableFactoryReal;
template <typename REAL> class LimitStencilTableFactoryReal;
/// \brief Vertex stencil descriptor
///
/// Allows access and manipulation of a single stencil in a StencilTable.
///
template <typename REAL>
class StencilReal {
public:
/// \brief Default constructor
StencilReal() {}
/// \brief Constructor
///
/// @param size Table pointer to the size of the stencil
///
/// @param indices Table pointer to the vertex indices of the stencil
///
/// @param weights Table pointer to the vertex weights of the stencil
///
StencilReal(int * size, Index * indices, REAL * weights)
: _size(size), _indices(indices), _weights(weights) { }
/// \brief Copy constructor
StencilReal(StencilReal const & other) {
_size = other._size;
_indices = other._indices;
_weights = other._weights;
}
/// \brief Returns the size of the stencil
int GetSize() const {
return *_size;
}
/// \brief Returns the size of the stencil as a pointer
int * GetSizePtr() const {
return _size;
}
/// \brief Returns the control vertices' indices
Index const * GetVertexIndices() const {
return _indices;
}
/// \brief Returns the interpolation weights
REAL const * GetWeights() const {
return _weights;
}
/// \brief Advance to the next stencil in the table
void Next() {
int stride = *_size;
++_size;
_indices += stride;
_weights += stride;
}
protected:
friend class StencilTableFactoryReal<REAL>;
friend class LimitStencilTableFactoryReal<REAL>;
int * _size;
Index * _indices;
REAL * _weights;
};
/// \brief Vertex stencil class wrapping the template for compatibility.
///
class Stencil : public StencilReal<float> {
protected:
typedef StencilReal<float> BaseStencil;
public:
Stencil() : BaseStencil() { }
Stencil(BaseStencil const & other) : BaseStencil(other) { }
Stencil(int * size, Index * indices, float * weights)
: BaseStencil(size, indices, weights) { }
};
/// \brief Table of subdivision stencils.
///
/// Stencils are the most direct method of evaluation of locations on the limit
/// of a surface. Every point of a limit surface can be computed by linearly
/// blending a collection of coarse control vertices.
///
/// A stencil assigns a series of control vertex indices with a blending weight
/// that corresponds to a unique parametric location of the limit surface. When
/// the control vertices move in space, the limit location can be very efficiently
/// recomputed simply by applying the blending weights to the series of coarse
/// control vertices.
///
template <typename REAL>
class StencilTableReal {
protected:
StencilTableReal(int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<REAL> const& weights,
bool includeCoarseVerts,
size_t firstOffset);
public:
virtual ~StencilTableReal() {};
/// \brief Returns the number of stencils in the table
int GetNumStencils() const {
return (int)_sizes.size();
}
/// \brief Returns the number of control vertices indexed in the table
int GetNumControlVertices() const {
return _numControlVertices;
}
/// \brief Returns a Stencil at index i in the table
StencilReal<REAL> GetStencil(Index i) const;
/// \brief Returns the number of control vertices of each stencil in the table
std::vector<int> const & GetSizes() const {
return _sizes;
}
/// \brief Returns the offset to a given stencil (factory may leave empty)
std::vector<Index> const & GetOffsets() const {
return _offsets;
}
/// \brief Returns the indices of the control vertices
std::vector<Index> const & GetControlIndices() const {
return _indices;
}
/// \brief Returns the stencil interpolation weights
std::vector<REAL> const & GetWeights() const {
return _weights;
}
/// \brief Returns the stencil at index i in the table
StencilReal<REAL> operator[] (Index index) const;
/// \brief Updates point values based on the control values
///
/// \note The destination buffers are assumed to have allocated at least
/// \c GetNumStencils() elements.
///
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param dstValues Destination buffer for the interpolated primvar data
///
/// @param start Index of first destination value to update
///
/// @param end Index of last destination value to update
///
template <class T, class U>
void UpdateValues(T const &srcValues, U &dstValues, Index start=-1, Index end=-1) const {
this->update(srcValues, dstValues, _weights, start, end);
}
template <class T1, class T2, class U>
void UpdateValues(T1 const &srcBase, int numBase, T2 const &srcRef,
U &dstValues, Index start=-1, Index end=-1) const {
this->update(srcBase, numBase, srcRef, dstValues, _weights, start, end);
}
// Pointer interface for backward compatibility
template <class T, class U>
void UpdateValues(T const *src, U *dst, Index start=-1, Index end=-1) const {
this->update(src, dst, _weights, start, end);
}
template <class T1, class T2, class U>
void UpdateValues(T1 const *srcBase, int numBase, T2 const *srcRef,
U *dst, Index start=-1, Index end=-1) const {
this->update(srcBase, numBase, srcRef, dst, _weights, start, end);
}
/// \brief Clears the stencils from the table
void Clear();
protected:
// Update values by applying cached stencil weights to new control values
template <class T, class U>
void update( T const &srcValues, U &dstValues,
std::vector<REAL> const & valueWeights, Index start, Index end) const;
template <class T1, class T2, class U>
void update( T1 const &srcBase, int numBase, T2 const &srcRef, U &dstValues,
std::vector<REAL> const & valueWeights, Index start, Index end) const;
// Populate the offsets table from the stencil sizes in _sizes (factory helper)
void generateOffsets();
// Resize the table arrays (factory helper)
void resize(int nstencils, int nelems);
// Reserves the table arrays (factory helper)
void reserve(int nstencils, int nelems);
// Reallocates the table arrays to remove excess capacity (factory helper)
void shrinkToFit();
// Performs any final operations on internal tables (factory helper)
void finalize();
protected:
StencilTableReal() : _numControlVertices(0) {}
StencilTableReal(int numControlVerts)
: _numControlVertices(numControlVerts)
{ }
friend class StencilTableFactoryReal<REAL>;
friend class Far::PatchTableBuilder;
int _numControlVertices; // number of control vertices
std::vector<int> _sizes; // number of coefficients for each stencil
std::vector<Index> _offsets, // offset to the start of each stencil
_indices; // indices of contributing coarse vertices
std::vector<REAL> _weights; // stencil weight coefficients
};
/// \brief Stencil table class wrapping the template for compatibility.
///
class StencilTable : public StencilTableReal<float> {
protected:
typedef StencilTableReal<float> BaseTable;
public:
Stencil GetStencil(Index index) const {
return Stencil(BaseTable::GetStencil(index));
}
Stencil operator[] (Index index) const {
return Stencil(BaseTable::GetStencil(index));
}
protected:
StencilTable() : BaseTable() { }
StencilTable(int numControlVerts) : BaseTable(numControlVerts) { }
StencilTable(int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<float> const& weights,
bool includeCoarseVerts,
size_t firstOffset)
: BaseTable(numControlVerts, offsets,
sizes, sources, weights, includeCoarseVerts, firstOffset) { }
};
/// \brief Limit point stencil descriptor
///
template <typename REAL>
class LimitStencilReal : public StencilReal<REAL> {
public:
/// \brief Constructor
///
/// @param size Table pointer to the size of the stencil
///
/// @param indices Table pointer to the vertex indices of the stencil
///
/// @param weights Table pointer to the vertex weights of the stencil
///
/// @param duWeights Table pointer to the 'u' derivative weights
///
/// @param dvWeights Table pointer to the 'v' derivative weights
///
/// @param duuWeights Table pointer to the 'uu' derivative weights
///
/// @param duvWeights Table pointer to the 'uv' derivative weights
///
/// @param dvvWeights Table pointer to the 'vv' derivative weights
///
LimitStencilReal( int* size,
Index * indices,
REAL * weights,
REAL * duWeights=0,
REAL * dvWeights=0,
REAL * duuWeights=0,
REAL * duvWeights=0,
REAL * dvvWeights=0)
: StencilReal<REAL>(size, indices, weights),
_duWeights(duWeights),
_dvWeights(dvWeights),
_duuWeights(duuWeights),
_duvWeights(duvWeights),
_dvvWeights(dvvWeights) {
}
/// \brief Returns the u derivative weights
REAL const * GetDuWeights() const {
return _duWeights;
}
/// \brief Returns the v derivative weights
REAL const * GetDvWeights() const {
return _dvWeights;
}
/// \brief Returns the uu derivative weights
REAL const * GetDuuWeights() const {
return _duuWeights;
}
/// \brief Returns the uv derivative weights
REAL const * GetDuvWeights() const {
return _duvWeights;
}
/// \brief Returns the vv derivative weights
REAL const * GetDvvWeights() const {
return _dvvWeights;
}
/// \brief Advance to the next stencil in the table
void Next() {
int stride = *this->_size;
++this->_size;
this->_indices += stride;
this->_weights += stride;
if (_duWeights) _duWeights += stride;
if (_dvWeights) _dvWeights += stride;
if (_duuWeights) _duuWeights += stride;
if (_duvWeights) _duvWeights += stride;
if (_dvvWeights) _dvvWeights += stride;
}
private:
friend class StencilTableFactoryReal<REAL>;
friend class LimitStencilTableFactoryReal<REAL>;
REAL * _duWeights, // pointer to stencil u derivative limit weights
* _dvWeights, // pointer to stencil v derivative limit weights
* _duuWeights, // pointer to stencil uu derivative limit weights
* _duvWeights, // pointer to stencil uv derivative limit weights
* _dvvWeights; // pointer to stencil vv derivative limit weights
};
/// \brief Limit point stencil class wrapping the template for compatibility.
///
class LimitStencil : public LimitStencilReal<float> {
protected:
typedef LimitStencilReal<float> BaseStencil;
public:
LimitStencil(BaseStencil const & other) : BaseStencil(other) { }
LimitStencil(int* size, Index * indices, float * weights,
float * duWeights=0, float * dvWeights=0,
float * duuWeights=0, float * duvWeights=0, float * dvvWeights=0)
: BaseStencil(size, indices, weights,
duWeights, dvWeights, duuWeights, duvWeights, dvvWeights) { }
};
/// \brief Table of limit subdivision stencils.
///
template <typename REAL>
class LimitStencilTableReal : public StencilTableReal<REAL> {
protected:
LimitStencilTableReal(
int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<REAL> const& weights,
std::vector<REAL> const& duWeights,
std::vector<REAL> const& dvWeights,
std::vector<REAL> const& duuWeights,
std::vector<REAL> const& duvWeights,
std::vector<REAL> const& dvvWeights,
bool includeCoarseVerts,
size_t firstOffset);
public:
/// \brief Returns a LimitStencil at index i in the table
LimitStencilReal<REAL> GetLimitStencil(Index i) const;
/// \brief Returns the limit stencil at index i in the table
LimitStencilReal<REAL> operator[] (Index index) const;
/// \brief Returns the 'u' derivative stencil interpolation weights
std::vector<REAL> const & GetDuWeights() const {
return _duWeights;
}
/// \brief Returns the 'v' derivative stencil interpolation weights
std::vector<REAL> const & GetDvWeights() const {
return _dvWeights;
}
/// \brief Returns the 'uu' derivative stencil interpolation weights
std::vector<REAL> const & GetDuuWeights() const {
return _duuWeights;
}
/// \brief Returns the 'uv' derivative stencil interpolation weights
std::vector<REAL> const & GetDuvWeights() const {
return _duvWeights;
}
/// \brief Returns the 'vv' derivative stencil interpolation weights
std::vector<REAL> const & GetDvvWeights() const {
return _dvvWeights;
}
/// \brief Updates derivative values based on the control values
///
/// \note The destination buffers ('uderivs' & 'vderivs') are assumed to
/// have allocated at least \c GetNumStencils() elements.
///
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param uderivs Destination buffer for the interpolated 'u'
/// derivative primvar data
///
/// @param vderivs Destination buffer for the interpolated 'v'
/// derivative primvar data
///
/// @param start Index of first destination derivative to update
///
/// @param end Index of last destination derivative to update
///
template <class T, class U>
void UpdateDerivs(T const & srcValues, U & uderivs, U & vderivs,
int start=-1, int end=-1) const {
this->update(srcValues, uderivs, _duWeights, start, end);
this->update(srcValues, vderivs, _dvWeights, start, end);
}
template <class T1, class T2, class U>
void UpdateDerivs(T1 const & srcBase, int numBase, T2 const & srcRef,
U & uderivs, U & vderivs, int start=-1, int end=-1) const {
this->update(srcBase, numBase, srcRef, uderivs, _duWeights, start, end);
this->update(srcBase, numBase, srcRef, vderivs, _dvWeights, start, end);
}
// Pointer interface for backward compatibility
template <class T, class U>
void UpdateDerivs(T const *src, U *uderivs, U *vderivs,
int start=-1, int end=-1) const {
this->update(src, uderivs, _duWeights, start, end);
this->update(src, vderivs, _dvWeights, start, end);
}
template <class T1, class T2, class U>
void UpdateDerivs(T1 const *srcBase, int numBase, T2 const *srcRef,
U *uderivs, U *vderivs, int start=-1, int end=-1) const {
this->update(srcBase, numBase, srcRef, uderivs, _duWeights, start, end);
this->update(srcBase, numBase, srcRef, vderivs, _dvWeights, start, end);
}
/// \brief Updates 2nd derivative values based on the control values
///
/// \note The destination buffers ('uuderivs', 'uvderivs', & 'vderivs') are
/// assumed to have allocated at least \c GetNumStencils() elements.
///
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param uuderivs Destination buffer for the interpolated 'uu'
/// derivative primvar data
///
/// @param uvderivs Destination buffer for the interpolated 'uv'
/// derivative primvar data
///
/// @param vvderivs Destination buffer for the interpolated 'vv'
/// derivative primvar data
///
/// @param start Index of first destination derivative to update
///
/// @param end Index of last destination derivative to update
///
template <class T, class U>
void Update2ndDerivs(T const & srcValues,
U & uuderivs, U & uvderivs, U & vvderivs,
int start=-1, int end=-1) const {
this->update(srcValues, uuderivs, _duuWeights, start, end);
this->update(srcValues, uvderivs, _duvWeights, start, end);
this->update(srcValues, vvderivs, _dvvWeights, start, end);
}
template <class T1, class T2, class U>
void Update2ndDerivs(T1 const & srcBase, int numBase, T2 const & srcRef,
U & uuderivs, U & uvderivs, U & vvderivs, int start=-1, int end=-1) const {
this->update(srcBase, numBase, srcRef, uuderivs, _duuWeights, start, end);
this->update(srcBase, numBase, srcRef, uvderivs, _duvWeights, start, end);
this->update(srcBase, numBase, srcRef, vvderivs, _dvvWeights, start, end);
}
// Pointer interface for backward compatibility
template <class T, class U>
void Update2ndDerivs(T const *src, T *uuderivs, U *uvderivs, U *vvderivs,
int start=-1, int end=-1) const {
this->update(src, uuderivs, _duuWeights, start, end);
this->update(src, uvderivs, _duvWeights, start, end);
this->update(src, vvderivs, _dvvWeights, start, end);
}
template <class T1, class T2, class U>
void Update2ndDerivs(T1 const *srcBase, int numBase, T2 const *srcRef,
U *uuderivs, U *uvderivs, U *vvderivs, int start=-1, int end=-1) const {
this->update(srcBase, numBase, srcRef, uuderivs, _duuWeights, start, end);
this->update(srcBase, numBase, srcRef, uvderivs, _duvWeights, start, end);
this->update(srcBase, numBase, srcRef, vvderivs, _dvvWeights, start, end);
}
/// \brief Clears the stencils from the table
void Clear();
private:
friend class LimitStencilTableFactoryReal<REAL>;
// Resize the table arrays (factory helper)
void resize(int nstencils, int nelems);
private:
std::vector<REAL> _duWeights, // u derivative limit stencil weights
_dvWeights, // v derivative limit stencil weights
_duuWeights, // uu derivative limit stencil weights
_duvWeights, // uv derivative limit stencil weights
_dvvWeights; // vv derivative limit stencil weights
};
/// \brief Limit stencil table class wrapping the template for compatibility.
///
class LimitStencilTable : public LimitStencilTableReal<float> {
protected:
typedef LimitStencilTableReal<float> BaseTable;
public:
LimitStencil GetLimitStencil(Index index) const {
return LimitStencil(BaseTable::GetLimitStencil(index));
}
LimitStencil operator[] (Index index) const {
return LimitStencil(BaseTable::GetLimitStencil(index));
}
protected:
LimitStencilTable(int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<float> const& weights,
std::vector<float> const& duWeights,
std::vector<float> const& dvWeights,
std::vector<float> const& duuWeights,
std::vector<float> const& duvWeights,
std::vector<float> const& dvvWeights,
bool includeCoarseVerts,
size_t firstOffset)
: BaseTable(numControlVerts,
offsets, sizes, sources, weights,
duWeights, dvWeights, duuWeights, duvWeights, dvvWeights,
includeCoarseVerts, firstOffset) { }
};
// Update values by applying cached stencil weights to new control values
template <typename REAL>
template <class T1, class T2, class U> void
StencilTableReal<REAL>::update(T1 const &srcBase, int numBase,
T2 const &srcRef, U &dstValues,
std::vector<REAL> const &valueWeights, Index start, Index end) const {
int const * sizes = &_sizes.at(0);
Index const * indices = &_indices.at(0);
REAL const * weights = &valueWeights.at(0);
if (start > 0) {
assert(start < (Index)_offsets.size());
sizes += start;
indices += _offsets[start];
weights += _offsets[start];
} else {
start = 0;
}
int nstencils = ((end < start) ? GetNumStencils() : end) - start;
for (int i = 0; i < nstencils; ++i, ++sizes) {
dstValues[start + i].Clear();
for (int j = 0; j < *sizes; ++j, ++indices, ++weights) {
if (*indices < numBase) {
dstValues[start + i].AddWithWeight(srcBase[*indices], *weights);
} else {
dstValues[start + i].AddWithWeight(srcRef[*indices - numBase], *weights);
}
}
}
}
template <typename REAL>
template <class T, class U> void
StencilTableReal<REAL>::update(T const &srcValues, U &dstValues,
std::vector<REAL> const &valueWeights, Index start, Index end) const {
int const * sizes = &_sizes.at(0);
Index const * indices = &_indices.at(0);
REAL const * weights = &valueWeights.at(0);
if (start > 0) {
assert(start < (Index)_offsets.size());
sizes += start;
indices += _offsets[start];
weights += _offsets[start];
} else {
start = 0;
}
int nstencils = ((end < start) ? GetNumStencils() : end) - start;
for (int i = 0; i < nstencils; ++i, ++sizes) {
dstValues[start + i].Clear();
for (int j = 0; j < *sizes; ++j, ++indices, ++weights) {
dstValues[start + i].AddWithWeight(srcValues[*indices], *weights);
}
}
}
template <typename REAL>
inline void
StencilTableReal<REAL>::generateOffsets() {
Index offset=0;
int noffsets = (int)_sizes.size();
_offsets.resize(noffsets);
for (int i=0; i<(int)_sizes.size(); ++i ) {
_offsets[i]=offset;
offset+=_sizes[i];
}
}
template <typename REAL>
inline void
StencilTableReal<REAL>::resize(int nstencils, int nelems) {
_sizes.resize(nstencils);
_indices.resize(nelems);
_weights.resize(nelems);
}
template <typename REAL>
inline void
StencilTableReal<REAL>::reserve(int nstencils, int nelems) {
_sizes.reserve(nstencils);
_indices.reserve(nelems);
_weights.reserve(nelems);
}
template <typename REAL>
inline void
StencilTableReal<REAL>::shrinkToFit() {
std::vector<int>(_sizes).swap(_sizes);
std::vector<Index>(_indices).swap(_indices);
std::vector<REAL>(_weights).swap(_weights);
}
template <typename REAL>
inline void
StencilTableReal<REAL>::finalize() {
shrinkToFit();
generateOffsets();
}
// Returns a Stencil at index i in the table
template <typename REAL>
inline StencilReal<REAL>
StencilTableReal<REAL>::GetStencil(Index i) const {
assert((! _offsets.empty()) && i<(int)_offsets.size());
Index ofs = _offsets[i];
return StencilReal<REAL>(const_cast<int*>(&_sizes[i]),
const_cast<Index*>(&_indices[ofs]),
const_cast<REAL*>(&_weights[ofs]));
}
template <typename REAL>
inline StencilReal<REAL>
StencilTableReal<REAL>::operator[] (Index index) const {
return GetStencil(index);
}
template <typename REAL>
inline void
LimitStencilTableReal<REAL>::resize(int nstencils, int nelems) {
StencilTableReal<REAL>::resize(nstencils, nelems);
_duWeights.resize(nelems);
_dvWeights.resize(nelems);
}
// Returns a LimitStencil at index i in the table
template <typename REAL>
inline LimitStencilReal<REAL>
LimitStencilTableReal<REAL>::GetLimitStencil(Index i) const {
assert((! this->GetOffsets().empty()) && i<(int)this->GetOffsets().size());
Index ofs = this->GetOffsets()[i];
if (!_duWeights.empty() && !_dvWeights.empty() &&
!_duuWeights.empty() && !_duvWeights.empty() && !_dvvWeights.empty()) {
return LimitStencilReal<REAL>(
const_cast<int *>(&this->GetSizes()[i]),
const_cast<Index *>(&this->GetControlIndices()[ofs]),
const_cast<REAL *>(&this->GetWeights()[ofs]),
const_cast<REAL *>(&GetDuWeights()[ofs]),
const_cast<REAL *>(&GetDvWeights()[ofs]),
const_cast<REAL *>(&GetDuuWeights()[ofs]),
const_cast<REAL *>(&GetDuvWeights()[ofs]),
const_cast<REAL *>(&GetDvvWeights()[ofs]) );
} else if (!_duWeights.empty() && !_dvWeights.empty()) {
return LimitStencilReal<REAL>(
const_cast<int *>(&this->GetSizes()[i]),
const_cast<Index *>(&this->GetControlIndices()[ofs]),
const_cast<REAL *>(&this->GetWeights()[ofs]),
const_cast<REAL *>(&GetDuWeights()[ofs]),
const_cast<REAL *>(&GetDvWeights()[ofs]) );
} else {
return LimitStencilReal<REAL>(
const_cast<int *>(&this->GetSizes()[i]),
const_cast<Index *>(&this->GetControlIndices()[ofs]),
const_cast<REAL *>(&this->GetWeights()[ofs]) );
}
}
template <typename REAL>
inline LimitStencilReal<REAL>
LimitStencilTableReal<REAL>::operator[] (Index index) const {
return GetLimitStencil(index);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_FAR_STENCILTABLE_H

View File

@@ -0,0 +1,678 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/stencilTableFactory.h"
#include "../far/stencilBuilder.h"
#include "../far/patchTable.h"
#include "../far/patchTableFactory.h"
#include "../far/patchMap.h"
#include "../far/topologyRefiner.h"
#include "../far/primvarRefiner.h"
#include <cassert>
#include <algorithm>
#include <iostream>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
using internal::StencilBuilder;
namespace {
#ifdef __INTEL_COMPILER
#pragma warning (push)
#pragma warning disable 1572
#endif
template <typename REAL>
inline bool isWeightZero(REAL w) { return (w == (REAL) 0.0); }
#ifdef __INTEL_COMPILER
#pragma warning (pop)
#endif
}
//------------------------------------------------------------------------------
template <typename REAL>
void
StencilTableFactoryReal<REAL>::generateControlVertStencils(
int numControlVerts,
StencilReal<REAL> & dst) {
// Control vertices contribute a single index with a weight of 1.0
for (int i=0; i<numControlVerts; ++i) {
*dst._size = 1;
*dst._indices = i;
*dst._weights = (REAL) 1.0;
dst.Next();
}
}
//
// StencilTable factory
//
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::Create(TopologyRefiner const & refiner,
Options options) {
bool interpolateVertex = options.interpolationMode==INTERPOLATE_VERTEX;
bool interpolateVarying = options.interpolationMode==INTERPOLATE_VARYING;
bool interpolateFaceVarying = options.interpolationMode==INTERPOLATE_FACE_VARYING;
int numControlVertices = !interpolateFaceVarying
? refiner.GetLevel(0).GetNumVertices()
: refiner.GetLevel(0).GetNumFVarValues(options.fvarChannel);
int maxlevel = std::min(int(options.maxLevel), refiner.GetMaxLevel());
if (maxlevel==0 && (! options.generateControlVerts)) {
StencilTableReal<REAL> * result = new StencilTableReal<REAL>;
result->_numControlVertices = numControlVertices;
return result;
}
StencilBuilder<REAL> builder(numControlVertices,
/*genControlVerts*/ true,
/*compactWeights*/ true);
//
// Interpolate stencils for each refinement level using
// PrimvarRefiner::InterpolateLevel<>() for vertex or varying
//
PrimvarRefiner primvarRefiner(refiner);
typename StencilBuilder<REAL>::Index srcIndex(&builder, 0);
typename StencilBuilder<REAL>::Index dstIndex(&builder, numControlVertices);
for (int level=1; level<=maxlevel; ++level) {
if (interpolateVertex) {
primvarRefiner.Interpolate(level, srcIndex, dstIndex);
} else if (interpolateVarying) {
primvarRefiner.InterpolateVarying(level, srcIndex, dstIndex);
} else {
primvarRefiner.InterpolateFaceVarying(level, srcIndex, dstIndex, options.fvarChannel);
}
if (options.factorizeIntermediateLevels) {
srcIndex = dstIndex;
}
int dstVertex = !interpolateFaceVarying
? refiner.GetLevel(level).GetNumVertices()
: refiner.GetLevel(level).GetNumFVarValues(options.fvarChannel);
dstIndex = dstIndex[dstVertex];
if (! options.factorizeIntermediateLevels) {
// All previous verts are considered as coarse verts, as a
// result, we don't update the srcIndex and update the coarse
// vertex count.
builder.SetCoarseVertCount(dstIndex.GetOffset());
}
}
size_t firstOffset = numControlVertices;
if (! options.generateIntermediateLevels)
firstOffset = srcIndex.GetOffset();
// Copy stencils from the StencilBuilder into the StencilTable.
// Always initialize numControlVertices (useful for torus case)
StencilTableReal<REAL> * result =
new StencilTableReal<REAL>(numControlVertices,
builder.GetStencilOffsets(),
builder.GetStencilSizes(),
builder.GetStencilSources(),
builder.GetStencilWeights(),
options.generateControlVerts,
firstOffset);
return result;
}
//------------------------------------------------------------------------------
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::Create(int numTables,
StencilTableReal<REAL> const ** tables) {
// XXXtakahito:
// This function returns NULL for empty inputs or erroneous condition.
// It's convenient for skipping varying stencils etc, however,
// other Create() API returns an empty stencil instead of NULL.
// They need to be consistent.
if ( (numTables<=0) || (! tables)) {
return NULL;
}
int ncvs = -1,
nstencils = 0,
nelems = 0;
for (int i=0; i<numTables; ++i) {
StencilTableReal<REAL> const * st = tables[i];
// allow the tables could have a null entry.
if (!st) continue;
if (ncvs >= 0 && st->GetNumControlVertices() != ncvs) {
return NULL;
}
ncvs = st->GetNumControlVertices();
nstencils += st->GetNumStencils();
nelems += (int)st->GetControlIndices().size();
}
if (ncvs == -1) {
return NULL;
}
StencilTableReal<REAL> * result = new StencilTableReal<REAL>;
result->resize(nstencils, nelems);
int * sizes = &result->_sizes[0];
Index * indices = &result->_indices[0];
REAL * weights = &result->_weights[0];
for (int i=0; i<numTables; ++i) {
StencilTableReal<REAL> const * st = tables[i];
if (!st) continue;
int st_nstencils = st->GetNumStencils(),
st_nelems = (int)st->_indices.size();
memcpy(sizes, &st->_sizes[0], st_nstencils*sizeof(int));
memcpy(indices, &st->_indices[0], st_nelems*sizeof(Index));
memcpy(weights, &st->_weights[0], st_nelems*sizeof(REAL));
sizes += st_nstencils;
indices += st_nelems;
weights += st_nelems;
}
result->_numControlVertices = ncvs;
// have to re-generate offsets from scratch
result->generateOffsets();
return result;
}
//------------------------------------------------------------------------------
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::AppendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const * baseStencilTable,
StencilTableReal<REAL> const * localPointStencilTable,
bool factorize) {
return appendLocalPointStencilTable(
refiner,
baseStencilTable,
localPointStencilTable,
/*channel*/-1,
factorize);
}
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::AppendLocalPointStencilTableFaceVarying(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const * baseStencilTable,
StencilTableReal<REAL> const * localPointStencilTable,
int channel,
bool factorize) {
return appendLocalPointStencilTable(
refiner,
baseStencilTable,
localPointStencilTable,
channel,
factorize);
}
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::appendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const * baseStencilTable,
StencilTableReal<REAL> const * localPointStencilTable,
int channel,
bool factorize) {
// require the local point stencils exist and be non-empty
if ((localPointStencilTable == NULL) ||
(localPointStencilTable->GetNumStencils() == 0)) {
return NULL;
}
int nControlVerts = channel < 0
? refiner.GetLevel(0).GetNumVertices()
: refiner.GetLevel(0).GetNumFVarValues(channel);
// if no base stencils or empty, return copy of local point stencils
if ((baseStencilTable == NULL) ||
(baseStencilTable->GetNumStencils() == 0)) {
StencilTableReal<REAL> * result =
new StencilTableReal<REAL>(*localPointStencilTable);
result->_numControlVertices = nControlVerts;
return result;
}
// baseStencilTable can be built with or without singular stencils
// (single weight of 1.0f) as place-holders for coarse mesh vertices.
int controlVertsIndexOffset = 0;
int nBaseStencils = baseStencilTable->GetNumStencils();
int nBaseStencilsElements = (int)baseStencilTable->_indices.size();
{
int nverts = channel < 0
? refiner.GetNumVerticesTotal()
: refiner.GetNumFVarValuesTotal(channel);
if (nBaseStencils == nverts) {
// the table contains stencils for the control vertices
//
// <----------------- nverts ------------------>
//
// +---------------+----------------------------+-----------------+
// | control verts | refined verts : (max lv) | local points |
// +---------------+----------------------------+-----------------+
// | base stencil table | LP stencils |
// +--------------------------------------------+-----------------+
// ^ /
// \_________________________/
//
//
controlVertsIndexOffset = 0;
} else if (nBaseStencils == (nverts - nControlVerts)) {
// the table does not contain stencils for the control vertices
//
// <----------------- nverts ------------------>
// <------ nBaseStencils ------->
// +---------------+----------------------------+-----------------+
// | control verts | refined verts : (max lv) | local points |
// +---------------+----------------------------+-----------------+
// | base stencil table | LP stencils |
// +----------------------------+-----------------+
// ^ /
// \_________________/
// <-------------->
// controlVertsIndexOffset
//
controlVertsIndexOffset = nControlVerts;
} else {
// these are not the stencils you are looking for.
assert(0);
return NULL;
}
}
// copy all local point stencils to proto stencils, and factorize if needed.
int nLocalPointStencils = localPointStencilTable->GetNumStencils();
int nLocalPointStencilsElements = 0;
StencilBuilder<REAL> builder(nControlVerts,
/*genControlVerts*/ false,
/*compactWeights*/ factorize);
typename StencilBuilder<REAL>::Index origin(&builder, 0);
typename StencilBuilder<REAL>::Index dst = origin;
typename StencilBuilder<REAL>::Index srcIdx = origin;
for (int i = 0 ; i < nLocalPointStencils; ++i) {
StencilReal<REAL> src = localPointStencilTable->GetStencil(i);
dst = origin[i];
for (int j = 0; j < src.GetSize(); ++j) {
Index index = src.GetVertexIndices()[j];
REAL weight = src.GetWeights()[j];
if (isWeightZero<REAL>(weight)) continue;
if (factorize) {
dst.AddWithWeight(
// subtracting controlVertsIndex if the baseStencil doesn't
// include control vertices (see above diagram)
// since currently local point stencils are created with
// absolute indices including control (level=0) vertices.
baseStencilTable->GetStencil(index - controlVertsIndexOffset),
weight);
} else {
srcIdx = origin[index + controlVertsIndexOffset];
dst.AddWithWeight(srcIdx, weight);
}
}
nLocalPointStencilsElements += builder.GetNumVertsInStencil(i);
}
// create new stencil table
StencilTableReal<REAL> * result = new StencilTableReal<REAL>;
result->_numControlVertices = nControlVerts;
result->resize(nBaseStencils + nLocalPointStencils,
nBaseStencilsElements + nLocalPointStencilsElements);
int* sizes = &result->_sizes[0];
Index * indices = &result->_indices[0];
REAL * weights = &result->_weights[0];
// put base stencils first
memcpy(sizes, &baseStencilTable->_sizes[0],
nBaseStencils*sizeof(int));
memcpy(indices, &baseStencilTable->_indices[0],
nBaseStencilsElements*sizeof(Index));
memcpy(weights, &baseStencilTable->_weights[0],
nBaseStencilsElements*sizeof(REAL));
sizes += nBaseStencils;
indices += nBaseStencilsElements;
weights += nBaseStencilsElements;
// endcap stencils second
for (int i = 0 ; i < nLocalPointStencils; ++i) {
int size = builder.GetNumVertsInStencil(i);
int idx = builder.GetStencilOffsets()[i];
for (int j = 0; j < size; ++j) {
*indices++ = builder.GetStencilSources()[idx+j];
*weights++ = builder.GetStencilWeights()[idx+j];
}
*sizes++ = size;
}
// have to re-generate offsets from scratch
result->generateOffsets();
return result;
}
//------------------------------------------------------------------------------
template <typename REAL>
LimitStencilTableReal<REAL> const *
LimitStencilTableFactoryReal<REAL>::Create(TopologyRefiner const & refiner,
LocationArrayVec const & locationArrays,
StencilTableReal<REAL> const * cvStencilsIn,
PatchTable const * patchTableIn,
Options options) {
// Compute the total number of stencils to generate
int numStencils=0, numLimitStencils=0;
for (int i=0; i<(int)locationArrays.size(); ++i) {
assert(locationArrays[i].numLocations>=0);
numStencils += locationArrays[i].numLocations;
}
if (numStencils<=0) {
return 0;
}
bool uniform = refiner.IsUniform();
int maxlevel = refiner.GetMaxLevel();
bool interpolateVertex = (options.interpolationMode == INTERPOLATE_VERTEX);
bool interpolateVarying = (options.interpolationMode == INTERPOLATE_VARYING);
bool interpolateFaceVarying = (options.interpolationMode == INTERPOLATE_FACE_VARYING);
int fvarChannel = options.fvarChannel;
//
// Quick sanity checks for given PatchTable and/or StencilTables:
//
int nRefinedStencils = 0;
if (uniform) {
// Uniform stencils must include at least the last level points:
nRefinedStencils = interpolateFaceVarying
? refiner.GetLevel(maxlevel).GetNumFVarValues(fvarChannel)
: refiner.GetLevel(maxlevel).GetNumVertices();
} else {
// Adaptive stencils must include at least all refined points:
nRefinedStencils = interpolateFaceVarying
? refiner.GetNumFVarValuesTotal(fvarChannel)
: refiner.GetNumVerticesTotal();
}
if (cvStencilsIn && (cvStencilsIn->GetNumStencils() < nRefinedStencils)) {
// Too few stencils in given StencilTable
return 0;
}
if (patchTableIn && (patchTableIn->IsFeatureAdaptive() == uniform)) {
// Adaptive/uniform mismatch with given PatchTable and refiner
return 0;
}
// If an appropriate StencilTable was given, use it, otherwise, create a new one
StencilTableReal<REAL> const * cvstencils = cvStencilsIn;
if (! cvstencils) {
//
// Generate stencils for the control vertices - this is necessary to
// properly factorize patches with control vertices at level 0 (natural
// regular patches, such as in a torus)
// note: the control vertices of the mesh are added as single-index
// stencils of weight 1.0f
//
typename StencilTableFactoryReal<REAL>::Options stencilTableOptions;
stencilTableOptions.generateIntermediateLevels = uniform ? false :true;
stencilTableOptions.generateControlVerts = true;
stencilTableOptions.generateOffsets = true;
stencilTableOptions.interpolationMode = options.interpolationMode;
stencilTableOptions.fvarChannel = options.fvarChannel;
cvstencils = StencilTableFactoryReal<REAL>::Create(refiner, stencilTableOptions);
}
// If an appropriate PatchTable was given, use it, otherwise, create a new one
PatchTable const * patchtable = patchTableIn;
if (! patchtable) {
//
// Ideally we could create a sparse PatchTable here for the given
// Locations, but that requires inverting the ptex/base-face relation.
// so the caller must deal with that and provide such a PatchTable
//
PatchTableFactory::Options patchTableOptions;
patchTableOptions.SetPatchPrecision<REAL>();
patchTableOptions.includeBaseLevelIndices = true;
patchTableOptions.generateVaryingTables = interpolateVarying;
patchTableOptions.generateFVarTables = interpolateFaceVarying;
if (interpolateFaceVarying) {
patchTableOptions.includeFVarBaseLevelIndices = true;
patchTableOptions.numFVarChannels = 1;
patchTableOptions.fvarChannelIndices = &fvarChannel;
patchTableOptions.generateFVarLegacyLinearPatches = uniform ||
!refiner.GetAdaptiveOptions().considerFVarChannels;
}
patchTableOptions.SetEndCapType(
Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS);
patchTableOptions.useInfSharpPatch = !uniform &&
refiner.GetAdaptiveOptions().useInfSharpPatch;
patchtable = PatchTableFactory::Create(refiner, patchTableOptions);
}
// Append local point stencils and further verfiy size of given StencilTable:
StencilTableReal<REAL> const * localstencils = 0;
if (interpolateVertex) {
localstencils = patchtable->GetLocalPointStencilTable<REAL>();
} else if (interpolateFaceVarying) {
localstencils = patchtable->GetLocalPointFaceVaryingStencilTable<REAL>(fvarChannel);
} else {
localstencils = patchtable->GetLocalPointVaryingStencilTable<REAL>();
}
if (localstencils && (cvstencils->GetNumStencils() == nRefinedStencils)) {
StencilTableReal<REAL> const *refinedstencils = cvstencils;
if (interpolateFaceVarying) {
cvstencils = StencilTableFactoryReal<REAL>::AppendLocalPointStencilTableFaceVarying(
refiner, refinedstencils, localstencils, fvarChannel);
} else {
cvstencils = StencilTableFactoryReal<REAL>::AppendLocalPointStencilTable(
refiner, refinedstencils, localstencils);
}
if (!cvStencilsIn) delete refinedstencils;
}
assert(patchtable && cvstencils);
// Create a patch-map to locate sub-patches faster
PatchMap patchmap( *patchtable );
//
// Generate limit stencils for locations
//
int nControlVertices = interpolateFaceVarying
? refiner.GetLevel(0).GetNumFVarValues(fvarChannel)
: refiner.GetLevel(0).GetNumVertices();
StencilBuilder<REAL> builder(nControlVertices,
/*genControlVerts*/ false,
/*compactWeights*/ true);
typename StencilBuilder<REAL>::Index origin(&builder, 0);
typename StencilBuilder<REAL>::Index dst = origin;
//
// Generally use the patches corresponding to the interpolation mode, but Uniform
// PatchTables do not have varying patches -- use the equivalent linear vertex
// patches in this case:
//
bool useVertexPatches = interpolateVertex || (interpolateVarying && uniform);
bool useFVarPatches = interpolateFaceVarying;
REAL wP[20], wDs[20], wDt[20], wDss[20], wDst[20], wDtt[20];
for (size_t i=0; i<locationArrays.size(); ++i) {
LocationArray const & array = locationArrays[i];
assert(array.ptexIdx>=0);
for (int j=0; j<array.numLocations; ++j) { // for each face we're working on
REAL s = array.s[j],
t = array.t[j]; // for each target (s,t) point on that face
PatchMap::Handle const * handle =
patchmap.FindPatch(array.ptexIdx, s, t);
if (handle) {
ConstIndexArray cvs;
if (useVertexPatches) {
cvs = patchtable->GetPatchVertices(*handle);
} else if (useFVarPatches) {
cvs = patchtable->GetPatchFVarValues(*handle, fvarChannel);
} else {
cvs = patchtable->GetPatchVaryingVertices(*handle);
}
StencilTableReal<REAL> const & src = *cvstencils;
dst = origin[numLimitStencils];
if (options.generate2ndDerivatives) {
if (useVertexPatches) {
patchtable->EvaluateBasis<REAL>(
*handle, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
} else if (useFVarPatches) {
patchtable->EvaluateBasisFaceVarying<REAL>(
*handle, s, t, wP, wDs, wDt, wDss, wDst, wDtt, fvarChannel);
} else {
patchtable->EvaluateBasisVarying<REAL>(
*handle, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
}
dst.Clear();
for (int k = 0; k < cvs.size(); ++k) {
dst.AddWithWeight(src[cvs[k]], wP[k], wDs[k], wDt[k], wDss[k], wDst[k], wDtt[k]);
}
} else if (options.generate1stDerivatives) {
if (useVertexPatches) {
patchtable->EvaluateBasis<REAL>(
*handle, s, t, wP, wDs, wDt);
} else if (useFVarPatches) {
patchtable->EvaluateBasisFaceVarying<REAL>(
*handle, s, t, wP, wDs, wDt, 0, 0, 0, fvarChannel);
} else {
patchtable->EvaluateBasisVarying<REAL>(
*handle, s, t, wP, wDs, wDt);
}
dst.Clear();
for (int k = 0; k < cvs.size(); ++k) {
dst.AddWithWeight(src[cvs[k]], wP[k], wDs[k], wDt[k]);
}
} else {
if (useVertexPatches) {
patchtable->EvaluateBasis<REAL>(
*handle, s, t, wP);
} else if (useFVarPatches) {
patchtable->EvaluateBasisFaceVarying<REAL>(
*handle, s, t, wP, 0, 0, 0, 0, 0, fvarChannel);
} else {
patchtable->EvaluateBasisVarying<REAL>(
*handle, s, t, wP);
}
dst.Clear();
for (int k = 0; k < cvs.size(); ++k) {
dst.AddWithWeight(src[cvs[k]], wP[k]);
}
}
++numLimitStencils;
}
}
}
if (! cvStencilsIn) {
delete cvstencils;
}
if (! patchTableIn) {
delete patchtable;
}
//
// Copy the proto-stencils into the limit stencil table
//
LimitStencilTableReal<REAL> * result = new LimitStencilTableReal<REAL>(
nControlVertices,
builder.GetStencilOffsets(),
builder.GetStencilSizes(),
builder.GetStencilSources(),
builder.GetStencilWeights(),
builder.GetStencilDuWeights(),
builder.GetStencilDvWeights(),
builder.GetStencilDuuWeights(),
builder.GetStencilDuvWeights(),
builder.GetStencilDvvWeights(),
/*ctrlVerts*/false,
/*fristOffset*/0);
return result;
}
//
// Explicit instantiation for float and double:
//
template class StencilTableFactoryReal<float>;
template class StencilTableFactoryReal<double>;
template class LimitStencilTableFactoryReal<float>;
template class LimitStencilTableFactoryReal<double>;
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,384 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_STENCILTABLE_FACTORY_H
#define OPENSUBDIV3_FAR_STENCILTABLE_FACTORY_H
#include "../version.h"
#include "../far/patchTable.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
class TopologyRefiner;
template <typename REAL> class StencilReal;
template <typename REAL> class StencilTableReal;
template <typename REAL> class LimitStencilReal;
template <typename REAL> class LimitStencilTableReal;
/// \brief A specialized factory for StencilTable
///
template <typename REAL>
class StencilTableFactoryReal {
public:
enum Mode {
INTERPOLATE_VERTEX=0, ///< vertex primvar stencils
INTERPOLATE_VARYING, ///< varying primvar stencils
INTERPOLATE_FACE_VARYING ///< face-varying primvar stencils
};
struct Options {
Options() : interpolationMode(INTERPOLATE_VERTEX),
generateOffsets(false),
generateControlVerts(false),
generateIntermediateLevels(true),
factorizeIntermediateLevels(true),
maxLevel(10),
fvarChannel(0) { }
unsigned int interpolationMode : 2, ///< interpolation mode
generateOffsets : 1, ///< populate optional "_offsets" field
generateControlVerts : 1, ///< generate stencils for control-vertices
generateIntermediateLevels : 1, ///< vertices at all levels or highest only
factorizeIntermediateLevels : 1, ///< accumulate stencil weights from control
/// vertices or from the stencils of the
/// previous level
maxLevel : 4; ///< generate stencils up to 'maxLevel'
unsigned int fvarChannel; ///< face-varying channel to use
/// when generating face-varying stencils
};
/// \brief Instantiates StencilTable from TopologyRefiner that have been
/// refined uniformly or adaptively.
///
/// \note The factory only creates stencils for vertices that have already
/// been refined in the TopologyRefiner. Use RefineUniform() or
/// RefineAdaptive() before constructing the stencils.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param options Options controlling the creation of the table
///
static StencilTableReal<REAL> const * Create(
TopologyRefiner const & refiner, Options options = Options());
/// \brief Instantiates StencilTable by concatenating an array of existing
/// stencil tables.
///
/// \note This factory checks that the stencil tables point to the same set
/// of supporting control vertices - no re-indexing is done.
/// GetNumControlVertices() *must* return the same value for all input
/// tables.
///
/// @param numTables Number of input StencilTables
///
/// @param tables Array of input StencilTables
///
static StencilTableReal<REAL> const * Create(
int numTables, StencilTableReal<REAL> const ** tables);
/// \brief Utility function for stencil splicing for local point stencils.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param baseStencilTable Input StencilTable for refined vertices
///
/// @param localPointStencilTable
/// StencilTable for the change of basis patch points.
///
/// @param factorize If factorize is set to true, endcap stencils will be
/// factorized with supporting vertices from baseStencil
/// table so that the endcap points can be computed
/// directly from control vertices.
///
static StencilTableReal<REAL> const * AppendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const *baseStencilTable,
StencilTableReal<REAL> const *localPointStencilTable,
bool factorize = true);
/// \brief Utility function for stencil splicing for local point varying stencils.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param baseStencilTable Input StencilTable for refined vertices
///
/// @param localPointStencilTable
/// StencilTable for the change of basis patch points.
///
/// @param factorize If factorize is set to true, endcap stencils will be
/// factorized with supporting vertices from baseStencil
/// table so that the endcap points can be computed
/// directly from control vertices.
///
static StencilTableReal<REAL> const * AppendLocalPointStencilTableVarying(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const *baseStencilTable,
StencilTableReal<REAL> const *localPointStencilTable,
bool factorize = true) {
return AppendLocalPointStencilTable(
refiner, baseStencilTable, localPointStencilTable, factorize);
}
/// \brief Utility function for stencil splicing for local point
/// face-varying stencils.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param baseStencilTable Input StencilTable for refined vertices
///
/// @param localPointStencilTable
/// StencilTable for the change of basis patch points.
///
/// @param channel face-varying channel
///
/// @param factorize If factorize is set to true, endcap stencils will be
/// factorized with supporting vertices from baseStencil
/// table so that the endcap points can be computed
/// directly from control vertices.
///
static StencilTableReal<REAL> const * AppendLocalPointStencilTableFaceVarying(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const *baseStencilTable,
StencilTableReal<REAL> const *localPointStencilTable,
int channel = 0,
bool factorize = true);
private:
// Generate stencils for the coarse control-vertices (single weight = 1.0f)
static void generateControlVertStencils(
int numControlVerts,
StencilReal<REAL> & dst);
// Internal method to splice local point stencils
static StencilTableReal<REAL> const * appendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const * baseStencilTable,
StencilTableReal<REAL> const * localPointStencilTable,
int channel,
bool factorize);
};
/// \brief A specialized factory for LimitStencilTable
///
/// The LimitStencilTableFactory creates a table of limit stencils. Limit
/// stencils can interpolate any arbitrary location on the limit surface.
/// The stencils will be bilinear if the surface is refined uniformly, and
/// bicubic if feature adaptive isolation is used instead.
///
/// Surface locations are expressed as a combination of ptex face index and
/// normalized (s,t) patch coordinates. The factory exposes the LocationArray
/// struct as a container for these location descriptors.
///
template <typename REAL>
class LimitStencilTableFactoryReal {
public:
enum Mode {
INTERPOLATE_VERTEX=0, ///< vertex primvar stencils
INTERPOLATE_VARYING, ///< varying primvar stencils
INTERPOLATE_FACE_VARYING ///< face-varying primvar stencils
};
struct Options {
Options() : interpolationMode(INTERPOLATE_VERTEX),
generate1stDerivatives(true),
generate2ndDerivatives(false),
fvarChannel(0) { }
unsigned int interpolationMode : 2, ///< interpolation mode
generate1stDerivatives : 1, ///< Generate weights for 1st derivatives
generate2ndDerivatives : 1; ///< Generate weights for 2nd derivatives
unsigned int fvarChannel; ///< face-varying channel to use
};
/// \brief Descriptor for limit surface locations
struct LocationArray {
LocationArray() : ptexIdx(-1), numLocations(0), s(0), t(0) { }
int ptexIdx, ///< ptex face index
numLocations; ///< number of (u,v) coordinates in the array
REAL const * s, ///< array of u coordinates
* t; ///< array of v coordinates
};
typedef std::vector<LocationArray> LocationArrayVec;
/// \brief Instantiates LimitStencilTable from a TopologyRefiner that has
/// been refined either uniformly or adaptively.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param locationArrays An array of surface location descriptors
/// (see LocationArray)
///
/// @param cvStencils A StencilTable generated from the TopologyRefiner
/// (Optional: prevents redundant instantiation of the
/// table if available. The given table must at least
/// contain stencils for all control points and all
/// refined points -- any stencils for local points of
/// a PatchTable must match the PatchTable provided or
/// internally generated)
///
/// @param patchTable A PatchTable generated from the TopologyRefiner
/// (Optional: prevents redundant instantiation of the
/// table if available. The given table must match
/// the optional StencilTable if also provided)
///
/// @param options Options controlling the creation of the table
///
static LimitStencilTableReal<REAL> const * Create(
TopologyRefiner const & refiner,
LocationArrayVec const & locationArrays,
StencilTableReal<REAL> const * cvStencils = 0,
PatchTable const * patchTable = 0,
Options options = Options());
};
//
// Public wrapper classes for the templates
//
class Stencil;
class StencilTable;
/// \brief Stencil table factory class wrapping the template for compatibility.
///
class StencilTableFactory : public StencilTableFactoryReal<float> {
private:
typedef StencilTableFactoryReal<float> BaseFactory;
typedef StencilTableReal<float> BaseTable;
public:
static StencilTable const * Create(
TopologyRefiner const & refiner, Options options = Options()) {
return static_cast<StencilTable const *>(
BaseFactory::Create(refiner, options));
}
static StencilTable const * Create(
int numTables, StencilTable const ** tables) {
return static_cast<StencilTable const *>(
BaseFactory::Create(numTables,
reinterpret_cast<BaseTable const **>(tables)));
}
static StencilTable const * AppendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTable const *baseStencilTable,
StencilTable const *localPointStencilTable,
bool factorize = true) {
return static_cast<StencilTable const *>(
BaseFactory::AppendLocalPointStencilTable(refiner,
static_cast<BaseTable const *>(baseStencilTable),
static_cast<BaseTable const *>(localPointStencilTable),
factorize));
}
static StencilTable const * AppendLocalPointStencilTableVarying(
TopologyRefiner const &refiner,
StencilTable const *baseStencilTable,
StencilTable const *localPointStencilTable,
bool factorize = true) {
return static_cast<StencilTable const *>(
BaseFactory::AppendLocalPointStencilTableVarying(refiner,
static_cast<BaseTable const *>(baseStencilTable),
static_cast<BaseTable const *>(localPointStencilTable),
factorize));
}
static StencilTable const * AppendLocalPointStencilTableFaceVarying(
TopologyRefiner const &refiner,
StencilTable const *baseStencilTable,
StencilTable const *localPointStencilTable,
int channel = 0,
bool factorize = true) {
return static_cast<StencilTable const *>(
BaseFactory::AppendLocalPointStencilTableFaceVarying(refiner,
static_cast<BaseTable const *>(baseStencilTable),
static_cast<BaseTable const *>(localPointStencilTable),
channel, factorize));
}
};
class LimitStencil;
class LimitStencilTable;
/// \brief Stencil table factory class wrapping the template for compatibility.
///
class LimitStencilTableFactory : public LimitStencilTableFactoryReal<float> {
private:
typedef LimitStencilTableFactoryReal<float> BaseFactory;
typedef StencilTableReal<float> BaseTable;
public:
static LimitStencilTable const * Create(
TopologyRefiner const & refiner,
LocationArrayVec const & locationArrays,
StencilTable const * cvStencils = 0,
PatchTable const * patchTable = 0,
Options options = Options()) {
return static_cast<LimitStencilTable const *>(
BaseFactory::Create(
refiner,
locationArrays,
static_cast<BaseTable const *>(cvStencils),
patchTable,
options));
}
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_FAR_STENCILTABLE_FACTORY_H

View File

@@ -0,0 +1,185 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/topologyDescriptor.h"
#include "../far/topologyRefinerFactory.h"
#include "../far/topologyRefiner.h"
// Unfortunately necessary for error codes that should be more accessible...
#include "../vtr/level.h"
#include <cstdio>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Definitions for TopologyDescriptor:
//
TopologyDescriptor::TopologyDescriptor() {
memset(this, 0, sizeof(TopologyDescriptor));
}
//
// Definitions/specializations for its RefinerFactory<TopologyDescriptor>:
//
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::resizeComponentTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc) {
setNumBaseVertices(refiner, desc.numVertices);
setNumBaseFaces(refiner, desc.numFaces);
for (int face=0; face<desc.numFaces; ++face) {
setNumBaseFaceVertices(refiner, face, desc.numVertsPerFace[face]);
}
return true;
}
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc) {
for (int face=0, idx=0; face<desc.numFaces; ++face) {
IndexArray dstFaceVerts = getBaseFaceVertices(refiner, face);
if (desc.isLeftHanded) {
dstFaceVerts[0] = desc.vertIndicesPerFace[idx++];
for (int vert=dstFaceVerts.size()-1; vert > 0; --vert) {
dstFaceVerts[vert] = desc.vertIndicesPerFace[idx++];
}
} else {
for (int vert=0; vert<dstFaceVerts.size(); ++vert) {
dstFaceVerts[vert] = desc.vertIndicesPerFace[idx++];
}
}
}
return true;
}
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTags(
TopologyRefiner & refiner, TopologyDescriptor const & desc) {
if ((desc.numCreases>0) && desc.creaseVertexIndexPairs && desc.creaseWeights) {
int const * vertIndexPairs = desc.creaseVertexIndexPairs;
for (int edge=0; edge<desc.numCreases; ++edge, vertIndexPairs+=2) {
Index idx = findBaseEdge(refiner, vertIndexPairs[0], vertIndexPairs[1]);
if (idx!=INDEX_INVALID) {
setBaseEdgeSharpness(refiner, idx, desc.creaseWeights[edge]);
} else {
char msg[1024];
snprintf(msg, 1024, "Edge %d specified to be sharp does not exist (%d, %d)",
edge, vertIndexPairs[0], vertIndexPairs[1]);
reportInvalidTopology(Vtr::internal::Level::TOPOLOGY_INVALID_CREASE_EDGE, msg, desc);
}
}
}
if ((desc.numCorners>0) && desc.cornerVertexIndices && desc.cornerWeights) {
for (int vert=0; vert<desc.numCorners; ++vert) {
int idx = desc.cornerVertexIndices[vert];
if (idx >= 0 && idx < getNumBaseVertices(refiner)) {
setBaseVertexSharpness(refiner, idx, desc.cornerWeights[vert]);
} else {
char msg[1024];
snprintf(msg, 1024, "Vertex %d specified to be sharp does not exist", idx);
reportInvalidTopology(Vtr::internal::Level::TOPOLOGY_INVALID_CREASE_VERT, msg, desc);
}
}
}
if (desc.numHoles>0) {
for (int i=0; i<desc.numHoles; ++i) {
setBaseFaceHole(refiner, desc.holeIndices[i], true);
}
}
return true;
}
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignFaceVaryingTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc) {
if (desc.numFVarChannels>0) {
for (int channel=0; channel<desc.numFVarChannels; ++channel) {
int numFVarValues = desc.fvarChannels[channel].numValues;
int const* srcFVarValues = desc.fvarChannels[channel].valueIndices;
createBaseFVarChannel(refiner, numFVarValues);
for (int face = 0, srcNext = 0; face < desc.numFaces; ++face) {
IndexArray dstFaceFVarValues = getBaseFaceFVarValues(refiner, face, channel);
if (desc.isLeftHanded) {
dstFaceFVarValues[0] = srcFVarValues[srcNext++];
for (int vert = dstFaceFVarValues.size() - 1; vert > 0; --vert) {
dstFaceFVarValues[vert] = srcFVarValues[srcNext++];
}
} else {
for (int vert = 0; vert < dstFaceFVarValues.size(); ++vert) {
dstFaceFVarValues[vert] = srcFVarValues[srcNext++];
}
}
}
}
}
return true;
}
template <>
void
TopologyRefinerFactory<TopologyDescriptor>::reportInvalidTopology(
TopologyError /* errCode */, char const * msg, TopologyDescriptor const& /* mesh */) {
Warning(msg);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,127 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_TOPOLOGY_DESCRIPTOR_H
#define OPENSUBDIV3_FAR_TOPOLOGY_DESCRIPTOR_H
#include "../version.h"
#include "../far/topologyRefiner.h"
#include "../far/topologyRefinerFactory.h"
#include "../far/error.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
///
/// \brief A simple reference to raw topology data for use with TopologyRefinerFactory
///
/// TopologyDescriptor is a simple struct containing references to raw topology data used
/// to construct a TopologyRefiner. It is not a requirement but a convenience for use
/// with TopologyRefinerFactory when mesh topology is not available in an existing mesh
/// data structure. It should be functionally complete and simple to use, but for more
/// demanding situations, writing a custom Factory is usually warranted.
///
struct TopologyDescriptor {
int numVertices,
numFaces;
int const * numVertsPerFace;
Index const * vertIndicesPerFace;
int numCreases;
Index const * creaseVertexIndexPairs;
float const * creaseWeights;
int numCorners;
Index const * cornerVertexIndices;
float const * cornerWeights;
int numHoles;
Index const * holeIndices;
bool isLeftHanded;
// Face-varying data channel -- value indices correspond to vertex indices,
// i.e. one for every vertex of every face:
//
struct FVarChannel {
int numValues;
Index const * valueIndices;
FVarChannel() : numValues(0), valueIndices(0) { }
};
int numFVarChannels;
FVarChannel const * fvarChannels;
TopologyDescriptor();
};
//
// Forward declarations of required TopologyRefinerFactory<TopologyDescriptor>
// specializations (defined internally):
//
// @cond EXCLUDE_DOXYGEN
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::resizeComponentTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc);
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc);
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTags(
TopologyRefiner & refiner, TopologyDescriptor const & desc);
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignFaceVaryingTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc);
template <>
void
TopologyRefinerFactory<TopologyDescriptor>::reportInvalidTopology(
TopologyError errCode, char const * msg, TopologyDescriptor const & desc);
// @endcond
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TOPOLOGY_DESCRIPTOR_H */

View File

@@ -0,0 +1,273 @@
//
// Copyright 2015 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_TOPOLOGY_LEVEL_H
#define OPENSUBDIV3_FAR_TOPOLOGY_LEVEL_H
#include "../version.h"
#include "../vtr/level.h"
#include "../vtr/refinement.h"
#include "../far/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
///
/// \brief An interface for accessing data in a specific level of a refined topology hierarchy.
///
/// TopologyLevel provides an interface to data in a specific level of a topology hierarchy.
/// Instances of TopologyLevel are created and owned by a TopologyRefiner,
/// which will return const-references to them. Such references are only valid during the
/// lifetime of the TopologyRefiner that created and returned them, and only for a given refinement,
/// i.e. if the TopologyRefiner is re-refined, any references to TopoologyLevels are invalidated.
///
class TopologyLevel {
public:
//@{
/// @name Methods to inspect the overall inventory of components:
///
/// All three main component types are indexed locally within each level. For
/// some topological relationships -- notably face-vertices, which is often
/// the only relationship of interest -- the total number of entries is also
/// made available.
///
/// \brief Return the number of vertices in this level
int GetNumVertices() const { return _level->getNumVertices(); }
/// \brief Return the number of faces in this level
int GetNumFaces() const { return _level->getNumFaces(); }
/// \brief Return the number of edges in this level
int GetNumEdges() const { return _level->getNumEdges(); }
/// \brief Return the total number of face-vertices, i.e. the sum of all vertices for all faces
int GetNumFaceVertices() const { return _level->getNumFaceVerticesTotal(); }
//@}
//@{
/// @name Methods to inspect topological relationships for individual components:
///
/// With three main component types (vertices, faces and edges), for each of the
/// three components the TopologyLevel stores the incident/adjacent components of
/// the other two types. So there are six relationships available for immediate
/// inspection. All are accessed by methods that return an array of fixed size
/// containing the indices of the incident components.
///
/// For some of the relations, i.e. those for which the incident components are
/// of higher order or 'contain' the component itself (e.g. a vertex has incident
/// faces that contain it), an additional 'local index' is available that identifies
/// the component within each of its neighbors. For example, if vertex V is the k'th
/// vertex in some face F, then when F occurs in the set of incident vertices of V,
/// the local index corresponding to F will be k. The ordering of local indices
/// matches the ordering of the incident component to which it corresponds.
//
/// \brief Access the vertices incident a given face
ConstIndexArray GetFaceVertices(Index f) const { return _level->getFaceVertices(f); }
/// \brief Access the edges incident a given face
ConstIndexArray GetFaceEdges(Index f) const { return _level->getFaceEdges(f); }
/// \brief Access the vertices incident a given edge
ConstIndexArray GetEdgeVertices(Index e) const { return _level->getEdgeVertices(e); }
/// \brief Access the faces incident a given edge
ConstIndexArray GetEdgeFaces(Index e) const { return _level->getEdgeFaces(e); }
/// \brief Access the faces incident a given vertex
ConstIndexArray GetVertexFaces(Index v) const { return _level->getVertexFaces(v); }
/// \brief Access the edges incident a given vertex
ConstIndexArray GetVertexEdges(Index v) const { return _level->getVertexEdges(v); }
/// \brief Access the local indices of a vertex with respect to its incident faces
ConstLocalIndexArray GetVertexFaceLocalIndices(Index v) const { return _level->getVertexFaceLocalIndices(v); }
/// \brief Access the local indices of a vertex with respect to its incident edges
ConstLocalIndexArray GetVertexEdgeLocalIndices(Index v) const { return _level->getVertexEdgeLocalIndices(v); }
/// \brief Access the local indices of an edge with respect to its incident faces
ConstLocalIndexArray GetEdgeFaceLocalIndices(Index e) const { return _level->getEdgeFaceLocalIndices(e); }
/// \brief Identify the edge matching the given vertex pair
Index FindEdge(Index v0, Index v1) const { return _level->findEdge(v0, v1); }
//@}
//@{
/// @name Methods to inspect other topological properties of individual components:
///
/// \brief Return if the edge is non-manifold
bool IsEdgeNonManifold(Index e) const { return _level->isEdgeNonManifold(e); }
/// \brief Return if the vertex is non-manifold
bool IsVertexNonManifold(Index v) const { return _level->isVertexNonManifold(v); }
/// \brief Return if the edge is a boundary
bool IsEdgeBoundary(Index e) const { return _level->getEdgeTag(e)._boundary; }
/// \brief Return if the vertex is a boundary
bool IsVertexBoundary(Index v) const { return _level->getVertexTag(v)._boundary; }
//@}
//@{
/// @name Methods to inspect feature tags for individual components:
///
/// While only a subset of components may have been tagged with features such
/// as sharpness, all such features have a default value and so all components
/// can be inspected.
/// \brief Return the sharpness assigned a given edge
float GetEdgeSharpness(Index e) const { return _level->getEdgeSharpness(e); }
/// \brief Return the sharpness assigned a given vertex
float GetVertexSharpness(Index v) const { return _level->getVertexSharpness(v); }
/// \brief Return if a given face has been tagged as a hole
bool IsFaceHole(Index f) const { return _level->isFaceHole(f); }
/// \brief Return the subdivision rule assigned a given vertex specific to this level
Sdc::Crease::Rule GetVertexRule(Index v) const { return _level->getVertexRule(v); }
//@}
//@{
/// @name Methods to inspect face-varying data:
///
/// Face-varying data is organized into topologically independent channels,
/// each with an integer identifier. Access to face-varying data generally
/// requires the specification of a channel, though with a single channel
/// being a common situation the first/only channel will be assumed if
/// unspecified.
///
/// A face-varying channel is composed of a set of values that may be shared
/// by faces meeting at a common vertex. Just as there are sets of vertices
/// that are associated with faces by index (ranging from 0 to
/// num-vertices - 1), face-varying values are also referenced by index
/// (ranging from 0 to num-values -1).
///
/// The face-varying values associated with a face are accessed similarly to
/// the way in which vertices associated with the face are accessed -- an
/// array of fixed size containing the indices for each corner is provided
/// for inspection, iteration, etc.
///
/// When the face-varying topology around a vertex "matches", it has the
/// same limit properties and so results in the same limit surface when
/// collections of adjacent vertices match. Like other references to
/// "topology", this includes consideration of sharpness. So it may be
/// that face-varying values are assigned around a vertex on a boundary in
/// a way that appears to match, but the face-varying interpolation option
/// requires sharpening of that vertex in face-varying space -- the
/// difference in the topology of the resulting limit surfaces leading to
/// the query returning false for the match. The edge case is simpler in
/// that it only considers continuity across the edge, not the entire
/// neighborhood around each end vertex.
/// \brief Return the number of face-varying channels (should be same for all levels)
int GetNumFVarChannels() const { return _level->getNumFVarChannels(); }
/// \brief Return the total number of face-varying values in a particular channel
/// (the upper bound of a face-varying value index)
int GetNumFVarValues(int channel = 0) const { return _level->getNumFVarValues(channel); }
/// \brief Access the face-varying values associated with a particular face
ConstIndexArray GetFaceFVarValues(Index f, int channel = 0) const {
return _level->getFaceFVarValues(f, channel);
}
/// \brief Return if face-varying topology around a vertex matches
bool DoesVertexFVarTopologyMatch(Index v, int channel = 0) const {
return _level->doesVertexFVarTopologyMatch(v, channel);
}
/// \brief Return if face-varying topology across the edge only matches
bool DoesEdgeFVarTopologyMatch(Index e, int channel = 0) const {
return _level->doesEdgeFVarTopologyMatch(e, channel);
}
/// \brief Return if face-varying topology around a face matches
bool DoesFaceFVarTopologyMatch(Index f, int channel = 0) const {
return _level->doesFaceFVarTopologyMatch(f, channel);
}
//@}
//@{
/// @name Methods to identify parent or child components in adjoining levels of refinement:
/// \brief Access the child faces (in the next level) of a given face
ConstIndexArray GetFaceChildFaces(Index f) const { return _refToChild->getFaceChildFaces(f); }
/// \brief Access the child edges (in the next level) of a given face
ConstIndexArray GetFaceChildEdges(Index f) const { return _refToChild->getFaceChildEdges(f); }
/// \brief Access the child edges (in the next level) of a given edge
ConstIndexArray GetEdgeChildEdges(Index e) const { return _refToChild->getEdgeChildEdges(e); }
/// \brief Return the child vertex (in the next level) of a given face
Index GetFaceChildVertex( Index f) const { return _refToChild->getFaceChildVertex(f); }
/// \brief Return the child vertex (in the next level) of a given edge
Index GetEdgeChildVertex( Index e) const { return _refToChild->getEdgeChildVertex(e); }
/// \brief Return the child vertex (in the next level) of a given vertex
Index GetVertexChildVertex(Index v) const { return _refToChild->getVertexChildVertex(v); }
/// \brief Return the parent face (in the previous level) of a given face
Index GetFaceParentFace(Index f) const { return _refToParent->getChildFaceParentFace(f); }
//@}
//@{
/// @name Debugging aides:
bool ValidateTopology() const { return _level->validateTopology(); }
void PrintTopology(bool children = true) const { _level->print((children && _refToChild) ? _refToChild : 0); }
//@}
private:
friend class TopologyRefiner;
Vtr::internal::Level const * _level;
Vtr::internal::Refinement const * _refToParent;
Vtr::internal::Refinement const * _refToChild;
public:
// Not intended for public use, but required by std::vector, etc...
TopologyLevel() { }
~TopologyLevel() { }
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TOPOLOGY_LEVEL_H */

View File

@@ -0,0 +1,826 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/topologyRefiner.h"
#include "../far/error.h"
#include "../vtr/fvarLevel.h"
#include "../vtr/sparseSelector.h"
#include "../vtr/quadRefinement.h"
#include "../vtr/triRefinement.h"
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Relatively trivial construction/destruction -- the base level (level[0]) needs
// to be explicitly initialized after construction and refinement then applied
//
TopologyRefiner::TopologyRefiner(Sdc::SchemeType schemeType, Sdc::Options schemeOptions) :
_subdivType(schemeType),
_subdivOptions(schemeOptions),
_isUniform(true),
_hasHoles(false),
_hasIrregFaces(false),
_regFaceSize(Sdc::SchemeTypeTraits::GetRegularFaceSize(schemeType)),
_maxLevel(0),
_uniformOptions(0),
_adaptiveOptions(0),
_totalVertices(0),
_totalEdges(0),
_totalFaces(0),
_totalFaceVertices(0),
_maxValence(0),
_baseLevelOwned(true) {
// Need to revisit allocation scheme here -- want to use smart-ptrs for these
// but will probably have to settle for explicit new/delete...
_levels.reserve(10);
_levels.push_back(new Vtr::internal::Level);
_farLevels.reserve(10);
assembleFarLevels();
}
//
// The copy constructor is protected and used by the factory to create a new instance
// from only the base level of the given instance -- it does not create a full copy.
// So members reflecting any refinement are default-initialized while those dependent
// on the base level are copied or explicitly initialized after its assignment.
//
TopologyRefiner::TopologyRefiner(TopologyRefiner const & source) :
_subdivType(source._subdivType),
_subdivOptions(source._subdivOptions),
_isUniform(true),
_hasHoles(source._hasHoles),
_hasIrregFaces(source._hasIrregFaces),
_regFaceSize(source._regFaceSize),
_maxLevel(0),
_uniformOptions(0),
_adaptiveOptions(0),
_baseLevelOwned(false) {
_levels.reserve(10);
_levels.push_back(source._levels[0]);
initializeInventory();
_farLevels.reserve(10);
assembleFarLevels();
}
TopologyRefiner::~TopologyRefiner() {
for (int i=0; i<(int)_levels.size(); ++i) {
if ((i > 0) || _baseLevelOwned) delete _levels[i];
}
for (int i=0; i<(int)_refinements.size(); ++i) {
delete _refinements[i];
}
}
void
TopologyRefiner::Unrefine() {
if (_levels.size()) {
for (int i=1; i<(int)_levels.size(); ++i) {
delete _levels[i];
}
_levels.resize(1);
initializeInventory();
}
for (int i=0; i<(int)_refinements.size(); ++i) {
delete _refinements[i];
}
_refinements.clear();
assembleFarLevels();
}
//
// Initializing and updating the component inventory:
//
void
TopologyRefiner::initializeInventory() {
if (_levels.size()) {
assert(_levels.size() == 1);
Vtr::internal::Level const & baseLevel = *_levels[0];
_totalVertices = baseLevel.getNumVertices();
_totalEdges = baseLevel.getNumEdges();
_totalFaces = baseLevel.getNumFaces();
_totalFaceVertices = baseLevel.getNumFaceVerticesTotal();
_maxValence = baseLevel.getMaxValence();
} else {
_totalVertices = 0;
_totalEdges = 0;
_totalFaces = 0;
_totalFaceVertices = 0;
_maxValence = 0;
}
}
void
TopologyRefiner::updateInventory(Vtr::internal::Level const & newLevel) {
_totalVertices += newLevel.getNumVertices();
_totalEdges += newLevel.getNumEdges();
_totalFaces += newLevel.getNumFaces();
_totalFaceVertices += newLevel.getNumFaceVerticesTotal();
_maxValence = std::max(_maxValence, newLevel.getMaxValence());
}
void
TopologyRefiner::appendLevel(Vtr::internal::Level & newLevel) {
_levels.push_back(&newLevel);
updateInventory(newLevel);
}
void
TopologyRefiner::appendRefinement(Vtr::internal::Refinement & newRefinement) {
_refinements.push_back(&newRefinement);
}
void
TopologyRefiner::assembleFarLevels() {
_farLevels.resize(_levels.size());
_farLevels[0]._refToParent = 0;
_farLevels[0]._level = _levels[0];
_farLevels[0]._refToChild = 0;
int nRefinements = (int)_refinements.size();
if (nRefinements) {
_farLevels[0]._refToChild = _refinements[0];
for (int i = 1; i < nRefinements; ++i) {
_farLevels[i]._refToParent = _refinements[i - 1];
_farLevels[i]._level = _levels[i];
_farLevels[i]._refToChild = _refinements[i];;
}
_farLevels[nRefinements]._refToParent = _refinements[nRefinements - 1];
_farLevels[nRefinements]._level = _levels[nRefinements];
_farLevels[nRefinements]._refToChild = 0;
}
}
//
// Accessors to the topology information:
//
int
TopologyRefiner::GetNumFVarValuesTotal(int channel) const {
int sum = 0;
for (int i = 0; i < (int)_levels.size(); ++i) {
sum += _levels[i]->getNumFVarValues(channel);
}
return sum;
}
//
// Main refinement method -- allocating and initializing levels and refinements:
//
void
TopologyRefiner::RefineUniform(UniformOptions options) {
if (_levels[0]->getNumVertices() == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefiner::RefineUniform() -- base level is uninitialized.");
return;
}
if (_refinements.size()) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefiner::RefineUniform() -- previous refinements already applied.");
return;
}
//
// Allocate the stack of levels and the refinements between them:
//
_uniformOptions = options;
_isUniform = true;
_maxLevel = options.refinementLevel;
Sdc::Split splitType = Sdc::SchemeTypeTraits::GetTopologicalSplitType(_subdivType);
//
// Initialize refinement options for Vtr -- adjusting full-topology for the last level:
//
Vtr::internal::Refinement::Options refineOptions;
refineOptions._sparse = false;
refineOptions._faceVertsFirst = options.orderVerticesFromFacesFirst;
for (int i = 1; i <= (int)options.refinementLevel; ++i) {
refineOptions._minimalTopology =
options.fullTopologyInLastLevel ? false : (i == (int)options.refinementLevel);
Vtr::internal::Level& parentLevel = getLevel(i-1);
Vtr::internal::Level& childLevel = *(new Vtr::internal::Level);
Vtr::internal::Refinement* refinement = 0;
if (splitType == Sdc::SPLIT_TO_QUADS) {
refinement = new Vtr::internal::QuadRefinement(parentLevel, childLevel, _subdivOptions);
} else {
refinement = new Vtr::internal::TriRefinement(parentLevel, childLevel, _subdivOptions);
}
refinement->refine(refineOptions);
appendLevel(childLevel);
appendRefinement(*refinement);
}
assembleFarLevels();
}
//
// Internal utility class and function supporting feature adaptive selection of faces...
//
namespace internal {
//
// FeatureMask is a simple set of bits identifying features to be selected during a level of
// adaptive refinement. Adaptive refinement options passed the Refiner are interpreted as a
// specific set of features defined here. Given options to reduce faces generated at deeper
// levels, a method to "reduce" the set of features is also provided here.
//
// This class was specifically not nested in TopologyRefiner to allow simple non-class methods
// to make use of it in the core selection methods. Those selection methods were similarly
// made non-class methods to ensure they conform to the feature set defined by the FeatureMask
// and not some internal class state.
//
class FeatureMask {
public:
typedef TopologyRefiner::AdaptiveOptions Options;
typedef unsigned int int_type;
void Clear() { *((int_type*)this) = 0; }
bool IsEmpty() const { return *((int_type*)this) == 0; }
FeatureMask() { Clear(); }
FeatureMask(Options const & options, int regFaceSize) {
Clear();
InitializeFeatures(options, regFaceSize);
}
// These are the two primary methods intended for use -- intialization via a set of Options
// and reduction of the subsequent feature set (which presumes prior initialization with the
// same set as give)
//
void InitializeFeatures(Options const & options, int regFaceSize);
void ReduceFeatures( Options const & options);
public:
int_type selectXOrdinaryInterior : 1;
int_type selectXOrdinaryBoundary : 1;
int_type selectSemiSharpSingle : 1;
int_type selectSemiSharpNonSingle : 1;
int_type selectInfSharpRegularCrease : 1;
int_type selectInfSharpRegularCorner : 1;
int_type selectInfSharpIrregularDart : 1;
int_type selectInfSharpIrregularCrease : 1;
int_type selectInfSharpIrregularCorner : 1;
int_type selectUnisolatedInteriorEdge : 1;
int_type selectNonManifold : 1;
int_type selectFVarFeatures : 1;
};
void
FeatureMask::InitializeFeatures(Options const & options, int regFaceSize) {
//
// Support for the "single-crease patch" case is limited to the subdivision scheme
// (currently only Catmull-Clark). It has historically been applied to both semi-
// sharp and inf-sharp creases -- the semi-sharp application is still relevant,
// but the inf-sharp has been superceded.
//
// The inf-sharp single-crease case now corresponds to an inf-sharp regular crease
// in the interior -- and since such regular creases on the boundary are never
// considered for selection (just as interior smoot regular faces are not), this
// feature is only relevant for the interior case. So aside from it being used
// when regular inf-sharp features are all selected, it can also be used for the
// single-crease case.
//
bool useSingleCreasePatch = options.useSingleCreasePatch && (regFaceSize == 4);
// Extra-ordinary features (independent of the inf-sharp options):
selectXOrdinaryInterior = true;
selectXOrdinaryBoundary = true;
// Semi-sharp features -- the regular single crease case and all others:
selectSemiSharpSingle = !useSingleCreasePatch;
selectSemiSharpNonSingle = true;
// Inf-sharp features -- boundary extra-ordinary vertices are irreg creases:
selectInfSharpRegularCrease = !(options.useInfSharpPatch || useSingleCreasePatch);
selectInfSharpRegularCorner = !options.useInfSharpPatch;
selectInfSharpIrregularDart = true;
selectInfSharpIrregularCrease = true;
selectInfSharpIrregularCorner = true;
selectUnisolatedInteriorEdge = useSingleCreasePatch && !options.useInfSharpPatch;
selectNonManifold = true;
selectFVarFeatures = options.considerFVarChannels;
}
void
FeatureMask::ReduceFeatures(Options const & options) {
// Disable typical xordinary vertices:
selectXOrdinaryInterior = false;
selectXOrdinaryBoundary = false;
// If minimizing inf-sharp patches, disable all but sharp/corner irregularities
if (options.useInfSharpPatch) {
selectInfSharpRegularCrease = false;
selectInfSharpRegularCorner = false;
selectInfSharpIrregularDart = false;
selectInfSharpIrregularCrease = false;
}
}
} // end namespace internal
void
TopologyRefiner::RefineAdaptive(AdaptiveOptions options,
ConstIndexArray baseFacesToRefine) {
if (_levels[0]->getNumVertices() == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefiner::RefineAdaptive() -- base level is uninitialized.");
return;
}
if (_refinements.size()) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefiner::RefineAdaptive() -- previous refinements already applied.");
return;
}
//
// Initialize member and local variables from the adaptive options:
//
_isUniform = false;
_adaptiveOptions = options;
//
// Initialize the feature-selection options based on given options -- with two sets
// of levels isolating different sets of features, initialize the two feature sets
// up front and use the appropriate one for each level:
//
int nonLinearScheme = Sdc::SchemeTypeTraits::GetLocalNeighborhoodSize(_subdivType);
int shallowLevel = std::min<int>(options.secondaryLevel, options.isolationLevel);
int deeperLevel = options.isolationLevel;
int potentialMaxLevel = nonLinearScheme ? deeperLevel : _hasIrregFaces;
internal::FeatureMask moreFeaturesMask(options, _regFaceSize);
internal::FeatureMask lessFeaturesMask = moreFeaturesMask;
if (shallowLevel < potentialMaxLevel) {
lessFeaturesMask.ReduceFeatures(options);
}
//
// If face-varying channels are considered, make sure non-linear channels are present
// and turn off consideration if none present:
//
if (moreFeaturesMask.selectFVarFeatures && nonLinearScheme) {
bool nonLinearChannelsPresent = false;
for (int channel = 0; channel < _levels[0]->getNumFVarChannels(); ++channel) {
nonLinearChannelsPresent |= !_levels[0]->getFVarLevel(channel).isLinear();
}
if (!nonLinearChannelsPresent) {
moreFeaturesMask.selectFVarFeatures = false;
lessFeaturesMask.selectFVarFeatures = false;
}
}
//
// Initialize refinement options for Vtr -- full topology is always generated in
// the last level as expected usage is for patch retrieval:
//
Vtr::internal::Refinement::Options refineOptions;
refineOptions._sparse = true;
refineOptions._minimalTopology = false;
refineOptions._faceVertsFirst = options.orderVerticesFromFacesFirst;
Sdc::Split splitType = Sdc::SchemeTypeTraits::GetTopologicalSplitType(_subdivType);
for (int i = 1; i <= potentialMaxLevel; ++i) {
Vtr::internal::Level& parentLevel = getLevel(i-1);
Vtr::internal::Level& childLevel = *(new Vtr::internal::Level);
Vtr::internal::Refinement* refinement = 0;
if (splitType == Sdc::SPLIT_TO_QUADS) {
refinement = new Vtr::internal::QuadRefinement(parentLevel, childLevel, _subdivOptions);
} else {
refinement = new Vtr::internal::TriRefinement(parentLevel, childLevel, _subdivOptions);
}
//
// Initialize a Selector to mark a sparse set of components for refinement -- choose
// the feature selection mask appropriate to the level:
//
Vtr::internal::SparseSelector selector(*refinement);
internal::FeatureMask const & levelFeatures = (i <= shallowLevel) ? moreFeaturesMask
: lessFeaturesMask;
if (i > 1) {
selectFeatureAdaptiveComponents(selector, levelFeatures, ConstIndexArray());
} else if (nonLinearScheme) {
selectFeatureAdaptiveComponents(selector, levelFeatures, baseFacesToRefine);
} else {
selectLinearIrregularFaces(selector, baseFacesToRefine);
}
if (selector.isSelectionEmpty()) {
delete refinement;
delete &childLevel;
break;
} else {
refinement->refine(refineOptions);
appendLevel(childLevel);
appendRefinement(*refinement);
}
}
_maxLevel = (unsigned int) _refinements.size();
assembleFarLevels();
}
//
// Local utility functions for selecting features in faces for adaptive refinement:
//
namespace {
//
// First are a couple of low-level utility methods to perform the same analysis
// at a corner or the entire face for specific detection of inf-sharp or boundary
// features. These are shared between the analysis of the main face and those in
// face-varying channels (which only differ from the main face in the presence of
// face-varying boundaries).
//
// The first can be applied equally to an individual corner or to the entire face
// (using its composite tag). The second applies to the entire face, making use
// of the first, and is the main entry point for dealng with inf-sharp features.
//
// Note we can use the composite tag here even though it arises from all corners
// of the face and so does not represent a specific corner. When at least one
// smooth interior vertex exists, it limits the combinations that can exist on the
// remaining corners (though quads and tris cannot be treated equally here).
//
// If any inf-sharp features are to be selected, identify them first as irregular
// or not, then qualify them more specifically. (Remember that a regular vertex
// may have its neighboring faces partitioned into irregular regions in the
// presence of inf-sharp edges. Similarly an irregular vertex may have its
// neighborhood partitioned into regular regions.)
//
inline bool
doesInfSharpVTagHaveFeatures(Vtr::internal::Level::VTag compVTag,
internal::FeatureMask const & featureMask) {
// Note that even though the given VTag may represent an individual corner, we
// use more general bitwise tests here (particularly the Rule) so that we can
// pass in a composite tag for the entire face and have the same tests applied:
//
if (compVTag._infIrregular) {
if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
return featureMask.selectInfSharpIrregularCorner;
} else if (compVTag._rule & Sdc::Crease::RULE_CREASE) {
return compVTag._boundary ? featureMask.selectXOrdinaryBoundary :
featureMask.selectInfSharpIrregularCrease;
} else if (compVTag._rule & Sdc::Crease::RULE_DART) {
return featureMask.selectInfSharpIrregularDart;
}
} else if (compVTag._boundary) {
// Remember that regular boundary features should never be selected, except
// for a boundary crease sharpened (and so a Corner) by an interior edge:
if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
return compVTag._corner ? false : featureMask.selectInfSharpRegularCorner;
} else {
return false;
}
} else {
if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
return featureMask.selectInfSharpRegularCorner;
} else {
return featureMask.selectInfSharpRegularCrease;
}
}
return false;
}
inline bool
doesInfSharpFaceHaveFeatures(Vtr::internal::Level::VTag compVTag,
Vtr::internal::Level::VTag vTags[], int numVerts,
internal::FeatureMask const & featureMask) {
//
// For quads, if at least one smooth corner of a regular face, features
// are isolated enough to make use of the composite tag alone (unless
// boundary isolation is enabled, in which case trivially return).
//
// For tris, the presence of boundaries creates more ambiguity, so we
// need to exclude that case and inspect corner features individually.
//
bool isolateQuadBoundaries = false;
bool atLeastOneSmoothCorner = (compVTag._rule & Sdc::Crease::RULE_SMOOTH);
if (numVerts == 4) {
if (atLeastOneSmoothCorner) {
return doesInfSharpVTagHaveFeatures(compVTag, featureMask);
} else if (isolateQuadBoundaries) {
return true;
} else if (featureMask.selectUnisolatedInteriorEdge) {
// Needed for single-crease approximation to inf-sharp interior edge:
for (int i = 0; i < 4; ++i) {
if (vTags[i]._infSharpEdges && !vTags[i]._boundary) {
return true;
}
}
}
} else {
if (atLeastOneSmoothCorner && !compVTag._boundary) {
return doesInfSharpVTagHaveFeatures(compVTag, featureMask);
}
}
for (int i = 0; i < numVerts; ++i) {
if (!(vTags[i]._rule & Sdc::Crease::RULE_SMOOTH)) {
if (doesInfSharpVTagHaveFeatures(vTags[i], featureMask)) {
return true;
}
}
}
return false;
}
//
// This is the core method/function for analyzing a face and deciding whether or not
// to included it during feature-adaptive refinement.
//
// Topological analysis of the face exploits tags that are applied to corner vertices
// and carried through the refinement hierarchy. The tags were designed with this
// in mind and also to be combined via bitwise-OR to make collective decisions about
// the neighborhood of the entire face.
//
// After a few trivial acceptances/rejections, feature detection is divided up into
// semi-sharp and inf-sharp cases -- note that both may be present, but semi-sharp
// features have an implicit precedence until they decay and so are handled first.
// They are also fairly trivial to deal with (most often requiring selection) while
// the presence of boundaries and additional options complicates the inf-sharp case.
// Since the inf-sharp logic needs to be applied in face-varying cases, it exists in
// a separate method.
//
// This was originally written specific to the quad-centric Catmark scheme and was
// since generalized to support Loop given the enhanced tagging of components based
// on the scheme. Any enhancements here should be aware of the intended generality.
// Ultimately it may not be worth trying to keep this general and we will be better
// off specializing it for each scheme. The fact that this method is intimately tied
// to patch generation also begs for it to become part of a class that encompasses
// both the feature adaptive tagging and the identification of the intended patches
// that result from it.
//
bool
doesFaceHaveFeatures(Vtr::internal::Level const& level, Index face,
internal::FeatureMask const & featureMask, int regFaceSize) {
using Vtr::internal::Level;
ConstIndexArray fVerts = level.getFaceVertices(face);
// Irregular faces (base level) are unconditionally included:
if (fVerts.size() != regFaceSize) {
return true;
}
// Gather and combine the VTags:
Level::VTag vTags[4];
level.getFaceVTags(face, vTags);
Level::VTag compFaceVTag = Level::VTag::BitwiseOr(vTags, fVerts.size());
// Faces incident irregular faces (base level) are unconditionally included:
if (compFaceVTag._incidIrregFace) {
return true;
}
// Incomplete faces (incomplete neighborhood) are unconditionally excluded:
if (compFaceVTag._incomplete) {
return false;
}
// Select non-manifold features if specified, otherwise treat as inf-sharp:
if (compFaceVTag._nonManifold && featureMask.selectNonManifold) {
return true;
}
// Select (smooth) xord vertices if specified, boundaries handled with inf-sharp:
if (compFaceVTag._xordinary && featureMask.selectXOrdinaryInterior) {
if (compFaceVTag._rule == Sdc::Crease::RULE_SMOOTH) {
return true;
} else if (level.getDepth() < 2) {
for (int i = 0; i < fVerts.size(); ++i) {
if (vTags[i]._xordinary && (vTags[i]._rule == Sdc::Crease::RULE_SMOOTH)) {
return true;
}
}
}
}
// If all smooth corners, no remaining features to select (x-ordinary dealt with):
if (compFaceVTag._rule == Sdc::Crease::RULE_SMOOTH) {
return false;
}
// Semi-sharp features -- select all immediately or test the single-crease case:
if (compFaceVTag._semiSharp || compFaceVTag._semiSharpEdges) {
if (featureMask.selectSemiSharpSingle && featureMask.selectSemiSharpNonSingle) {
return true;
} else if (level.isSingleCreasePatch(face)) {
return featureMask.selectSemiSharpSingle;
} else {
return featureMask.selectSemiSharpNonSingle;
}
}
// Inf-sharp features (including boundaries) -- delegate to shared method:
if (compFaceVTag._infSharp || compFaceVTag._infSharpEdges) {
return doesInfSharpFaceHaveFeatures(compFaceVTag, vTags, fVerts.size(), featureMask);
}
return false;
}
//
// Analyzing the face-varying topology for selection is considerably simpler that
// for the face and its vertices -- in part due to the fact that these faces lie on
// face-varying boundaries, and also due to assumptions about prior inspection:
//
// - it is assumed the face topologgy does not match, so the face must lie on
// a FVar boundary, i.e. inf-sharp
//
// - it is assumed the face vertices were already inspected, so cases such as
// semi-sharp or smooth interior x-ordinary features have already triggered
// selection
//
// That leaves the inspection of inf-sharp features, for the tags from the face
// varying channel -- code that is shared with the main face.
//
bool
doesFaceHaveDistinctFaceVaryingFeatures(Vtr::internal::Level const& level, Index face,
internal::FeatureMask const & featureMask, int fvarChannel) {
using Vtr::internal::Level;
ConstIndexArray fVerts = level.getFaceVertices(face);
assert(!level.doesFaceFVarTopologyMatch(face, fvarChannel));
// We can't use the composite VTag for the face here as it only includes the FVar
// values specific to this face. We need to account for all FVar values around
// each corner of the face -- including those in potentially completely disjoint
// sets -- to ensure that adjacent faces remain compatibly refined (i.e. differ
// by only one level), so we use the composite tags for the corner vertices:
//
Level::VTag vTags[4];
for (int i = 0; i < fVerts.size(); ++i) {
vTags[i] = level.getVertexCompositeFVarVTag(fVerts[i], fvarChannel);
}
Level::VTag compVTag = Level::VTag::BitwiseOr(vTags, fVerts.size());
// Select non-manifold features if specified, otherwise treat as inf-sharp:
if (compVTag._nonManifold && featureMask.selectNonManifold) {
return true;
}
// Any remaining locally extra-ordinary face-varying boundaries warrant selection:
if (compVTag._xordinary && featureMask.selectXOrdinaryInterior) {
return true;
}
// Given faces with differing FVar topology are on boundaries, defer to inf-sharp:
return doesInfSharpFaceHaveFeatures(compVTag, vTags, fVerts.size(), featureMask);
}
} // end namespace
//
// Method for selecting components for sparse refinement based on the feature-adaptive needs
// of patch generation.
//
// It assumes we have a freshly initialized SparseSelector (i.e. nothing already selected)
// and will select all relevant topological features for inclusion in the subsequent sparse
// refinement.
//
void
TopologyRefiner::selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector& selector,
internal::FeatureMask const & featureMask,
ConstIndexArray facesToRefine) {
//
// Inspect each face and the properties tagged at all of its corners:
//
Vtr::internal::Level const& level = selector.getRefinement().parent();
int numFacesToRefine = facesToRefine.size() ? facesToRefine.size() : level.getNumFaces();
int numFVarChannels = featureMask.selectFVarFeatures ? level.getNumFVarChannels() : 0;
for (int fIndex = 0; fIndex < numFacesToRefine; ++fIndex) {
Vtr::Index face = facesToRefine.size() ? facesToRefine[fIndex] : (Index) fIndex;
if (HasHoles() && level.isFaceHole(face)) continue;
//
// Test if the face has any of the specified features present. If not, and FVar
// channels are to be considered, look for features in the FVar channels:
//
bool selectFace = doesFaceHaveFeatures(level, face, featureMask, _regFaceSize);
if (!selectFace && featureMask.selectFVarFeatures) {
for (int channel = 0; !selectFace && (channel < numFVarChannels); ++channel) {
// Only test the face for this channel if the topology does not match:
if (!level.doesFaceFVarTopologyMatch(face, channel)) {
selectFace = doesFaceHaveDistinctFaceVaryingFeatures(
level, face, featureMask, channel);
}
}
}
if (selectFace) {
selector.selectFace(face);
}
}
}
void
TopologyRefiner::selectLinearIrregularFaces(Vtr::internal::SparseSelector& selector,
ConstIndexArray facesToRefine) {
//
// Inspect each face and select only irregular faces:
//
Vtr::internal::Level const& level = selector.getRefinement().parent();
int numFacesToRefine = facesToRefine.size() ? facesToRefine.size() : level.getNumFaces();
for (int fIndex = 0; fIndex < numFacesToRefine; ++fIndex) {
Vtr::Index face = facesToRefine.size() ? facesToRefine[fIndex] : (Index) fIndex;
if (HasHoles() && level.isFaceHole(face)) continue;
if (level.getFaceVertices(face).size() != _regFaceSize) {
selector.selectFace(face);
}
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,298 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_TOPOLOGY_REFINER_H
#define OPENSUBDIV3_FAR_TOPOLOGY_REFINER_H
#include "../version.h"
#include "../sdc/types.h"
#include "../sdc/options.h"
#include "../far/types.h"
#include "../far/topologyLevel.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr { namespace internal { class SparseSelector; } }
namespace Far { namespace internal { class FeatureMask; } }
namespace Far {
template <typename REAL> class PrimvarRefinerReal;
template <class MESH> class TopologyRefinerFactory;
///
/// \brief Stores topology data for a specified set of refinement options.
///
class TopologyRefiner {
public:
/// \brief Constructor
TopologyRefiner(Sdc::SchemeType type, Sdc::Options options = Sdc::Options());
/// \brief Destructor
~TopologyRefiner();
/// \brief Returns the subdivision scheme
Sdc::SchemeType GetSchemeType() const { return _subdivType; }
/// \brief Returns the subdivision options
Sdc::Options GetSchemeOptions() const { return _subdivOptions; }
/// \brief Returns true if uniform refinement has been applied
bool IsUniform() const { return _isUniform; }
/// \brief Returns the number of refinement levels
int GetNumLevels() const { return (int)_farLevels.size(); }
/// \brief Returns the highest level of refinement
int GetMaxLevel() const { return _maxLevel; }
/// \brief Returns the maximum vertex valence in all levels
int GetMaxValence() const { return _maxValence; }
/// \brief Returns true if faces have been tagged as holes
bool HasHoles() const { return _hasHoles; }
/// \brief Returns the total number of vertices in all levels
int GetNumVerticesTotal() const { return _totalVertices; }
/// \brief Returns the total number of edges in all levels
int GetNumEdgesTotal() const { return _totalEdges; }
/// \brief Returns the total number of edges in all levels
int GetNumFacesTotal() const { return _totalFaces; }
/// \brief Returns the total number of face vertices in all levels
int GetNumFaceVerticesTotal() const { return _totalFaceVertices; }
/// \brief Returns a handle to access data specific to a particular level
TopologyLevel const & GetLevel(int level) const { return _farLevels[level]; }
//@{
/// @name High-level refinement and related methods
///
//
// Uniform refinement
//
/// \brief Uniform refinement options
///
/// Options for uniform refinement, including the number of levels, vertex
/// ordering and generation of topology information.
///
/// Note the impact of the option to generate fullTopologyInLastLevel. Given
/// subsequent levels of uniform refinement typically reguire 4x the data
/// of the previous level, only the minimum amount of data is generated in the
/// last level by default, i.e. a vertex and face-vertex list. If requiring
/// topology traversal of the last level, e.g. inspecting edges or incident
/// faces of vertices, the option to generate full topology in the last
/// level should be enabled.
///
struct UniformOptions {
UniformOptions(int level) :
refinementLevel(level),
orderVerticesFromFacesFirst(false),
fullTopologyInLastLevel(false) { }
unsigned int refinementLevel:4, ///< Number of refinement iterations
orderVerticesFromFacesFirst:1, ///< Order child vertices from faces first
///< instead of child vertices of vertices
fullTopologyInLastLevel:1; ///< Skip topological relationships in the last
///< level of refinement that are not needed for
///< interpolation (keep false if using limit).
};
/// \brief Refine the topology uniformly
///
/// This method applies uniform refinement to the level specified in the
/// given UniformOptions.
///
/// Note the impact of the UniformOption to generate fullTopologyInLastLevel
/// and be sure it is assigned to satisfy the needs of the resulting refinement.
///
/// @param options Options controlling uniform refinement
///
void RefineUniform(UniformOptions options);
/// \brief Returns the options specified on refinement
UniformOptions GetUniformOptions() const { return _uniformOptions; }
//
// Adaptive refinement
//
/// \brief Adaptive refinement options
struct AdaptiveOptions {
AdaptiveOptions(int level) :
isolationLevel(level),
secondaryLevel(15),
useSingleCreasePatch(false),
useInfSharpPatch(false),
considerFVarChannels(false),
orderVerticesFromFacesFirst(false) { }
unsigned int isolationLevel:4; ///< Number of iterations applied to isolate
///< extraordinary vertices and creases
unsigned int secondaryLevel:4; ///< Shallower level to stop isolation of
///< smooth irregular features
unsigned int useSingleCreasePatch:1; ///< Use 'single-crease' patch and stop
///< isolation where applicable
unsigned int useInfSharpPatch:1; ///< Use infinitely sharp patches and stop
///< isolation where applicable
unsigned int considerFVarChannels:1; ///< Inspect face-varying channels and
///< isolate when irregular features present
unsigned int orderVerticesFromFacesFirst:1; ///< Order child vertices from faces first
///< instead of child vertices of vertices
};
/// \brief Feature Adaptive topology refinement
///
/// @param options Options controlling adaptive refinement
///
/// @param selectedFaces Limit adaptive refinement to the specified faces
///
void RefineAdaptive(AdaptiveOptions options,
ConstIndexArray selectedFaces = ConstIndexArray());
/// \brief Returns the options specified on refinement
AdaptiveOptions GetAdaptiveOptions() const { return _adaptiveOptions; }
/// \brief Unrefine the topology, keeping only the base level.
void Unrefine();
//@{
/// @name Number and properties of face-varying channels:
///
/// \brief Returns the number of face-varying channels in the tables
int GetNumFVarChannels() const;
/// \brief Returns the face-varying interpolation rule set for a given channel
Sdc::Options::FVarLinearInterpolation GetFVarLinearInterpolation(int channel = 0) const;
/// \brief Returns the total number of face-varying values in all levels
int GetNumFVarValuesTotal(int channel = 0) const;
//@}
protected:
//
// Lower level protected methods intended strictly for internal use:
//
template <class MESH>
friend class TopologyRefinerFactory;
friend class TopologyRefinerFactoryBase;
friend class PatchTableBuilder;
friend class PatchBuilder;
friend class PtexIndices;
template <typename REAL>
friend class PrimvarRefinerReal;
// Copy constructor exposed via the factory class:
TopologyRefiner(TopologyRefiner const & source);
Vtr::internal::Level & getLevel(int l) { return *_levels[l]; }
Vtr::internal::Level const & getLevel(int l) const { return *_levels[l]; }
Vtr::internal::Refinement & getRefinement(int l) { return *_refinements[l]; }
Vtr::internal::Refinement const & getRefinement(int l) const { return *_refinements[l]; }
private:
// Not default constructible or copyable:
TopologyRefiner() : _uniformOptions(0), _adaptiveOptions(0) { }
TopologyRefiner & operator=(TopologyRefiner const &) { return *this; }
void selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector& selector,
internal::FeatureMask const & mask,
ConstIndexArray selectedFaces);
void selectLinearIrregularFaces(Vtr::internal::SparseSelector& selector,
ConstIndexArray selectedFaces);
void initializeInventory();
void updateInventory(Vtr::internal::Level const & newLevel);
void appendLevel(Vtr::internal::Level & newLevel);
void appendRefinement(Vtr::internal::Refinement & newRefinement);
void assembleFarLevels();
private:
Sdc::SchemeType _subdivType;
Sdc::Options _subdivOptions;
unsigned int _isUniform : 1;
unsigned int _hasHoles : 1;
unsigned int _hasIrregFaces : 1;
unsigned int _regFaceSize : 3;
unsigned int _maxLevel : 4;
// Options assigned on refinement:
UniformOptions _uniformOptions;
AdaptiveOptions _adaptiveOptions;
// Cumulative properties of all levels:
int _totalVertices;
int _totalEdges;
int _totalFaces;
int _totalFaceVertices;
int _maxValence;
// Note the base level may be shared with another instance
bool _baseLevelOwned;
std::vector<Vtr::internal::Level *> _levels;
std::vector<Vtr::internal::Refinement *> _refinements;
std::vector<TopologyLevel> _farLevels;
};
inline int
TopologyRefiner::GetNumFVarChannels() const {
return _levels[0]->getNumFVarChannels();
}
inline Sdc::Options::FVarLinearInterpolation
TopologyRefiner::GetFVarLinearInterpolation(int channel) const {
return _levels[0]->getFVarOptions(channel).GetFVarLinearInterpolation();
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TOPOLOGY_REFINER_H */

View File

@@ -0,0 +1,444 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/topologyRefinerFactory.h"
#include "../far/topologyRefiner.h"
#include "../sdc/types.h"
#include "../vtr/level.h"
#include <cstdio>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Methods for the Factory base class -- general enough to warrant including
// in the base class rather than the subclass template (and so replicated for
// each usage)
//
//
bool
TopologyRefinerFactoryBase::prepareComponentTopologySizing(
TopologyRefiner& refiner) {
Vtr::internal::Level& baseLevel = refiner.getLevel(0);
//
// At minimum we require face-vertices (the total count of which can be
// determined from the offsets accumulated during sizing pass) and we
// need to resize members related to them to be populated during
// assignment:
//
int vCount = baseLevel.getNumVertices();
int fCount = baseLevel.getNumFaces();
if (vCount == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"mesh contains no vertices.");
return false;
}
if (fCount == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"meshes without faces not yet supported.");
return false;
}
// Make sure no face was defined that would lead to a valence overflow --
// the max valence has been initialized with the maximum number of
// face-vertices:
if (baseLevel.getMaxValence() > Vtr::VALENCE_LIMIT) {
char msg[1024];
snprintf(msg, 1024,
"Failure in TopologyRefinerFactory<>::Create() -- "
"face with %d vertices > %d max.",
baseLevel.getMaxValence(), Vtr::VALENCE_LIMIT);
Error(FAR_RUNTIME_ERROR, msg);
return false;
}
int fVertCount = baseLevel.getNumFaceVertices(fCount - 1) +
baseLevel.getOffsetOfFaceVertices(fCount - 1);
if (fVertCount == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"mesh contains no face-vertices.");
return false;
}
if ((refiner.GetSchemeType() == Sdc::SCHEME_LOOP) &&
(fVertCount != (3 * fCount))) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"non-triangular faces not supported by Loop scheme.");
return false;
}
baseLevel.resizeFaceVertices(fVertCount);
//
// If edges were sized, all other topological relations must be sized
// with it, in which case we allocate those members to be populated.
// Otherwise, sizing of the other topology members is deferred until
// the face-vertices are assigned and the resulting relationships
// determined:
//
int eCount = baseLevel.getNumEdges();
if (eCount > 0) {
baseLevel.resizeFaceEdges(baseLevel.getNumFaceVerticesTotal());
baseLevel.resizeEdgeVertices();
baseLevel.resizeEdgeFaces( baseLevel.getNumEdgeFaces(eCount-1) +
baseLevel.getOffsetOfEdgeFaces(eCount-1));
baseLevel.resizeVertexFaces(baseLevel.getNumVertexFaces(vCount-1) +
baseLevel.getOffsetOfVertexFaces(vCount-1));
baseLevel.resizeVertexEdges(baseLevel.getNumVertexEdges(vCount-1) +
baseLevel.getOffsetOfVertexEdges(vCount-1));
assert(baseLevel.getNumFaceEdgesTotal() > 0);
assert(baseLevel.getNumEdgeVerticesTotal() > 0);
assert(baseLevel.getNumEdgeFacesTotal() > 0);
assert(baseLevel.getNumVertexFacesTotal() > 0);
assert(baseLevel.getNumVertexEdgesTotal() > 0);
}
return true;
}
bool
TopologyRefinerFactoryBase::prepareComponentTopologyAssignment(
TopologyRefiner& refiner, bool fullValidation,
TopologyCallback callback, void const * callbackData) {
Vtr::internal::Level& baseLevel = refiner.getLevel(0);
bool completeMissingTopology = (baseLevel.getNumEdges() == 0);
if (completeMissingTopology) {
if (! baseLevel.completeTopologyFromFaceVertices()) {
char msg[1024];
snprintf(msg, 1024,
"Failure in TopologyRefinerFactory<>::Create() -- "
"vertex with valence %d > %d max.",
baseLevel.getMaxValence(), Vtr::VALENCE_LIMIT);
Error(FAR_RUNTIME_ERROR, msg);
return false;
}
} else {
if (baseLevel.getMaxValence() == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"maximum valence not assigned.");
return false;
}
}
if (fullValidation) {
if (! baseLevel.validateTopology(callback, callbackData)) {
if (completeMissingTopology) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"invalid topology detected from partial specification.");
} else {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"invalid topology detected as fully specified.");
}
return false;
}
}
// Now that we have a valid base level, initialize the Refiner's
// component inventory:
refiner.initializeInventory();
return true;
}
bool
TopologyRefinerFactoryBase::prepareComponentTagsAndSharpness(
TopologyRefiner& refiner) {
//
// This method combines the initialization of internal component tags
// with the sharpening of edges and vertices according to the given
// boundary interpolation rule in the Options.
// Since both involve traversing the edge and vertex lists and noting
// the presence of boundaries -- best to do both at once...
//
Vtr::internal::Level& baseLevel = refiner.getLevel(0);
Sdc::Options options = refiner.GetSchemeOptions();
Sdc::Crease creasing(options);
bool makeBoundaryFacesHoles =
(options.GetVtxBoundaryInterpolation() ==
Sdc::Options::VTX_BOUNDARY_NONE) &&
(Sdc::SchemeTypeTraits::GetLocalNeighborhoodSize(
refiner.GetSchemeType()) > 0);
bool sharpenCornerVerts =
(options.GetVtxBoundaryInterpolation() ==
Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER);
bool sharpenNonManFeatures = true;
//
// Before initializing edge and vertex tags, tag any qualifying boundary
// faces as holes before the sharpness of incident vertices and edges is
// affected by boundary interpolation rules.
//
// Faces will be excluded (tagged as holes) if they contain a vertex on a
// boundary that did not have all of its incident boundary edges sharpened
// (not just the boundary edges within the face), so inspect the vertices
// and tag their incident faces when necessary:
//
if (makeBoundaryFacesHoles) {
for (Vtr::Index vIndex = 0; vIndex < baseLevel.getNumVertices();
++vIndex) {
Vtr::ConstIndexArray vEdges = baseLevel.getVertexEdges(vIndex);
Vtr::ConstIndexArray vFaces = baseLevel.getVertexFaces(vIndex);
// Ignore manifold interior vertices:
if ((vEdges.size() == vFaces.size()) &&
!baseLevel.getVertexTag(vIndex)._nonManifold) {
continue;
}
bool excludeFaces = false;
for (int i = 0; !excludeFaces && (i < vEdges.size()); ++i) {
excludeFaces = (baseLevel.getNumEdgeFaces(vEdges[i]) == 1) &&
!Sdc::Crease::IsInfinite(
baseLevel.getEdgeSharpness(vEdges[i]));
}
if (excludeFaces) {
for (int i = 0; i < vFaces.size(); ++i) {
baseLevel.getFaceTag(vFaces[i])._hole = true;
}
// Need to tag Refiner (the Level does not keep track of this)
refiner._hasHoles = true;
}
}
}
//
// Process the Edge tags first, as Vertex tags (notably the Rule) are
// dependent on properties of their incident edges.
//
for (Vtr::Index eIndex = 0; eIndex < baseLevel.getNumEdges(); ++eIndex) {
Vtr::internal::Level::ETag& eTag = baseLevel.getEdgeTag(eIndex);
float& eSharpness = baseLevel.getEdgeSharpness(eIndex);
eTag._boundary = (baseLevel.getNumEdgeFaces(eIndex) < 2);
if (eTag._boundary || (eTag._nonManifold && sharpenNonManFeatures)) {
eSharpness = Sdc::Crease::SHARPNESS_INFINITE;
}
eTag._infSharp = Sdc::Crease::IsInfinite(eSharpness);
eTag._semiSharp = Sdc::Crease::IsSharp(eSharpness) && !eTag._infSharp;
}
//
// Process the Vertex tags now -- for some tags (semi-sharp and its rule)
// we need to inspect all incident edges:
//
int schemeRegularInteriorValence =
Sdc::SchemeTypeTraits::GetRegularVertexValence(refiner.GetSchemeType());
int schemeRegularBoundaryValence = schemeRegularInteriorValence / 2;
for (Vtr::Index vIndex = 0; vIndex < baseLevel.getNumVertices(); ++vIndex) {
Vtr::internal::Level::VTag& vTag = baseLevel.getVertexTag(vIndex);
float& vSharpness = baseLevel.getVertexSharpness(vIndex);
Vtr::ConstIndexArray vEdges = baseLevel.getVertexEdges(vIndex);
Vtr::ConstIndexArray vFaces = baseLevel.getVertexFaces(vIndex);
//
// Take inventory of properties of incident edges that affect this
// vertex:
//
int boundaryEdgeCount = 0;
int infSharpEdgeCount = 0;
int semiSharpEdgeCount = 0;
int nonManifoldEdgeCount = 0;
for (int i = 0; i < vEdges.size(); ++i) {
Vtr::internal::Level::ETag const& eTag =
baseLevel.getEdgeTag(vEdges[i]);
boundaryEdgeCount += eTag._boundary;
infSharpEdgeCount += eTag._infSharp;
semiSharpEdgeCount += eTag._semiSharp;
nonManifoldEdgeCount += eTag._nonManifold;
}
int sharpEdgeCount = infSharpEdgeCount + semiSharpEdgeCount;
//
// Sharpen the vertex before using it in conjunction with incident edge
// properties to determine the semi-sharp tag and rule:
//
bool isTopologicalCorner = (vFaces.size() == 1) && (vEdges.size() == 2);
bool isSharpenedCorner = isTopologicalCorner && sharpenCornerVerts;
if (isSharpenedCorner) {
vSharpness = Sdc::Crease::SHARPNESS_INFINITE;
} else if (vTag._nonManifold && sharpenNonManFeatures) {
//
// We avoid sharpening non-manifold vertices when they occur on
// interior non-manifold creases, i.e. a pair of opposing non-
// manifold edges with more than two incident faces. In these
// cases there are more incident faces than edges (1 more for
// each additional "fin") and no boundaries.
//
if (! ((nonManifoldEdgeCount == 2) && (boundaryEdgeCount == 0) &&
(vFaces.size() > vEdges.size()))) {
vSharpness = Sdc::Crease::SHARPNESS_INFINITE;
}
}
vTag._infSharp = Sdc::Crease::IsInfinite(vSharpness);
vTag._semiSharp = Sdc::Crease::IsSemiSharp(vSharpness);
vTag._semiSharpEdges = (semiSharpEdgeCount > 0);
vTag._rule = (Vtr::internal::Level::VTag::VTagSize)
creasing.DetermineVertexVertexRule(vSharpness, sharpEdgeCount);
//
// Assign topological tags -- note that the "xordinary" tag is not
// assigned if non-manifold:
//
vTag._boundary = (boundaryEdgeCount > 0);
vTag._corner = isTopologicalCorner && vTag._infSharp;
if (vTag._nonManifold) {
vTag._xordinary = false;
} else if (vTag._corner) {
vTag._xordinary = false;
} else if (vTag._boundary) {
vTag._xordinary = (vFaces.size() != schemeRegularBoundaryValence);
} else {
vTag._xordinary = (vFaces.size() != schemeRegularInteriorValence);
}
vTag._incomplete = 0;
//
// Assign tags specific to inf-sharp features to identify regular
// topologies partitioned by inf-sharp creases -- must be no semi-
// harp features here (and manifold for now):
//
vTag._infSharpEdges = (infSharpEdgeCount > 0);
vTag._infSharpCrease = false;
vTag._infIrregular = vTag._infSharp || vTag._infSharpEdges;
if (vTag._infSharpEdges) {
// Ignore semi-sharp vertex sharpness when computing the
// inf-sharp Rule:
Sdc::Crease::Rule infRule = creasing.DetermineVertexVertexRule(
(vTag._infSharp ? vSharpness : 0.0f), infSharpEdgeCount);
if (infRule == Sdc::Crease::RULE_CREASE) {
vTag._infSharpCrease = true;
// A "regular" inf-crease can only occur along a manifold
// regular boundary or by bisecting a manifold interior
// region (it is also possible along non-manifold vertices
// in some cases, but that requires much more effort to
// detect -- perhaps later...)
//
if (!vTag._xordinary && !vTag._nonManifold) {
if (vTag._boundary) {
vTag._infIrregular = false;
} else {
assert((schemeRegularInteriorValence == 4) ||
(schemeRegularInteriorValence == 6));
if (schemeRegularInteriorValence == 4) {
vTag._infIrregular =
(baseLevel.getEdgeTag(vEdges[0])._infSharp !=
baseLevel.getEdgeTag(vEdges[2])._infSharp);
} else if (schemeRegularInteriorValence == 6) {
vTag._infIrregular =
(baseLevel.getEdgeTag(vEdges[0])._infSharp !=
baseLevel.getEdgeTag(vEdges[3])._infSharp) ||
(baseLevel.getEdgeTag(vEdges[1])._infSharp !=
baseLevel.getEdgeTag(vEdges[4])._infSharp);
}
}
}
} else if (infRule == Sdc::Crease::RULE_CORNER) {
// A regular set of inf-corners occurs when all edges are
// sharp and not a smooth corner:
//
if ((infSharpEdgeCount == vEdges.size() &&
((vEdges.size() > 2) || vTag._infSharp))) {
vTag._infIrregular = false;
}
}
}
//
// If any irregular faces are present, mark whether or not a vertex
// is incident any irregular face:
//
if (refiner._hasIrregFaces) {
int regSize = refiner._regFaceSize;
for (int i = 0; i < vFaces.size(); ++i) {
if (baseLevel.getFaceVertices(vFaces[i]).size() != regSize) {
vTag._incidIrregFace = true;
break;
}
}
}
}
return true;
}
bool
TopologyRefinerFactoryBase::prepareFaceVaryingChannels(
TopologyRefiner& refiner) {
Vtr::internal::Level& baseLevel = refiner.getLevel(0);
int regVertexValence =
Sdc::SchemeTypeTraits::GetRegularVertexValence(refiner.GetSchemeType());
int regBoundaryValence = regVertexValence / 2;
for (int channel=0; channel<refiner.GetNumFVarChannels(); ++channel) {
if (baseLevel.getNumFVarValues(channel) == 0) {
char msg[1024];
snprintf(msg, 1024,
"Failure in TopologyRefinerFactory<>::Create() -- "
"face-varying channel %d has no values.", channel);
Error(FAR_RUNTIME_ERROR, msg);
return false;
}
baseLevel.completeFVarChannelTopology(channel, regBoundaryValence);
}
return true;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,710 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H
#define OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H
#include "../version.h"
#include "../far/topologyRefiner.h"
#include "../far/error.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
///\brief Private base class of Factories for constructing TopologyRefiners
///
/// TopologyRefinerFactoryBase is the base class for subclasses that are intended to
/// construct TopologyRefiners directly from meshes in their native representations.
/// The subclasses are parameterized by the mesh type \<class MESH\> and are expected
/// to inherit the details related to assembly and validation provided here that are
/// independent of the subclass' mesh type.
//
class TopologyRefinerFactoryBase {
protected:
//
// Protected methods invoked by the subclass template to verify and process each
// stage of construction implemented by the subclass:
//
typedef Vtr::internal::Level::ValidationCallback TopologyCallback;
static bool prepareComponentTopologySizing(TopologyRefiner& refiner);
static bool prepareComponentTopologyAssignment(TopologyRefiner& refiner, bool fullValidation,
TopologyCallback callback, void const * callbackData);
static bool prepareComponentTagsAndSharpness(TopologyRefiner& refiner);
static bool prepareFaceVaryingChannels(TopologyRefiner& refiner);
};
///\brief Factory for constructing TopologyRefiners from specific mesh classes.
///
/// TopologyRefinerFactory<MESH> is the factory class template to convert an instance of
/// TopologyRefiner from an arbitrary mesh class. While a class template, the implementation
/// is not (cannot) be complete, so specialization of a few methods is required (it is a
/// stateless factory, so no instance and only static methods).
///
/// This template provides both the interface and high level assembly for the construction
/// of the TopologyRefiner instance. The high level construction executes a specific set
/// of operations to convert the client's MESH into TopologyRefiner. This set of operations
/// combines methods independent of MESH from the base class with those specialized here for
/// class MESH.
///
template <class MESH>
class TopologyRefinerFactory : public TopologyRefinerFactoryBase {
public:
/// \brief Options related to the construction of each TopologyRefiner.
///
struct Options {
Options(Sdc::SchemeType sdcType = Sdc::SCHEME_CATMARK, Sdc::Options sdcOptions = Sdc::Options()) :
schemeType(sdcType),
schemeOptions(sdcOptions),
validateFullTopology(false) { }
Sdc::SchemeType schemeType; ///< The subdivision scheme type identifier
Sdc::Options schemeOptions; ///< The full set of options for the scheme,
///< e.g. boundary interpolation rules...
unsigned int validateFullTopology : 1; ///< Apply more extensive validation of
///< the constructed topology -- intended
///< for debugging.
};
/// \brief Instantiates a TopologyRefiner from client-provided topological
/// representation.
///
/// If only the face-vertices topological relationships are specified
/// with this factory, edge relationships have to be inferred, which
/// requires additional processing. If the client topological rep can
/// provide this information, it is highly recommended to do so.
///
/// @param mesh Client's topological representation (or a converter)
//
/// @param options Options controlling the creation of the TopologyRefiner
///
/// @return A new instance of TopologyRefiner or 0 for failure
///
static TopologyRefiner* Create(MESH const& mesh, Options options = Options());
/// \brief Instantiates a TopologyRefiner from the base level of an
/// existing instance.
///
/// This allows lightweight copies of the same topology to be refined
/// differently for each new instance. As with other classes that refer
/// to an existing TopologyRefiner, it must generally exist for the entire
/// lifetime of the new instance. In this case, the base level of the
/// original instance must be preserved.
///
/// @param baseLevel An existing TopologyRefiner to share base level.
///
/// @return A new instance of TopologyRefiner or 0 for failure
///
static TopologyRefiner* Create(TopologyRefiner const & baseLevel);
protected:
typedef Vtr::internal::Level::TopologyError TopologyError;
//@{
/// @name Methods to be provided to complete assembly of the TopologyRefiner
///
///
/// These methods are to be specialized to implement all details specific to
/// class MESH required to convert MESH data to TopologyRefiner. Note that
/// some of these *must* be specialized in order to complete construction while
/// some are optional.
///
/// There are two minimal construction requirements (to specify the size and
/// content of all topology relations) and three optional (to specify feature
/// tags, face-varying data, and runtime validation and error reporting).
///
/// See comments in the generic stubs, the factory for Far::TopologyDescriptor
/// or the tutorials for more details on writing these.
///
/// \brief Specify the number of vertices, faces, face-vertices, etc.
static bool resizeComponentTopology(TopologyRefiner& newRefiner, MESH const& mesh);
/// \brief Specify the relationships between vertices, faces, etc. ie the
/// face-vertices, vertex-faces, edge-vertices, etc.
static bool assignComponentTopology(TopologyRefiner& newRefiner, MESH const& mesh);
/// \brief (Optional) Specify edge or vertex sharpness or face holes
static bool assignComponentTags(TopologyRefiner& newRefiner, MESH const& mesh);
/// \brief (Optional) Specify face-varying data per face
static bool assignFaceVaryingTopology(TopologyRefiner& newRefiner, MESH const& mesh);
/// \brief (Optional) Control run-time topology validation and error reporting
static void reportInvalidTopology(TopologyError errCode, char const * msg, MESH const& mesh);
//@}
protected:
//@{
/// @name Base level assembly methods to be used within resizeComponentTopology()
///
/// \brief These methods specify sizes of various quantities, e.g. the number of
/// vertices, faces, face-vertices, etc. The number of the primary components
/// (vertices, faces and edges) should be specified prior to anything else that
/// references them (e.g. we need to know the number of faces before specifying
/// the vertices for that face.
///
/// If a full boundary representation with all neighborhood information is not
/// available, e.g. faces and vertices are available but not edges, only the
/// face-vertices should be specified. The remaining topological relationships
/// will be constructed later in the assembly (though at greater cost than if
/// specified directly).
///
/// The sizes for topological relationships between individual components should be
/// specified in order, i.e. the number of face-vertices for each successive face.
///
/// \brief Specify the number of vertices to be accommodated
static void setNumBaseVertices(TopologyRefiner & newRefiner, int count);
/// \brief Specify the number of faces to be accommodated
static void setNumBaseFaces(TopologyRefiner & newRefiner, int count);
/// \brief Specify the number of edges to be accommodated
static void setNumBaseEdges(TopologyRefiner & newRefiner, int count);
/// \brief Specify the number of vertices incident each face
static void setNumBaseFaceVertices(TopologyRefiner & newRefiner, Index f, int count);
/// \brief Specify the number of faces incident each edge
static void setNumBaseEdgeFaces(TopologyRefiner & newRefiner, Index e, int count);
/// \brief Specify the number of faces incident each vertex
static void setNumBaseVertexFaces(TopologyRefiner & newRefiner, Index v, int count);
/// \brief Specify the number of edges incident each vertex
static void setNumBaseVertexEdges(TopologyRefiner & newRefiner, Index v, int count);
static int getNumBaseVertices(TopologyRefiner const & newRefiner);
static int getNumBaseFaces(TopologyRefiner const & newRefiner);
static int getNumBaseEdges(TopologyRefiner const & newRefiner);
//@}
//@{
/// @name Base level assembly methods to be used within assignComponentTopology()
///
/// \brief These methods populate relationships between components -- in much the
/// same manner as they are inspected once the TopologyRefiner is completed.
///
/// An array of fixed size is returned from these methods and its entries are to be
/// populated with the appropriate indices for its neighbors. At minimum, the
/// vertices for each face must be specified. As noted previously, the remaining
/// relationships will be constructed as needed.
///
/// The ordering of entries in these arrays is important -- they are expected to
/// be ordered counter-clockwise for a right-hand orientation.
///
/// Non-manifold components must be explicitly tagged as such and they do not
/// require the ordering expected of manifold components. Special consideration
/// must also be given to certain non-manifold situations, e.g. the same edge
/// cannot appear twice in a face, and a degenerate edge (same vertex at both
/// ends) can only have one incident face. Such considerations are typically
/// achievable by creating multiple instances of an edge. So while there will
/// always be a one-to-one correspondence between vertices and faces, the same
/// is not guaranteed of edges in certain non-manifold circumstances.
///
/// \brief Assign the vertices incident each face
static IndexArray getBaseFaceVertices(TopologyRefiner & newRefiner, Index f);
/// \brief Assign the edges incident each face
static IndexArray getBaseFaceEdges(TopologyRefiner & newRefiner, Index f);
/// \brief Assign the vertices incident each edge
static IndexArray getBaseEdgeVertices(TopologyRefiner & newRefiner, Index e);
/// \brief Assign the faces incident each edge
static IndexArray getBaseEdgeFaces(TopologyRefiner & newRefiner, Index e);
/// \brief Assign the faces incident each vertex
static IndexArray getBaseVertexFaces(TopologyRefiner & newRefiner, Index v);
/// \brief Assign the edges incident each vertex
static IndexArray getBaseVertexEdges(TopologyRefiner & newRefiner, Index v);
/// \brief Assign the local indices of a vertex within each of its incident faces
static LocalIndexArray getBaseVertexFaceLocalIndices(TopologyRefiner & newRefiner, Index v);
/// \brief Assign the local indices of a vertex within each of its incident edges
static LocalIndexArray getBaseVertexEdgeLocalIndices(TopologyRefiner & newRefiner, Index v);
/// \brief Assign the local indices of an edge within each of its incident faces
static LocalIndexArray getBaseEdgeFaceLocalIndices(TopologyRefiner & newRefiner, Index e);
/// \brief Determine all local indices by inspection (only for pure manifold meshes)
static void populateBaseLocalIndices(TopologyRefiner & newRefiner);
/// \brief Tag an edge as non-manifold
static void setBaseEdgeNonManifold(TopologyRefiner & newRefiner, Index e, bool b);
/// \brief Tag a vertex as non-manifold
static void setBaseVertexNonManifold(TopologyRefiner & newRefiner, Index v, bool b);
//@}
//@{
/// @name Base level assembly methods to be used within assignComponentTags()
///
/// These methods are used to assign edge or vertex sharpness, for tagging faces
/// as holes, etc. Unlike topological assignment, only those components that
/// possess a feature of interest need be explicitly assigned.
///
/// Since topological construction is largely complete by this point, a method is
/// available to identify an edge for sharpness assignment given a pair of vertices.
///
/// \brief Identify an edge to be assigned a sharpness value given a vertex pair
static Index findBaseEdge(TopologyRefiner const & newRefiner, Index v0, Index v1);
/// \brief Assign a sharpness value to a given edge
static void setBaseEdgeSharpness(TopologyRefiner & newRefiner, Index e, float sharpness);
/// \brief Assign a sharpness value to a given vertex
static void setBaseVertexSharpness(TopologyRefiner & newRefiner, Index v, float sharpness);
/// \brief Tag a face as a hole
static void setBaseFaceHole(TopologyRefiner & newRefiner, Index f, bool isHole);
//@}
//@{
/// @name Base level assembly methods to be used within assignFaceVaryingTopology()
///
/// Face-varying data is assigned to faces in much the same way as face-vertex
/// topology is assigned -- indices for face-varying values are assigned to the
/// corners of each face just as indices for vertices were assigned.
///
/// Independent sets of face-varying data are stored in channels. The identifier
/// of each channel (an integer) is expected whenever referring to face-varying
/// data in any form.
///
/// \brief Create a new face-varying channel with the given number of values
static int createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues);
/// \brief Create a new face-varying channel with the given number of values and independent interpolation options
static int createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues, Sdc::Options const& fvarOptions);
/// \brief Assign the face-varying values for the corners of each face
static IndexArray getBaseFaceFVarValues(TopologyRefiner & newRefiner, Index face, int channel = 0);
//@}
protected:
//
// Not to be specialized:
//
static bool populateBaseLevel(TopologyRefiner& refiner, MESH const& mesh, Options options);
};
//
// Generic implementations:
//
template <class MESH>
TopologyRefiner*
TopologyRefinerFactory<MESH>::Create(MESH const& mesh, Options options) {
TopologyRefiner * refiner = new TopologyRefiner(options.schemeType, options.schemeOptions);
if (! populateBaseLevel(*refiner, mesh, options)) {
delete refiner;
return 0;
}
// Eventually want to move the Refiner's inventory initialization here. Currently it
// is handled after topology assignment, but if the inventory is to include additional
// features (e.g. holes, etc.) it is better off deferred to here.
return refiner;
}
template <class MESH>
TopologyRefiner*
TopologyRefinerFactory<MESH>::Create(TopologyRefiner const & source) {
return new TopologyRefiner(source);
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::populateBaseLevel(TopologyRefiner& refiner, MESH const& mesh, Options options) {
//
// Construction of a specialized topology refiner involves four steps, each of which
// involves a method specialized for MESH followed by one that takes an action in
// response to it or in preparation for the next step.
//
// Both the specialized methods and those that follow them may find fault in the
// construction and trigger failure at any time:
//
//
// Sizing of the topology -- this is a required specialization for MESH. This defines
// an inventory of all components and their relations that is used to allocate buffers
// to be efficiently populated in the subsequent topology assignment step.
//
if (! resizeComponentTopology(refiner, mesh)) return false;
if (! prepareComponentTopologySizing(refiner)) return false;
//
// Assignment of the topology -- this is a required specialization for MESH. If edges
// are specified, all other topological relations are expected to be defined for them.
// Otherwise edges and remaining topology will be completed from the face-vertices:
//
bool validate = options.validateFullTopology;
TopologyCallback callback = reinterpret_cast<TopologyCallback>(reportInvalidTopology);
void const * userData = &mesh;
if (! assignComponentTopology(refiner, mesh)) return false;
if (! prepareComponentTopologyAssignment(refiner, validate, callback, userData)) return false;
//
// User assigned and internal tagging of components -- an optional specialization for
// MESH. Allows the specification of sharpness values, holes, etc.
//
if (! assignComponentTags(refiner, mesh)) return false;
if (! prepareComponentTagsAndSharpness(refiner)) return false;
//
// Defining channels of face-varying primvar data -- an optional specialization for MESH.
//
if (! assignFaceVaryingTopology(refiner, mesh)) return false;
if (! prepareFaceVaryingChannels(refiner)) return false;
return true;
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseFaces(TopologyRefiner & newRefiner, int count) {
newRefiner._levels[0]->resizeFaces(count);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseEdges(TopologyRefiner & newRefiner, int count) {
newRefiner._levels[0]->resizeEdges(count);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseVertices(TopologyRefiner & newRefiner, int count) {
newRefiner._levels[0]->resizeVertices(count);
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::getNumBaseFaces(TopologyRefiner const & newRefiner) {
return newRefiner._levels[0]->getNumFaces();
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::getNumBaseEdges(TopologyRefiner const & newRefiner) {
return newRefiner._levels[0]->getNumEdges();
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::getNumBaseVertices(TopologyRefiner const & newRefiner) {
return newRefiner._levels[0]->getNumVertices();
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseFaceVertices(TopologyRefiner & newRefiner, Index f, int count) {
newRefiner._levels[0]->resizeFaceVertices(f, count);
newRefiner._hasIrregFaces |= (count != newRefiner._regFaceSize);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseEdgeFaces(TopologyRefiner & newRefiner, Index e, int count) {
newRefiner._levels[0]->resizeEdgeFaces(e, count);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseVertexFaces(TopologyRefiner & newRefiner, Index v, int count) {
newRefiner._levels[0]->resizeVertexFaces(v, count);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseVertexEdges(TopologyRefiner & newRefiner, Index v, int count) {
newRefiner._levels[0]->resizeVertexEdges(v, count);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseFaceVertices(TopologyRefiner & newRefiner, Index f) {
return newRefiner._levels[0]->getFaceVertices(f);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseFaceEdges(TopologyRefiner & newRefiner, Index f) {
return newRefiner._levels[0]->getFaceEdges(f);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseEdgeVertices(TopologyRefiner & newRefiner, Index e) {
return newRefiner._levels[0]->getEdgeVertices(e);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseEdgeFaces(TopologyRefiner & newRefiner, Index e) {
return newRefiner._levels[0]->getEdgeFaces(e);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseVertexFaces(TopologyRefiner & newRefiner, Index v) {
return newRefiner._levels[0]->getVertexFaces(v);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseVertexEdges(TopologyRefiner & newRefiner, Index v) {
return newRefiner._levels[0]->getVertexEdges(v);
}
template <class MESH>
inline LocalIndexArray
TopologyRefinerFactory<MESH>::getBaseEdgeFaceLocalIndices(TopologyRefiner & newRefiner, Index e) {
return newRefiner._levels[0]->getEdgeFaceLocalIndices(e);
}
template <class MESH>
inline LocalIndexArray
TopologyRefinerFactory<MESH>::getBaseVertexFaceLocalIndices(TopologyRefiner & newRefiner, Index v) {
return newRefiner._levels[0]->getVertexFaceLocalIndices(v);
}
template <class MESH>
inline LocalIndexArray
TopologyRefinerFactory<MESH>::getBaseVertexEdgeLocalIndices(TopologyRefiner & newRefiner, Index v) {
return newRefiner._levels[0]->getVertexEdgeLocalIndices(v);
}
template <class MESH>
inline Index
TopologyRefinerFactory<MESH>::findBaseEdge(TopologyRefiner const & newRefiner, Index v0, Index v1) {
return newRefiner._levels[0]->findEdge(v0, v1);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::populateBaseLocalIndices(TopologyRefiner & newRefiner) {
newRefiner._levels[0]->populateLocalIndices();
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseEdgeNonManifold(TopologyRefiner & newRefiner, Index e, bool b) {
newRefiner._levels[0]->setEdgeNonManifold(e, b);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseVertexNonManifold(TopologyRefiner & newRefiner, Index v, bool b) {
newRefiner._levels[0]->setVertexNonManifold(v, b);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseEdgeSharpness(TopologyRefiner & newRefiner, Index e, float s) {
newRefiner._levels[0]->getEdgeSharpness(e) = s;
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseVertexSharpness(TopologyRefiner & newRefiner, Index v, float s) {
newRefiner._levels[0]->getVertexSharpness(v) = s;
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseFaceHole(TopologyRefiner & newRefiner, Index f, bool b) {
newRefiner._levels[0]->setFaceHole(f, b);
newRefiner._hasHoles |= b;
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues) {
return newRefiner._levels[0]->createFVarChannel(numValues, newRefiner._subdivOptions);
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues, Sdc::Options const& fvarOptions) {
Sdc::Options newOptions = newRefiner._subdivOptions;
newOptions.SetFVarLinearInterpolation(fvarOptions.GetFVarLinearInterpolation());
return newRefiner._levels[0]->createFVarChannel(numValues, newOptions);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseFaceFVarValues(TopologyRefiner & newRefiner, Index face, int channel) {
return newRefiner._levels[0]->getFaceFVarValues(face, channel);
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::resizeComponentTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::resizeComponentTopology() -- no specialization provided.");
//
// Sizing the topology tables:
// This method is for determining the sizes of the various topology tables (and other
// data) associated with the mesh. Once completed, appropriate memory will be allocated
// and an additional method invoked to populate it accordingly.
//
// The following methods should be called -- first those to specify the number of faces,
// edges and vertices in the mesh:
//
// void setBaseFaceCount( TopologyRefiner& newRefiner, int count)
// void setBaseEdgeCount( TopologyRefiner& newRefiner, int count)
// void setBaseVertexCount(TopologyRefiner& newRefiner, int count)
//
// and then for each face, edge and vertex, the number of its incident components:
//
// void setBaseFaceVertexCount(TopologyRefiner& newRefiner, Index face, int count)
// void setBaseEdgeFaceCount( TopologyRefiner& newRefiner, Index edge, int count)
// void setBaseVertexFaceCount(TopologyRefiner& newRefiner, Index vertex, int count)
// void setBaseVertexEdgeCount(TopologyRefiner& newRefiner, Index vertex, int count)
//
// The count/size for a component type must be set before indices associated with that
// component type can be used.
//
// Note that it is only necessary to size 4 of the 6 supported topological relations --
// the number of edge-vertices is fixed at two per edge, and the number of face-edges is
// the same as the number of face-vertices.
//
// So a single pass through your mesh to gather up all of this sizing information will
// allow the Tables to be allocated appropriately once and avoid any dynamic resizing as
// it grows.
//
return false;
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::assignComponentTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::assignComponentTopology() -- no specialization provided.");
//
// Assigning the topology tables:
// Once the topology tables have been allocated, the six required topological
// relations can be directly populated using the following methods:
//
// IndexArray setBaseFaceVertices(TopologyRefiner& newRefiner, Index face)
// IndexArray setBaseFaceEdges(TopologyRefiner& newRefiner, Index face)
//
// IndexArray setBaseEdgeVertices(TopologyRefiner& newRefiner, Index edge)
// IndexArray setBaseEdgeFaces(TopologyRefiner& newRefiner, Index edge)
//
// IndexArray setBaseVertexEdges(TopologyRefiner& newRefiner, Index vertex)
// IndexArray setBaseVertexFaces(TopologyRefiner& newRefiner, Index vertex)
//
// For the last two relations -- the faces and edges incident a vertex -- there are
// also "local indices" that must be specified (considering doing this internally),
// where the "local index" of each incident face or edge is the index of the vertex
// within that face or edge, and so ranging from 0-3 for incident quads and 0-1 for
// incident edges. These are assigned through similarly retrieved arrays:
//
// LocalIndexArray setBaseVertexFaceLocalIndices(TopologyRefiner& newRefiner, Index vertex)
// LocalIndexArray setBaseVertexEdgeLocalIndices(TopologyRefiner& newRefiner, Index vertex)
// LocalIndexArray setBaseEdgeFaceLocalIndices( TopologyRefiner& newRefiner, Index edge)
//
// or, if the mesh is manifold, explicit assignment of these can be deferred and
// all can be determined by calling:
//
// void populateBaseLocalIndices(TopologyRefiner& newRefiner)
//
// All components are assumed to be locally manifold and ordering of components in
// the above relations is expected to be counter-clockwise.
//
// For non-manifold components, no ordering/orientation of incident components is
// assumed or required, but be sure to explicitly tag such components (vertices and
// edges) as non-manifold:
//
// void setBaseEdgeNonManifold(TopologyRefiner& newRefiner, Index edge, bool b);
//
// void setBaseVertexNonManifold(TopologyRefiner& newRefiner, Index vertex, bool b);
//
// Also consider using TopologyLevel::ValidateTopology() when debugging to ensure
// that topology has been completely and correctly specified.
//
return false;
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::assignFaceVaryingTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) {
//
// Optional assigning face-varying topology tables:
//
// Create independent face-varying primitive variable channels:
// int createBaseFVarChannel(TopologyRefiner& newRefiner, int numValues)
//
// For each channel, populate the face-vertex values:
// IndexArray setBaseFaceFVarValues(TopologyRefiner& newRefiner, Index face, int channel = 0)
//
return true;
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::assignComponentTags(TopologyRefiner& /* refiner */, MESH const& /* mesh */) {
//
// Optional tagging:
// This is where any additional feature tags -- sharpness, holes, etc. -- can be
// specified using:
//
// void setBaseEdgeSharpness(TopologyRefiner& newRefiner, Index edge, float sharpness)
// void setBaseVertexSharpness(TopologyRefiner& newRefiner, Index vertex, float sharpness)
//
// void setBaseFaceHole(TopologyRefiner& newRefiner, Index face, bool hole)
//
return true;
}
template <class MESH>
void
TopologyRefinerFactory<MESH>::reportInvalidTopology(
TopologyError /* errCode */, char const * /* msg */, MESH const& /* mesh */) {
//
// Optional topology validation error reporting:
// This method is called whenever the factory encounters topology validation
// errors. By default, nothing is reported
//
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H */

View File

@@ -0,0 +1,61 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_FAR_TYPES_H
#define OPENSUBDIV3_FAR_TYPES_H
#include "../version.h"
#include "../vtr/types.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Typedefs for indices that are inherited from the Vtr level -- eventually
// these primitive Vtr types may be declared at a lower, more public level.
//
typedef Vtr::Index Index;
typedef Vtr::LocalIndex LocalIndex;
typedef Vtr::IndexArray IndexArray;
typedef Vtr::LocalIndexArray LocalIndexArray;
typedef Vtr::ConstIndexArray ConstIndexArray;
typedef Vtr::ConstLocalIndexArray ConstLocalIndexArray;
inline bool IndexIsValid(Index index) { return Vtr::IndexIsValid(index); }
static const Index INDEX_INVALID = Vtr::INDEX_INVALID;
static const int VALENCE_LIMIT = Vtr::VALENCE_LIMIT;
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TYPES_H */

View File

@@ -0,0 +1,58 @@
#
# Copyright 2013 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
#-------------------------------------------------------------------------------
set(PUBLIC_HEADER_FILES
allocator.h
bilinear.h
catmark.h
cornerEdit.h
creaseEdit.h
faceEdit.h
face.h
fvarData.h
fvarEdit.h
halfedge.h
hierarchicalEdit.h
holeEdit.h
loop.h
mesh.h
subdivision.h
vertexEdit.h
vertex.h
)
#-------------------------------------------------------------------------------
install(
FILES
${PUBLIC_HEADER_FILES}
DESTINATION
"${CMAKE_INCDIR_BASE}/hbr"
PERMISSIONS
OWNER_READ
GROUP_READ
WORLD_READ )
#-------------------------------------------------------------------------------

View File

@@ -0,0 +1,172 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRALLOCATOR_H
#define OPENSUBDIV3_HBRALLOCATOR_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
typedef void (*HbrMemStatFunction)(size_t bytes);
/**
* HbrAllocator - derived from UtBlockAllocator.h, but embedded in
* libhbrep.
*/
template <typename T> class HbrAllocator {
public:
/// Constructor
HbrAllocator(size_t *memorystat, int blocksize, void (*increment)(size_t bytes), void (*decrement)(size_t bytes), size_t elemsize = sizeof(T));
/// Destructor
~HbrAllocator();
/// Create an allocated object
T * Allocate();
/// Return an allocated object to the block allocator
void Deallocate(T *);
/// Clear the allocator, deleting all allocated objects.
void Clear();
void SetMemStatsIncrement(void (*increment)(size_t bytes)) { m_increment = increment; }
void SetMemStatsDecrement(void (*decrement)(size_t bytes)) { m_decrement = decrement; }
private:
size_t *m_memorystat;
const int m_blocksize;
int m_elemsize;
T** m_blocks;
// Number of actually allocated blocks
int m_nblocks;
// Size of the m_blocks array (which is NOT the number of actually
// allocated blocks)
int m_blockCapacity;
int m_freecount;
T * m_freelist;
// Memory statistics tracking routines
HbrMemStatFunction m_increment;
HbrMemStatFunction m_decrement;
};
template <typename T>
HbrAllocator<T>::HbrAllocator(size_t *memorystat, int blocksize, void (*increment)(size_t bytes), void (*decrement)(size_t bytes), size_t elemsize)
: 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 <typename T>
HbrAllocator<T>::~HbrAllocator() {
Clear();
}
template <typename T>
void HbrAllocator<T>::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);
*m_memorystat -= m_blocksize * m_elemsize;
}
free(m_blocks);
m_blocks = 0;
m_nblocks = 0;
m_blockCapacity = 0;
m_freecount = 0;
m_freelist = NULL;
}
template <typename T>
T*
HbrAllocator<T>::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);
*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;
}
T* obj = m_freelist;
m_freelist = obj->GetNext();
obj->GetNext() = 0;
m_freecount--;
return obj;
}
template <typename T>
void
HbrAllocator<T>::Deallocate(T * obj) {
assert(!obj->GetNext());
obj->GetNext() = m_freelist;
m_freelist = obj;
m_freecount++;
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRALLOCATOR_H */

View File

@@ -0,0 +1,909 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRBILINEAR_H
#define OPENSUBDIV3_HBRBILINEAR_H
/*#define HBR_DEBUG */
#include "../hbr/subdivision.h"
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T>
class HbrBilinearSubdivision : public HbrSubdivision<T> {
public:
HbrBilinearSubdivision<T>()
: HbrSubdivision<T>() {}
virtual HbrSubdivision<T>* Clone() const {
return new HbrBilinearSubdivision<T>();
}
virtual void Refine(HbrMesh<T>* mesh, HbrFace<T>* face);
virtual HbrFace<T>* RefineFaceAtVertex(HbrMesh<T>* mesh, HbrFace<T>* face, HbrVertex<T>* vertex);
virtual void GuaranteeNeighbor(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
virtual void GuaranteeNeighbors(HbrMesh<T>* mesh, HbrVertex<T>* vertex);
virtual bool HasLimit(HbrMesh<T>* mesh, HbrFace<T>* face);
virtual bool HasLimit(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
virtual bool HasLimit(HbrMesh<T>* mesh, HbrVertex<T>* vertex);
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrFace<T>* face);
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrVertex<T>* vertex);
virtual bool VertexIsExtraordinary(HbrMesh<T> const * /* mesh */, HbrVertex<T>* vertex) { return vertex->GetValence() != 4; }
virtual bool FaceIsExtraordinary(HbrMesh<T> const * /* mesh */, HbrFace<T>* 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
void transferFVarToChild(HbrMesh<T>* mesh, HbrFace<T>* face, HbrFace<T>* child, int index);
// Transfers vertex and edge edits from a parent face to a child face
void transferEditsToChild(HbrFace<T>* face, HbrFace<T>* child, int index);
};
template <class T>
void
HbrBilinearSubdivision<T>::transferFVarToChild(HbrMesh<T>* mesh, HbrFace<T>* face, HbrFace<T>* child, int index) {
typename HbrMesh<T>::InterpolateBoundaryMethod fvarinterp = mesh->GetFVarInterpolateBoundaryMethod();
const int fvarcount = mesh->GetFVarCount();
int fvarindex = 0;
const int nv = face->GetNumVertices();
bool extraordinary = (nv != 4);
HbrVertex<T> *v = face->GetVertex(index), *childVertex;
HbrHalfedge<T>* edge;
// We do the face subdivision rule first, because we may reuse the
// result (stored in fv2) for the other subdivisions.
float weight = 1.0f / nv;
// For the face center vertex, the facevarying data can be cleared
// and averaged en masse, since the subdivision rules don't change
// for any of the data - we use the smooth rule for all of it.
// And since we know that the fvardata for this particular vertex
// is smooth and therefore shareable amongst all incident faces,
// we don't have to allocate extra storage for it. We also don't
// have to compute it if some other face got to it first (as
// indicated by the IsInitialized() flag).
HbrFVarData<T>& fv2 = child->GetFVarData(extraordinary ? 2 : (index+2)%4);
if (!fv2.IsInitialized()) {
const int totalfvarwidth = mesh->GetTotalFVarWidth();
fv2.ClearAll(totalfvarwidth);
for (int j = 0; j < nv; ++j) {
fv2.AddWithWeightAll(face->GetFVarData(j), totalfvarwidth, weight);
}
}
assert(fv2.IsInitialized());
v->GuaranteeNeighbors();
// Make sure that that each of the vertices of the child face have
// the appropriate facevarying storage as needed. If there are
// discontinuities in any facevarying datum, the vertex must
// 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<T>& fv0 = childVertex->GetFVarData(child);
edge = face->GetEdge(index);
GuaranteeNeighbor(mesh, edge);
assert(edge->GetOrgVertex() == v);
childVertex = child->GetVertex(extraordinary ? 1 : (index+1)%4);
fv1IsSmooth = !edge->IsFVarInfiniteSharpAnywhere();
if (!fv1IsSmooth) {
childVertex->NewFVarData(child);
}
HbrFVarData<T>& fv1 = childVertex->GetFVarData(child);
edge = edge->GetPrev();
GuaranteeNeighbor(mesh, edge);
assert(edge == face->GetEdge((index + nv - 1) % nv));
assert(edge->GetDestVertex() == v);
childVertex = child->GetVertex(extraordinary ? 3 : (index+3)%4);
fv3IsSmooth = !edge->IsFVarInfiniteSharpAnywhere();
if (!fv3IsSmooth) {
childVertex->NewFVarData(child);
}
HbrFVarData<T>& fv3 = childVertex->GetFVarData(child);
fvarindex = 0;
for (int fvaritem = 0; fvaritem < fvarcount; ++fvaritem) {
// Vertex subdivision rule. Analyze whether the vertex is on the
// boundary and whether it's an infinitely sharp corner. We
// determine the last by checking the propagate corners flag on
// the mesh; if it's off, we check the two edges of this face
// incident to that vertex and determining whether they are
// facevarying boundary edges - this is analogous to what goes on
// for the interpolateboundary tag (which when set to
// EDGEANDCORNER marks vertices with a valence of two as being
// sharp corners). If propagate corners is on, we check *all*
// faces to see if two edges side by side are facevarying boundary
// edges. The facevarying boundary check ignores geometric
// sharpness, otherwise we may swim at geometric creases which
// aren't actually discontinuous.
bool infcorner = false;
const int fvarwidth = mesh->GetFVarWidths()[fvaritem];
const unsigned char fvarmask = v->GetFVarMask(fvaritem);
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryEdgeAndCorner) {
if (fvarmask >= HbrVertex<T>::k_Corner) {
infcorner = true;
} else if (mesh->GetFVarPropagateCorners()) {
if (v->IsFVarCorner(fvaritem)) {
infcorner = true;
}
} else {
if (face->GetEdge(index)->GetFVarSharpness(fvaritem, true) && face->GetEdge(index)->GetPrev()->GetFVarSharpness(fvaritem, true)) {
infcorner = true;
}
}
}
// Infinitely sharp vertex rule. Applied if the vertex is:
// - undergoing no facevarying boundary interpolation;
// - at a geometric crease, in either boundary interpolation case; or
// - is an infinitely sharp facevarying vertex, in the EDGEANDCORNER case; or
// - has a mask equal or greater than one, in the "always
// sharp" interpolate boundary case
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
(fvarinterp == HbrMesh<T>::k_InterpolateBoundaryAlwaysSharp &&
fvarmask >= 1) ||
v->GetSharpness() > HbrVertex<T>::k_Smooth ||
infcorner) {
fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 1.0f);
}
// Dart rule: unlike geometric creases, because there's two
// discontinuous values for the one incident edge, we use the
// boundary rule and not the smooth rule
else if (fvarmask == 1) {
assert(!v->OnBoundary());
// Use 0.75 of the current vert
fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.75f);
// 0.125 of "two adjacent edge vertices", which in actuality
// are the facevarying values of the same vertex but on each
// side of the single incident facevarying sharp edge
HbrHalfedge<T>* start = v->GetIncidentEdge(), *nextedge;
edge = start;
while (edge) {
if (edge->GetFVarSharpness(fvaritem)) {
break;
}
nextedge = v->GetNextEdge(edge);
if (nextedge == start) {
assert(0); // we should have found it by now
break;
} else if (!nextedge) {
// should never get into this case - if the vertex is
// on a boundary, it can never be a facevarying dart
// vertex
assert(0);
edge = edge->GetPrev();
break;
} else {
edge = nextedge;
}
}
HbrVertex<T>* w = edge->GetDestVertex();
HbrFace<T>* bestface = edge->GetLeftFace();
int j;
for (j = 0; j < bestface->GetNumVertices(); ++j) {
if (bestface->GetVertex(j) == w) break;
}
assert(j != bestface->GetNumVertices());
fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
bestface = edge->GetRightFace();
for (j = 0; j < bestface->GetNumVertices(); ++j) {
if (bestface->GetVertex(j) == w) break;
}
assert(j != bestface->GetNumVertices());
fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
}
// Boundary vertex rule
else if (fvarmask != 0) {
// Use 0.75 of the current vert
fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.75f);
// Compute 0.125 of two adjacent edge vertices. However the
// two adjacent edge vertices we use must be part of the
// facevarying "boundary". To find the first edge we cycle
// counterclockwise around the current vertex v and look for
// the first boundary edge
HbrFace<T>* bestface = face;
HbrHalfedge<T>* bestedge = face->GetEdge(index)->GetPrev();
HbrHalfedge<T>* starte = bestedge->GetOpposite();
HbrVertex<T>* w = 0;
if (!starte) {
w = face->GetEdge(index)->GetPrev()->GetOrgVertex();
} else {
HbrHalfedge<T>* e = starte, *next;
assert(starte->GetOrgVertex() == v);
do {
if (e->GetFVarSharpness(fvaritem) || !e->GetLeftFace()) {
bestface = e->GetRightFace();
bestedge = e;
break;
}
next = v->GetNextEdge(e);
if (!next) {
bestface = e->GetLeftFace();
w = e->GetPrev()->GetOrgVertex();
break;
}
e = next;
} while (e && e != starte);
}
if (!w) w = bestedge->GetDestVertex();
int j;
for (j = 0; j < bestface->GetNumVertices(); ++j) {
if (bestface->GetVertex(j) == w) break;
}
assert(j != bestface->GetNumVertices());
fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
// Look for the other edge by cycling clockwise around v
bestface = face;
bestedge = face->GetEdge(index);
starte = bestedge;
w = 0;
if (HbrHalfedge<T>* e = starte) {
assert(starte->GetOrgVertex() == v);
do {
if (e->GetFVarSharpness(fvaritem) || !e->GetRightFace()) {
bestface = e->GetLeftFace();
bestedge = e;
break;
}
assert(e->GetOpposite());
e = v->GetPreviousEdge(e);
} while (e && e != starte);
}
if (!w) w = bestedge->GetDestVertex();
for (j = 0; j < bestface->GetNumVertices(); ++j) {
if (bestface->GetVertex(j) == w) break;
}
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
// computed the facevarying values
else if (!fv0IsSmooth || !fv0.IsInitialized()) {
int valence = v->GetValence();
float invvalencesquared = 1.0f / (valence * valence);
// Use n-2/n of the current vertex value
fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, invvalencesquared * valence * (valence - 2));
// Add 1/n^2 of surrounding edge vertices and surrounding face
// averages. We loop over all surrounding faces..
HbrHalfedge<T>* start = v->GetIncidentEdge(), *edge;
edge = start;
while (edge) {
HbrFace<T>* g = edge->GetLeftFace();
weight = invvalencesquared / g->GetNumVertices();
// .. and compute the average of each face. At the same
// time, we look for the edge on that face whose origin is
// the same as v, and add a contribution from its
// destination vertex value; this takes care of the
// surrounding edge vertex addition.
for (int j = 0; j < g->GetNumVertices(); ++j) {
fv0.AddWithWeight(g->GetFVarData(j), fvarindex, fvarwidth, weight);
if (g->GetEdge(j)->GetOrgVertex() == v) {
fv0.AddWithWeight(g->GetFVarData((j + 1) % g->GetNumVertices()), fvarindex, fvarwidth, invvalencesquared);
}
}
edge = v->GetNextEdge(edge);
if (edge == start) break;
}
}
// Edge subdivision rule
edge = face->GetEdge(index);
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {
// Sharp edge rule
fv1.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.5f);
fv1.AddWithWeight(face->GetFVarData((index + 1) % nv), fvarindex, fvarwidth, 0.5f);
} else if (!fv1IsSmooth || !fv1.IsInitialized()) {
// Smooth edge subdivision. Add 0.25 of adjacent vertices
fv1.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.25f);
fv1.AddWithWeight(face->GetFVarData((index + 1) % nv), fvarindex, fvarwidth, 0.25f);
// Local subdivided face vertex
fv1.AddWithWeight(fv2, fvarindex, fvarwidth, 0.25f);
// Add 0.25 * average of neighboring face vertices
HbrFace<T>* oppFace = edge->GetRightFace();
weight = 0.25f / oppFace->GetNumVertices();
for (int j = 0; j < oppFace->GetNumVertices(); ++j) {
fv1.AddWithWeight(oppFace->GetFVarData(j), fvarindex, fvarwidth, weight);
}
}
// Edge subdivision rule
edge = edge->GetPrev();
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {
// 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
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
fv3.AddWithWeight(fv2, fvarindex, fvarwidth, 0.25f);
// Add 0.25 * average of neighboring face vertices
HbrFace<T>* oppFace = edge->GetRightFace();
weight = 0.25f / oppFace->GetNumVertices();
for (int j = 0; j < oppFace->GetNumVertices(); ++j) {
fv3.AddWithWeight(oppFace->GetFVarData(j), fvarindex, fvarwidth, weight);
}
}
fvarindex += fvarwidth;
}
fv0.SetInitialized();
fv1.SetInitialized();
fv3.SetInitialized();
}
template <class T>
void
HbrBilinearSubdivision<T>::transferEditsToChild(HbrFace<T>* face, HbrFace<T>* child, int index) {
// Hand down hole tag
child->SetHole(face->IsHole());
// Hand down pointers to hierarchical edits
if (HbrHierarchicalEdit<T>** edits = face->GetHierarchicalEdits()) {
while (HbrHierarchicalEdit<T>* edit = *edits) {
if (!edit->IsRelevantToFace(face)) break;
if (edit->GetNSubfaces() > face->GetDepth() &&
(edit->GetSubface(face->GetDepth()) == index)) {
child->SetHierarchicalEdits(edits);
break;
}
edits++;
}
}
}
template <class T>
void
HbrBilinearSubdivision<T>::Refine(HbrMesh<T>* mesh, HbrFace<T>* face) {
// Create new quadrilateral children faces from this face
HbrFace<T>* child;
HbrVertex<T>* vertices[4];
HbrHalfedge<T>* edge = face->GetFirstEdge();
HbrHalfedge<T>* prevedge = edge->GetPrev();
HbrHalfedge<T>* childedge;
int nv = face->GetNumVertices();
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 (!face->GetChild(i)) {
#ifdef HBR_DEBUG
std::cerr << "Kid " << i << "\n";
#endif
HbrVertex<T>* 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";
#endif
// Hand down edge sharpnesses
childedge = vertex->Subdivide()->GetEdge(edge->Subdivide());
assert(childedge);
if ((sharpness = edge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
HbrSubdivision<T>::SubdivideCreaseWeight(edge, edge->GetOrgVertex(), childedge);
}
childedge->CopyFVarInfiniteSharpness(edge);
childedge = prevedge->Subdivide()->GetEdge(vertex->Subdivide());
assert(childedge);
if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
HbrSubdivision<T>::SubdivideCreaseWeight(prevedge, prevedge->GetDestVertex(), childedge);
}
childedge->CopyFVarInfiniteSharpness(prevedge);
if (mesh->GetTotalFVarWidth()) {
transferFVarToChild(mesh, face, child, i);
}
// 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();
}
}
template <class T>
HbrFace<T>*
HbrBilinearSubdivision<T>::RefineFaceAtVertex(HbrMesh<T>* mesh, HbrFace<T>* face, HbrVertex<T>* vertex) {
#ifdef HBR_DEBUG
std::cerr << " forcing refine on " << *face << " at " << *vertex << '\n';
#endif
// Create new quadrilateral children faces from this face
HbrHalfedge<T>* edge = face->GetFirstEdge();
HbrHalfedge<T>* prevedge = edge->GetPrev();
HbrHalfedge<T>* childedge;
int nv = face->GetNumVertices();
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<T>* child;
HbrVertex<T>* 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';
#endif
child = mesh->NewFace(4, vertices, face, i);
#ifdef HBR_DEBUG
std::cerr << "Creating face " << *child << " during refine\n";
#endif
// Hand down edge sharpness
childedge = vertex->Subdivide()->GetEdge(edge->Subdivide());
assert(childedge);
if ((sharpness = edge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
HbrSubdivision<T>::SubdivideCreaseWeight(edge, edge->GetOrgVertex(), childedge);
}
childedge->CopyFVarInfiniteSharpness(edge);
childedge = prevedge->Subdivide()->GetEdge(vertex->Subdivide());
assert(childedge);
if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
HbrSubdivision<T>::SubdivideCreaseWeight(prevedge, prevedge->GetDestVertex(), childedge);
}
childedge->CopyFVarInfiniteSharpness(prevedge);
if (mesh->GetTotalFVarWidth()) {
transferFVarToChild(mesh, face, child, i);
}
// 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;
}
template <class T>
void
HbrBilinearSubdivision<T>::GuaranteeNeighbor(HbrMesh<T>* mesh, HbrHalfedge<T>* edge) {
if (edge->GetOpposite()) {
return;
}
// For the given edge: if the parent of either of its incident
// vertices is itself a _face_, then ensuring that this parent
// face has refined at a particular vertex is sufficient to
// ensure that both of the faces on each side of the edge have
// been created.
bool destParentWasEdge = true;
HbrFace<T>* parentFace = edge->GetOrgVertex()->GetParentFace();
HbrHalfedge<T>* parentEdge = edge->GetDestVertex()->GetParentEdge();
if (!parentFace) {
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);
#ifdef HBR_DEBUG
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());
}
// It should always be the case that the opposite now exists -
// we can't have a boundary case here
assert(edge->GetOpposite());
} else {
HbrVertex<T>* parentVertex = edge->GetOrgVertex()->GetParentVertex();
parentEdge = edge->GetDestVertex()->GetParentEdge();
if (!parentVertex) {
parentVertex = edge->GetDestVertex()->GetParentVertex();
parentEdge = edge->GetOrgVertex()->GetParentEdge();
}
if (parentVertex) {
assert(parentEdge);
#ifdef HBR_DEBUG
std::cerr << "\nparent edge is " << *parentEdge << "\n";
#endif
// 1. Go up to the parent of my face
parentFace = edge->GetFace()->GetParent();
#ifdef HBR_DEBUG
std::cerr << "\nparent face is " << *parentFace << "\n";
#endif
// 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);
// 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);
// FIXME: assertion?
assert(edge->GetOpposite());
}
}
}
}
}
template <class T>
void
HbrBilinearSubdivision<T>::GuaranteeNeighbors(HbrMesh<T>* mesh, HbrVertex<T>* vertex) {
#ifdef HBR_DEBUG
std::cerr << "\n\nneighbor guarantee at " << *vertex << " invoked\n";
#endif
// If the vertex is a child of a face, guaranteeing the neighbors
// of the vertex is simply a matter of ensuring the parent face
// has refined.
HbrFace<T>* parentFace = vertex->GetParentFace();
if (parentFace) {
#ifdef HBR_DEBUG
std::cerr << " forcing full refine on parent face\n";
#endif
Refine(mesh, parentFace);
return;
}
// Otherwise if the vertex is a child of an edge, we need to
// ensure that the parent faces on either side of the parent edge
// 1) exist, and 2) have refined at both vertices of the parent
// edge
HbrHalfedge<T>* parentEdge = vertex->GetParentEdge();
if (parentEdge) {
#ifdef HBR_DEBUG
std::cerr << " forcing full refine on adjacent faces of parent edge\n";
#endif
HbrVertex<T>* dest = parentEdge->GetDestVertex();
HbrVertex<T>* 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";
#endif
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";
#endif
return;
}
// The last case: the vertex is a child of a vertex. In this case
// we have to first recursively guarantee that the parent's
// adjacent faces also exist.
HbrVertex<T>* parentVertex = vertex->GetParentVertex();
if (parentVertex) {
#ifdef HBR_DEBUG
std::cerr << " recursive parent vertex guarantee call\n";
#endif
parentVertex->GuaranteeNeighbors();
// And then we refine all the face neighbors of the
// parentVertex
HbrHalfedge<T>* start = parentVertex->GetIncidentEdge(), *edge;
edge = start;
while (edge) {
HbrFace<T>* f = edge->GetLeftFace();
RefineFaceAtVertex(mesh, f, parentVertex);
edge = parentVertex->GetNextEdge(edge);
if (edge == start) break;
}
}
}
template <class T>
bool
HbrBilinearSubdivision<T>::HasLimit(HbrMesh<T>* mesh, HbrFace<T>* 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;
}
}
return true;
}
template <class T>
bool
HbrBilinearSubdivision<T>::HasLimit(HbrMesh<T>* /* mesh */, HbrHalfedge<T>* /* edge */) {
return true;
}
template <class T>
bool
HbrBilinearSubdivision<T>::HasLimit(HbrMesh<T>* /* mesh */, HbrVertex<T>* vertex) {
vertex->GuaranteeNeighbors();
switch (vertex->GetMask(false)) {
case HbrVertex<T>::k_Smooth:
case HbrVertex<T>::k_Dart:
return !vertex->OnBoundary();
break;
case HbrVertex<T>::k_Crease:
case HbrVertex<T>::k_Corner:
default:
return true;
}
}
template <class T>
HbrVertex<T>*
HbrBilinearSubdivision<T>::Subdivide(HbrMesh<T>* mesh, HbrFace<T>* face) {
// Face rule: simply average all vertices on the face
HbrVertex<T>* v = mesh->NewVertex();
T& data = v->GetData();
int nv = face->GetNumVertices();
float weight = 1.0f / nv;
HbrHalfedge<T>* edge = face->GetFirstEdge();
for (int i = 0; i < face->GetNumVertices(); ++i) {
HbrVertex<T>* 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";
#endif
// 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
return v;
}
template <class T>
HbrVertex<T>*
HbrBilinearSubdivision<T>::Subdivide(HbrMesh<T>* mesh, HbrHalfedge<T>* edge) {
#ifdef HBR_DEBUG
float esharp = edge->GetSharpness();
std::cerr << "Subdividing at " << *edge << " (sharpness = " << esharp << ")";
#endif
HbrVertex<T>* v = mesh->NewVertex();
T& data = v->GetData();
// If there's the possibility of a crease edits, make sure the
// edit has been applied
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();
}
// 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
data.AddVaryingWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
data.AddVaryingWithWeight(edge->GetDestVertex()->GetData(), 0.5f);
#ifdef HBR_DEBUG
std::cerr << " created " << *v << "\n";
#endif
return v;
}
template <class T>
HbrVertex<T>*
HbrBilinearSubdivision<T>::Subdivide(HbrMesh<T>* mesh, HbrVertex<T>* vertex) {
HbrVertex<T>* 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
// with the parent
if (mesh->HasVertexEdits()) {
vertex->GuaranteeNeighbors();
v = mesh->NewVertex();
T& data = v->GetData();
// Just copy the old value
data.AddWithWeight(vertex->GetData(), 1.0f);
// 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 << " created " << *v << "\n";
#endif
// Inherit extraordinary flag and sharpness
if (vertex->IsExtraordinary()) v->SetExtraordinary();
float sharp = vertex->GetSharpness();
if (sharp >= HbrVertex<T>::k_InfinitelySharp) {
v->SetSharpness(HbrVertex<T>::k_InfinitelySharp);
} else if (sharp > HbrVertex<T>::k_Smooth) {
sharp -= 1.0f;
if (sharp < (float) HbrVertex<T>::k_Smooth) {
sharp = (float) HbrVertex<T>::k_Smooth;
}
v->SetSharpness(sharp);
} else {
v->SetSharpness(HbrVertex<T>::k_Smooth);
}
return v;
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRBILINEAR_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRCORNEREDIT_H
#define OPENSUBDIV3_HBRCORNEREDIT_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrCornerEdit;
template <class T>
std::ostream& operator<<(std::ostream& out, const HbrCornerEdit<T>& path) {
out << "vertex path = (" << path.faceid << ' ';
for (int i = 0; i < path.nsubfaces; ++i) {
out << static_cast<int>(path.subfaces[i]) << ' ';
}
return out << static_cast<int>(path.vertexid) << "), sharpness = " << path.sharpness;
}
template <class T>
class HbrCornerEdit : public HbrHierarchicalEdit<T> {
public:
HbrCornerEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _vertexid, typename HbrHierarchicalEdit<T>::Operation _op, float _sharpness)
: HbrHierarchicalEdit<T>(_faceid, _nsubfaces, _subfaces), vertexid(_vertexid), op(_op), sharpness(_sharpness) {
}
HbrCornerEdit(int _faceid, int _nsubfaces, int *_subfaces, int _vertexid, typename HbrHierarchicalEdit<T>::Operation _op, float _sharpness)
: HbrHierarchicalEdit<T>(_faceid, _nsubfaces, _subfaces), vertexid(static_cast<unsigned char>(_vertexid)), op(_op), sharpness(_sharpness) {
}
virtual ~HbrCornerEdit() {}
friend std::ostream& operator<< <T> (std::ostream& out, const HbrCornerEdit<T>& path);
virtual void ApplyEditToFace(HbrFace<T>* face) {
if (HbrHierarchicalEdit<T>::GetNSubfaces() == face->GetDepth()) {
// Modify vertex sharpness. Note that we could actually do
// this in ApplyEditToVertex as well!
float sharp = 0.0f;
if (op == HbrHierarchicalEdit<T>::Set) {
sharp = sharpness;
} else if (op == HbrHierarchicalEdit<T>::Add) {
sharp = face->GetVertex(vertexid)->GetSharpness() + sharpness;
} else if (op == HbrHierarchicalEdit<T>::Subtract) {
sharp = face->GetVertex(vertexid)->GetSharpness() - sharpness;
}
if (sharp < HbrVertex<T>::k_Smooth) {
sharp = HbrVertex<T>::k_Smooth;
}
if (sharp > HbrVertex<T>::k_InfinitelySharp) {
sharp = HbrVertex<T>::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)
const unsigned char vertexid;
typename HbrHierarchicalEdit<T>::Operation op;
// sharpness of the vertex edit
const float sharpness;
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRCORNEREDIT_H */

View File

@@ -0,0 +1,100 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRCREASEEDIT_H
#define OPENSUBDIV3_HBRCREASEEDIT_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrCreaseEdit;
template <class T>
std::ostream& operator<<(std::ostream& out, const HbrCreaseEdit<T>& path) {
out << "edge path = (" << path.faceid << ' ';
for (int i = 0; i < path.nsubfaces; ++i) {
out << static_cast<int>(path.subfaces[i]) << ' ';
}
return out << static_cast<int>(path.edgeid) << "), sharpness = " << path.sharpness;
}
template <class T>
class HbrCreaseEdit : public HbrHierarchicalEdit<T> {
public:
HbrCreaseEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _edgeid, typename HbrHierarchicalEdit<T>::Operation _op, float _sharpness)
: HbrHierarchicalEdit<T>(_faceid, _nsubfaces, _subfaces), edgeid(_edgeid), op(_op), sharpness(_sharpness) {
}
HbrCreaseEdit(int _faceid, int _nsubfaces, int *_subfaces, int _edgeid, typename HbrHierarchicalEdit<T>::Operation _op, float _sharpness)
: HbrHierarchicalEdit<T>(_faceid, _nsubfaces, _subfaces), edgeid(static_cast<unsigned char>(_edgeid)), op(_op), sharpness(_sharpness) {
}
virtual ~HbrCreaseEdit() {}
friend std::ostream& operator<< <T> (std::ostream& out, const HbrCreaseEdit<T>& path);
virtual void ApplyEditToFace(HbrFace<T>* face) {
if (HbrHierarchicalEdit<T>::GetNSubfaces() == face->GetDepth()) {
// Modify edge sharpness
float sharp=0.0f;
if (op == HbrHierarchicalEdit<T>::Set) {
sharp = sharpness;
} else if (op == HbrHierarchicalEdit<T>::Add) {
sharp = face->GetEdge(edgeid)->GetSharpness() + sharpness;
} else if (op == HbrHierarchicalEdit<T>::Subtract) {
sharp = face->GetEdge(edgeid)->GetSharpness() - sharpness;
}
if (sharp < HbrHalfedge<T>::k_Smooth)
sharp = HbrHalfedge<T>::k_Smooth;
if (sharp > HbrHalfedge<T>::k_InfinitelySharp)
sharp = HbrHalfedge<T>::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)
const unsigned char edgeid;
typename HbrHierarchicalEdit<T>::Operation op;
// sharpness of the edge edit
const float sharpness;
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRCREASEEDIT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRFACEEDIT_H
#define OPENSUBDIV3_HBRFACEEDIT_H
#include "../hbr/hierarchicalEdit.h"
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrFaceEdit;
template <class T>
std::ostream& operator<<(std::ostream& out, const HbrFaceEdit<T>& path) {
out << "face path = (" << path.faceid << ' ';
for (int i = 0; i < path.nsubfaces; ++i) {
out << static_cast<int>(path.subfaces[i]) << ' ';
}
return out << ")";
}
template <class T>
class HbrFaceEdit : public HbrHierarchicalEdit<T> {
public:
HbrFaceEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, int _index, int _width, typename HbrHierarchicalEdit<T>::Operation _op, float *_edit)
: HbrHierarchicalEdit<T>(_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<T>::Operation _op, float *_edit)
: HbrHierarchicalEdit<T>(_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<T>::Operation _op, RtToken _edit)
: HbrHierarchicalEdit<T>(_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<T>::Operation _op, RtToken _edit)
: HbrHierarchicalEdit<T>(_faceid, _nsubfaces, _subfaces), index(_index), width(_width), op(_op) {
edit = new float[width];
RtString* sedit = (RtString*) edit;
*sedit = _edit;
}
#endif
virtual ~HbrFaceEdit() {
delete[] edit;
}
friend std::ostream& operator<< <T> (std::ostream& out, const HbrFaceEdit<T>& path);
// Return index of variable this edit applies to
int GetIndex() const { return index; }
// 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<T>::Operation GetOperation() const { return op; }
virtual void ApplyEditToFace(HbrFace<T>* face) {
if (HbrHierarchicalEdit<T>::GetNSubfaces() == face->GetDepth()) {
int oldUniformIndex = face->GetUniformIndex();
// Any face below level 0 needs a new uniform index
if (face->GetDepth() > 0) {
face->SetUniformIndex(face->GetMesh()->NewUniformIndex());
}
// Apply edit
face->GetVertex(0)->GetData().ApplyFaceEdit(oldUniformIndex, face->GetUniformIndex(), *const_cast<const HbrFaceEdit<T>*>(this));
}
}
private:
int index;
int width;
typename HbrHierarchicalEdit<T>::Operation op;
float* edit;
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRFACEEDIT_H */

View File

@@ -0,0 +1,200 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRFVARDATA_H
#define OPENSUBDIV3_HBRFVARDATA_H
#include <cstring>
#include <cmath>
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrFVarEdit;
template <class T> class HbrFace;
template <class T> class HbrVertex;
// This class implements a "face varying vector item". Really it's
// just a smart wrapper around face varying data (itself just a bunch
// of floats) stored on each vertex.
template <class T> class HbrFVarData {
private:
HbrFVarData()
: faceid(0), initialized(0) {
}
~HbrFVarData() {
Uninitialize();
}
HbrFVarData(const HbrFVarData &/* data */) {}
public:
// Sets the face id
void SetFaceID(int id) {
faceid = id;
}
// Returns the id of the face to which this data is bound
int GetFaceID() const {
return faceid;
}
// Clears the initialized flag
void Uninitialize() {
initialized = 0;
faceid = 0;
}
// Returns initialized flag
bool IsInitialized() const {
return initialized;
}
// Sets initialized flag
void SetInitialized() {
initialized = 1;
}
// Return the data from the NgpFVVector
float* GetData(int item) { return data + item; }
// Clears the indicates value of this item
void Clear(int startindex, int width) {
memset(data + startindex, 0, width * sizeof(float));
}
// Clears all values of this item
void ClearAll(int width) {
initialized = 1;
memset(data, 0, width * sizeof(float));
}
// Set values of the indicated item (with the indicated weighing)
// on this item
void SetWithWeight(const HbrFVarData& fvvi, int startindex, int width, float weight) {
float *dst = data + startindex;
const float *src = fvvi.data + startindex;
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;
const float *src = fvvi.data + startindex;
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;
const float *src = fvvi.data;
for (int i = 0; i < width; ++i) {
*dst++ += weight * *src++;
}
}
// Compare all values item against a float buffer. Returns true
// if all values match
bool CompareAll(int width, const float *values, float tolerance=0.0f) const {
if (!initialized) return false;
for (int i = 0; i < width; ++i) {
if (fabsf(values[i] - data[i]) > tolerance) return false;
}
return true;
}
// Initializes data
void SetAllData(int width, const float *values) {
initialized = 1;
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 {
for (int i = 0; i < width; ++i) {
if (fabsf(data[startindex + i] - fvvi.data[startindex + i]) > tolerance) return false;
}
return true;
}
// Modify the data of the item with an edit
void ApplyFVarEdit(const HbrFVarEdit<T>& edit);
friend class HbrVertex<T>;
private:
unsigned int faceid:31;
unsigned int initialized:1;
float data[1];
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#include "../hbr/fvarEdit.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T>
void
HbrFVarData<T>::ApplyFVarEdit(const HbrFVarEdit<T>& 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<T>::Set:
*dst++ = *src++;
break;
case HbrVertexEdit<T>::Add:
*dst++ += *src++;
break;
case HbrVertexEdit<T>::Subtract:
*dst++ -= *src++;
}
}
initialized = 1;
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRFVARDATA_H */

View File

@@ -0,0 +1,121 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRFVAREDIT_H
#define OPENSUBDIV3_HBRFVAREDIT_H
#include "../hbr/hierarchicalEdit.h"
#include "../hbr/vertexEdit.h"
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrFVarEdit;
template <class T>
std::ostream& operator<<(std::ostream& out, const HbrFVarEdit<T>& path) {
out << "vertex path = (" << path.faceid << ' ';
for (int i = 0; i < path.nsubfaces; ++i) {
out << static_cast<int>(path.subfaces[i]) << ' ';
}
return out << static_cast<int>(path.vertexid) << "), edit = (" << path.edit[0] << ',' << path.edit[1] << ',' << path.edit[2] << ')';
}
template <class T>
class HbrFVarEdit : public HbrHierarchicalEdit<T> {
public:
HbrFVarEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _vertexid, int _index, int _width, int _offset, typename HbrHierarchicalEdit<T>::Operation _op, float *_edit)
: HbrHierarchicalEdit<T>(_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<T>::Operation _op, float *_edit)
: HbrHierarchicalEdit<T>(_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;
}
// Return the vertex id (the last element in the path)
unsigned char GetVertexID() const { return vertexid; }
friend std::ostream& operator<< <T> (std::ostream& out, const HbrFVarEdit<T>& path);
// Return index into the facevarying data
int GetIndex() const { return index; }
// Return width of the data
int GetWidth() const { return width; }
// 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<T>::Operation GetOperation() const { return op; }
virtual void ApplyEditToFace(HbrFace<T>* face) {
if (HbrHierarchicalEdit<T>::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
// one that already exists) and modify that
HbrFVarData<T> &fvt = face->GetVertex(vertexid)->GetFVarData(face);
if (fvt.GetFaceID() != face->GetID()) {
// This is the generic fvt, allocate a new copy and edit it
HbrFVarData<T> &newfvt = face->GetVertex(vertexid)->NewFVarData(face);
newfvt.SetAllData(face->GetMesh()->GetTotalFVarWidth(), fvt.GetData(0));
newfvt.ApplyFVarEdit(*const_cast<const HbrFVarEdit<T>*>(this));
} else {
fvt.ApplyFVarEdit(*const_cast<const HbrFVarEdit<T>*>(this));
}
}
}
private:
const unsigned char vertexid;
const int index;
const int width;
const int offset;
float* edit;
typename HbrHierarchicalEdit<T>::Operation op;
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRFVAREDIT_H */

View File

@@ -0,0 +1,740 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRHALFEDGE_H
#define OPENSUBDIV3_HBRHALFEDGE_H
#include <assert.h>
#include <stddef.h>
#include <cstring>
#include <iostream>
#ifdef HBRSTITCH
#include "libgprims/stitch.h"
#include "libgprims/stitchInternal.h"
#endif
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrFace;
template <class T> class HbrHalfedge;
template <class T> class HbrVertex;
template <class T> class HbrMesh;
template <class T> std::ostream& operator<<(std::ostream& out, const HbrHalfedge<T>& edge);
template <class T> class HbrHalfedge {
private:
HbrHalfedge(): opposite(0), incidentVertex(-1), vchild(-1), sharpness(0.0f)
#ifdef HBRSTITCH
, stitchccw(1), raystitchccw(1)
#endif
, coarse(1)
{
}
HbrHalfedge(const HbrHalfedge &/* edge */) {}
~HbrHalfedge();
void Clear();
// Finish the initialization of the halfedge. Should only be
// called by HbrFace
void Initialize(HbrHalfedge<T>* opposite, int index, HbrVertex<T>* origin, unsigned int *fvarbits, HbrFace<T>* face);
public:
// Returns the opposite half edge
HbrHalfedge<T>* GetOpposite() const { return opposite; }
// Sets the opposite half edge
void SetOpposite(HbrHalfedge<T>* opposite) { this->opposite = opposite; sharpness = opposite->sharpness; }
// Returns the next clockwise halfedge around the incident face
HbrHalfedge<T>* GetNext() const {
if (m_index == 4) {
const size_t edgesize = sizeof(HbrHalfedge<T>) + sizeof(HbrFace<T>*);
if (lastedge) {
return (HbrHalfedge<T>*) ((char*) this - (GetFace()->GetNumVertices() - 1) * edgesize);
} else {
return (HbrHalfedge<T>*) ((char*) this + edgesize);
}
} else {
if (lastedge) {
return (HbrHalfedge<T>*) ((char*) this - (m_index) * sizeof(HbrHalfedge<T>));
} else {
return (HbrHalfedge<T>*) ((char*) this + sizeof(HbrHalfedge<T>));
}
}
}
// Returns the previous counterclockwise halfedge around the incident face
HbrHalfedge<T>* GetPrev() const {
const size_t edgesize = (m_index == 4) ?
(sizeof(HbrHalfedge<T>) + sizeof(HbrFace<T>*)) :
sizeof(HbrHalfedge<T>);
if (firstedge) {
return (HbrHalfedge<T>*) ((char*) this + (GetFace()->GetNumVertices() - 1) * edgesize);
} else {
return (HbrHalfedge<T>*) ((char*) this - edgesize);
}
}
// Returns the incident vertex
HbrVertex<T>* GetVertex() const {
return GetMesh()->GetVertex(incidentVertex);
}
// Returns the incident vertex
HbrVertex<T>* GetVertex(HbrMesh<T> *mesh) const {
return mesh->GetVertex(incidentVertex);
}
// Returns the incident vertex
int GetVertexID() const {
return incidentVertex;
}
// Returns the source vertex
HbrVertex<T>* GetOrgVertex() const {
return GetVertex();
}
// Returns the source vertex
HbrVertex<T>* GetOrgVertex(HbrMesh<T> *mesh) const {
return GetVertex(mesh);
}
// Returns the source vertex id
int GetOrgVertexID() const {
return incidentVertex;
}
// Changes the origin vertex. Generally not a good idea to do
void SetOrgVertex(HbrVertex<T>* v) { incidentVertex = v->GetID(); }
// Returns the destination vertex
HbrVertex<T>* GetDestVertex() const { return GetNext()->GetOrgVertex(); }
// Returns the destination vertex
HbrVertex<T>* GetDestVertex(HbrMesh<T> *mesh) const { return GetNext()->GetOrgVertex(mesh); }
// Returns the destination vertex ID
int GetDestVertexID() const { return GetNext()->GetOrgVertexID(); }
// Returns the incident facet
HbrFace<T>* GetFace() const {
if (m_index == 4) {
// Pointer to face is stored after the data for the edge
return *(HbrFace<T>**)((char *) this + sizeof(HbrHalfedge<T>));
} else {
return (HbrFace<T>*) ((char*) this - (m_index) * sizeof(HbrHalfedge<T>) -
offsetof(HbrFace<T>, edges));
}
}
// Returns the mesh to which this edge belongs
HbrMesh<T>* GetMesh() const { return GetFace()->GetMesh(); }
// Returns the face on the right
HbrFace<T>* GetRightFace() const { return opposite ? opposite->GetLeftFace() : NULL; }
// Return the face on the left of the halfedge
HbrFace<T>* GetLeftFace() const { return GetFace(); }
// Returns whether this is a boundary edge
bool IsBoundary() const { return opposite == 0; }
// Tag the edge as being an infinitely sharp facevarying edge
void SetFVarInfiniteSharp(int datum, bool infsharp) {
int intindex = datum >> 4;
unsigned int bits = infsharp << ((datum & 15) * 2);
getFVarInfSharp()[intindex] |= bits;
if (opposite) {
opposite->getFVarInfSharp()[intindex] |= bits;
}
}
// Copy fvar infinite sharpness flags from another edge
void CopyFVarInfiniteSharpness(HbrHalfedge<T>* edge) {
unsigned int *fvarinfsharp = getFVarInfSharp();
if (fvarinfsharp) {
const int fvarcount = GetMesh()->GetFVarCount();
int fvarbitsSizePerEdge = ((fvarcount + 15) / 16);
if (edge->IsSharp(true)) {
memset(fvarinfsharp, 0x55555555, fvarbitsSizePerEdge * sizeof(unsigned int));
} else {
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);
// Returns whether the edge is infinitely sharp in any facevarying
// datum
bool IsFVarInfiniteSharpAnywhere();
// 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; }
// Sets the sharpness of the edge
void SetSharpness(float sharp) { sharpness = sharp; if (opposite) opposite->sharpness = sharp; ClearMask(); }
// Returns whether the edge 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)); }
// Clears the masks of the adjacent edge vertices. Usually called
// when a change in edge sharpness occurs.
void ClearMask() { GetOrgVertex()->ClearMask(); GetDestVertex()->ClearMask(); }
// Subdivide the edge into a vertex if needed and return
HbrVertex<T>* Subdivide();
// Make sure the edge has its opposite face
void GuaranteeNeighbor();
// True if the edge has a subdivided child vertex
bool HasChild() const { return vchild!=-1; }
// Remove the reference to subdivided vertex
void RemoveChild() { vchild = -1; }
// Sharpness constants
enum Mask {
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 stitch edge exists, and this edge has no opposite, destroy
// it
void DestroyStitchEdges(int stitchcount) {
if (!opposite) {
StitchEdge **stitchEdge = getStitchEdges();
for (int i = 0; i < stitchcount; ++i) {
if (stitchEdge[i]) {
StitchFreeEdge(stitchEdge[i]);
stitchEdge[i] = 0;
}
}
}
}
StitchEdge* GetRayStitchEdge(int i) {
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<T>* ea = GetOrgVertex()->Subdivide()->GetEdge(Subdivide());
HbrHalfedge<T>* 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;
}
}
}
void SplitRayStitchEdge(int i) {
SplitStitchEdge(i + 2);
}
void SetStitchEdge(int i, StitchEdge* edge) {
StitchEdge **stitchEdges = getStitchEdges();
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;
}
}
void* GetStitchData() const {
if (stitchdatavalid) return GetMesh()->GetStitchData(this);
else return 0;
}
void SetStitchData(void* data) {
GetMesh()->SetStitchData(this, data);
stitchdatavalid = data ? 1 : 0;
if (opposite) {
opposite->GetMesh()->SetStitchData(opposite, 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;
}
}
void ToggleStitchCCW(bool raytraced) {
if (raytraced) {
raystitchccw = 1 - raystitchccw;
if (opposite) opposite->raystitchccw = raystitchccw;
} else {
stitchccw = 1 - stitchccw;
if (opposite) opposite->stitchccw = stitchccw;
}
}
#endif
// Marks the edge as being "coarse" (belonging to the control
// mesh). Generally this distinction only needs to be made if
// we're worried about interpolateboundary behaviour
void SetCoarse(bool c) { coarse = c; }
bool IsCoarse() const { return coarse; }
friend class HbrFace<T>;
private:
HbrHalfedge<T>* opposite;
// Index of incident vertex
int incidentVertex;
// Index of subdivided vertex child
int vchild;
float sharpness;
#ifdef HBRSTITCH
unsigned short stitchccw:1;
unsigned short raystitchccw:1;
unsigned short stitchdatavalid:1;
#endif
unsigned short coarse:1;
unsigned short lastedge:1;
unsigned short firstedge:1;
// If m_index = 0, 1, 2 or 3: we are the m_index edge of an
// incident face with 3 or 4 vertices.
// If m_index = 4: our incident face has more than 4 vertices, and
// we must do some extra math to determine what our actual index
// is. See getIndex()
unsigned short m_index:3;
// Returns the index of the edge relative to its incident face.
// This relies on knowledge of the face's edge allocation pattern
int getIndex() const {
if (m_index < 4) {
return m_index;
} else {
// We allocate room for up to 4 values (to handle tri or
// quad) in the edges array. If there are more than that,
// they _all_ go in the faces' extraedges array.
HbrFace<T>* incidentFace = *(HbrFace<T>**)((char *) this + sizeof(HbrHalfedge<T>));
return int(((char *) this - incidentFace->extraedges) /
(sizeof(HbrHalfedge<T>) + sizeof(HbrFace<T>*)));
}
}
// 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
// been computed yet.
unsigned int *getFVarInfSharp() {
unsigned int *fvarbits = GetFace()->fvarbits;
if (fvarbits) {
int fvarbitsSizePerEdge = ((GetMesh()->GetFVarCount() + 15) / 16);
return fvarbits + getIndex() * fvarbitsSizePerEdge;
} else {
return 0;
}
}
#ifdef HBRSTITCH
StitchEdge **getStitchEdges() {
return GetFace()->stitchEdges + GetMesh()->GetStitchCount() * getIndex();
}
#endif
#ifdef HBR_ADAPTIVE
public:
struct adaptiveFlags {
unsigned isTransition:1;
unsigned isTriangleHead:1;
unsigned isWatertightCritical:1;
adaptiveFlags() : isTransition(0),isTriangleHead(0),isWatertightCritical(0) { }
};
adaptiveFlags _adaptiveFlags;
bool IsInsideHole() const {
HbrFace<T> * left = GetLeftFace();
if (left and (not left->IsHole()))
return false;
HbrFace<T> * right = GetRightFace();
if (right and (not right->IsHole()))
return false;
return true;
}
bool IsTransition() const { return _adaptiveFlags.isTransition; }
bool IsTriangleHead() const { return _adaptiveFlags.isTriangleHead; }
bool IsWatertightCritical() const { return _adaptiveFlags.isWatertightCritical; }
#endif
};
template <class T>
void
HbrHalfedge<T>::Initialize(HbrHalfedge<T>* opposite, int index, HbrVertex<T>* origin,
unsigned int *fvarbits, HbrFace<T>* face) {
HbrMesh<T> *mesh = face->GetMesh();
if (face->GetNumVertices() <= 4) {
m_index = index;
} else {
m_index = 4;
// Assumes upstream allocation ensured we have extra storage
// for pointer to face after the halfedge data structure
// itself
*(HbrFace<T>**)((char *) this + sizeof(HbrHalfedge<T>)) = face;
}
this->opposite = opposite;
incidentVertex = origin->GetID();
lastedge = (index == face->GetNumVertices() - 1);
firstedge = (index == 0);
if (opposite) {
sharpness = opposite->sharpness;
#ifdef HBRSTITCH
StitchEdge **stitchEdges = face->stitchEdges +
mesh->GetStitchCount() * index;
for (int i = 0; i < mesh->GetStitchCount(); ++i) {
stitchEdges[i] = opposite->getStitchEdges()[i];
}
stitchccw = opposite->stitchccw;
raystitchccw = opposite->raystitchccw;
stitchdatavalid = 0;
if (stitchEdges && opposite->GetStitchData()) {
mesh->SetStitchData(this, opposite->GetStitchData());
stitchdatavalid = 1;
}
#endif
if (fvarbits) {
const int fvarcount = mesh->GetFVarCount();
int fvarbitsSizePerEdge = ((fvarcount + 15) / 16);
memcpy(fvarbits, opposite->getFVarInfSharp(), fvarbitsSizePerEdge * sizeof(unsigned int));
}
} else {
sharpness = 0.0f;
#ifdef HBRSTITCH
StitchEdge **stitchEdges = getStitchEdges();
for (int i = 0; i < mesh->GetStitchCount(); ++i) {
stitchEdges[i] = 0;
}
stitchccw = 1;
raystitchccw = 1;
stitchdatavalid = 0;
#endif
if (fvarbits) {
const int fvarcount = mesh->GetFVarCount();
int fvarbitsSizePerEdge = ((fvarcount + 15) / 16);
memset(fvarbits, 0xff, fvarbitsSizePerEdge * sizeof(unsigned int));
}
}
}
template <class T>
HbrHalfedge<T>::~HbrHalfedge() {
Clear();
}
template <class T>
void
HbrHalfedge<T>::Clear() {
if (opposite) {
opposite->opposite = 0;
if (vchild != -1) {
// Transfer ownership of the vchild to the opposite ptr
opposite->vchild = vchild;
HbrVertex<T> *vchildVert = GetMesh()->GetVertex(vchild);
// Done this way just for assertion sanity
vchildVert->SetParent(static_cast<HbrHalfedge*>(0));
vchildVert->SetParent(opposite);
vchild = -1;
}
opposite = 0;
}
// Orphan the child vertex
else if (vchild != -1) {
HbrVertex<T> *vchildVert = GetMesh()->GetVertex(vchild);
vchildVert->SetParent(static_cast<HbrHalfedge*>(0));
vchild = -1;
}
}
template <class T>
HbrVertex<T>*
HbrHalfedge<T>::Subdivide() {
HbrMesh<T>* mesh = GetMesh();
if (vchild != -1) return mesh->GetVertex(vchild);
// Make sure that our opposite doesn't "own" a subdivided vertex
// already. If it does, use that
if (opposite && opposite->vchild != -1) return mesh->GetVertex(opposite->vchild);
HbrVertex<T>* vchildVert = mesh->GetSubdivision()->Subdivide(mesh, this);
vchild = vchildVert->GetID();
vchildVert->SetParent(this);
return vchildVert;
}
template <class T>
void
HbrHalfedge<T>::GuaranteeNeighbor() {
HbrMesh<T>* mesh = GetMesh();
mesh->GetSubdivision()->GuaranteeNeighbor(mesh, this);
}
// Determines whether an edge is infinitely sharp as far as its
// facevarying data is concerned. Happens if the faces on both sides
// disagree on the facevarying data at either of the shared vertices
// on the edge.
template <class T>
bool
HbrHalfedge<T>::GetFVarInfiniteSharp(int datum) {
// Check to see if already initialized
int intindex = datum >> 4;
int shift = (datum & 15) << 1;
unsigned int *fvarinfsharp = getFVarInfSharp();
unsigned int bits = (fvarinfsharp[intindex] >> shift) & 0x3;
if (bits != 3) {
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;
}
// If either incident face is missing, it's a geometric boundary
// edge, and also a facevarying boundary edge
HbrFace<T>* left = GetLeftFace(), *right = GetRightFace();
if (!left || !right) {
bits = ~(0x2 << shift);
fvarinfsharp[intindex] &= bits;
if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
return true;
}
// Look for the indices on each face which correspond to the
// origin and destination vertices of the edge
int lorg = -1, ldst = -1, rorg = -1, rdst = -1, i, nv;
HbrHalfedge<T>* e;
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();
}
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();
}
assert(lorg >= 0 && ldst >= 0 && rorg >= 0 && rdst >= 0);
// Compare the facevarying data to some tolerance
const int startindex = GetMesh()->GetFVarIndices()[datum];
const int width = GetMesh()->GetFVarWidths()[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;
return true;
}
bits = ~(0x3 << shift);
fvarinfsharp[intindex] &= bits;
if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
return false;
}
template <class T>
bool
HbrHalfedge<T>::IsFVarInfiniteSharpAnywhere() {
if (sharpness > k_Smooth) {
return true;
}
for (int i = 0; i < GetMesh()->GetFVarCount(); ++i) {
if (GetFVarInfiniteSharp(i)) return true;
}
return false;
}
template <class T>
float
HbrHalfedge<T>::GetFVarSharpness(int datum, bool ignoreGeometry) {
if (GetFVarInfiniteSharp(datum)) 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) {
SetFVarInfiniteSharp(datum, true);
return k_InfinitelySharp;
}
}
return k_Smooth;
}
template <class T>
std::ostream&
operator<<(std::ostream& out, const HbrHalfedge<T>& edge) {
if (edge.IsBoundary()) out << "boundary ";
out << "edge connecting ";
if (edge.GetOrgVertex())
out << *edge.GetOrgVertex();
else
out << "(none)";
out << " to ";
if (edge.GetDestVertex()) {
out << *edge.GetDestVertex();
} else {
out << "(none)";
}
return out;
}
// Sorts half edges by the relative ordering of the incident faces'
// paths.
template <class T>
class HbrHalfedgeCompare {
public:
bool operator() (const HbrHalfedge<T>* a, HbrHalfedge<T>* b) const {
return (a->GetFace()->GetPath() < b->GetFace()->GetPath());
}
};
template <class T>
class HbrHalfedgeOperator {
public:
virtual void operator() (HbrHalfedge<T> &edge) = 0;
virtual ~HbrHalfedgeOperator() {}
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRHALFEDGE_H */

View File

@@ -0,0 +1,171 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRHIERARCHICALEDIT_H
#define OPENSUBDIV3_HBRHIERARCHICALEDIT_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrHierarchicalEdit;
template <class T> class HbrFace;
template <class T> class HbrVertex;
template <class T>
class HbrHierarchicalEdit {
public:
typedef enum Operation {
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];
}
}
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<unsigned char>(_subfaces[i]);
}
}
public:
virtual ~HbrHierarchicalEdit() {
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);
}
// Return the face id (the first element in the path)
int GetFaceID() const { return faceid; }
// Return the number of subfaces in the path
int GetNSubfaces() const { return nsubfaces; }
// 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<T>* face) const;
// Applys edit to face. All subclasses may override this method
virtual void ApplyEditToFace(HbrFace<T>* /* face */) {}
// Applys edit to vertex. Subclasses may override this method.
virtual void ApplyEditToVertex(HbrFace<T>* /* face */, HbrVertex<T>* /* 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 */) const {}
#endif
protected:
// ID of the top most face in the mesh which begins the path
const int faceid;
// Number of subfaces
const int nsubfaces;
// IDs of the subfaces
unsigned char *subfaces;
};
template <class T>
class HbrHierarchicalEditComparator {
public:
bool operator() (const HbrHierarchicalEdit<T>* path1, const HbrHierarchicalEdit<T>* path2) const {
return (*path1 < *path2);
}
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#include "../hbr/face.h"
#include <cstring>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T>
bool
HbrHierarchicalEdit<T>::IsRelevantToFace(HbrFace<T>* face) const {
// Key assumption: the face's first vertex edit is relevant to
// that face. We will then compare ourselves to that edit and if
// the first part of our subpath is identical to the entirety of
// that subpath, this edit is relevant.
// Calling code is responsible for making sure we don't
// dereference a null pointer here
HbrHierarchicalEdit<T>* p = *face->GetHierarchicalEdits();
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
// that we're dealing with another face somewhere up the path, so
// we're not relevant
if (nsubfaces < face->GetDepth()) return false;
if (memcmp(subfaces, p->subfaces, face->GetDepth() * sizeof(unsigned char)) != 0) {
return false;
}
return true;
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRHIERARCHICALEDIT_H */

View File

@@ -0,0 +1,74 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRHOLEEDIT_H
#define OPENSUBDIV3_HBRHOLEEDIT_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrHoleEdit;
template <class T>
std::ostream& operator<<(std::ostream& out, const HbrHoleEdit<T>& path) {
out << "edge path = (" << path.faceid << ' ';
for (int i = 0; i < path.nsubfaces; ++i) {
out << static_cast<int>(path.subfaces[i]) << ' ';
}
return out << ")";
}
template <class T>
class HbrHoleEdit : public HbrHierarchicalEdit<T> {
public:
HbrHoleEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces)
: HbrHierarchicalEdit<T>(_faceid, _nsubfaces, _subfaces) {
}
HbrHoleEdit(int _faceid, int _nsubfaces, int *_subfaces)
: HbrHierarchicalEdit<T>(_faceid, _nsubfaces, _subfaces) {
}
virtual ~HbrHoleEdit() {}
friend std::ostream& operator<< <T> (std::ostream& out, const HbrHoleEdit<T>& path);
virtual void ApplyEditToFace(HbrFace<T>* face) {
if (HbrHierarchicalEdit<T>::GetNSubfaces() == face->GetDepth()) {
face->SetHole();
}
}
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRHOLEEDIT_H */

View File

@@ -0,0 +1,979 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRLOOP_H
#define OPENSUBDIV3_HBRLOOP_H
#include <cmath>
#include <assert.h>
#include <algorithm>
#include "../hbr/subdivision.h"
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
/* #define HBR_DEBUG */
template <class T>
class HbrLoopSubdivision : public HbrSubdivision<T>{
public:
HbrLoopSubdivision<T>()
: HbrSubdivision<T>() {}
virtual HbrSubdivision<T>* Clone() const {
return new HbrLoopSubdivision<T>();
}
virtual void Refine(HbrMesh<T>* mesh, HbrFace<T>* face);
virtual HbrFace<T>* RefineFaceAtVertex(HbrMesh<T>* mesh, HbrFace<T>* face, HbrVertex<T>* vertex);
virtual void GuaranteeNeighbor(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
virtual void GuaranteeNeighbors(HbrMesh<T>* mesh, HbrVertex<T>* vertex);
virtual bool HasLimit(HbrMesh<T>* mesh, HbrFace<T>* face);
virtual bool HasLimit(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
virtual bool HasLimit(HbrMesh<T>* mesh, HbrVertex<T>* vertex);
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrFace<T>* face);
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrVertex<T>* vertex);
virtual bool VertexIsExtraordinary(HbrMesh<T> const * /* mesh */, HbrVertex<T>* vertex) { return vertex->GetValence() != 6; }
virtual bool FaceIsExtraordinary(HbrMesh<T> const * /* mesh */, HbrFace<T>* 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
void transferFVarToChild(HbrMesh<T>* mesh, HbrFace<T>* face, HbrFace<T>* child, int index);
// Transfers vertex and edge edits from a parent face to a child face
void transferEditsToChild(HbrFace<T>* face, HbrFace<T>* child, int index);
// Generates the fourth child of a triangle: the triangle in the
// middle whose vertices have parents which are all edges
void refineFaceAtMiddle(HbrMesh<T>* mesh, HbrFace<T>* face);
};
template <class T>
void
HbrLoopSubdivision<T>::transferFVarToChild(HbrMesh<T>* mesh, HbrFace<T>* face, HbrFace<T>* child, int index) {
typename HbrMesh<T>::InterpolateBoundaryMethod fvarinterp = mesh->GetFVarInterpolateBoundaryMethod();
HbrVertex<T>* childVertex;
// In the case of index == 3, this is the middle face, and so
// we need to do three edge subdivision rules
if (index == 3) {
const int fvarcount = mesh->GetFVarCount();
for (int i = 0; i < 3; ++i) {
HbrHalfedge<T> *edge = face->GetEdge(i);
GuaranteeNeighbor(mesh, edge);
childVertex = child->GetVertex((i + 2) % 3);
bool fvIsSmooth = !edge->IsFVarInfiniteSharpAnywhere();
if (!fvIsSmooth) {
childVertex->NewFVarData(child);
}
HbrFVarData<T>& fv = childVertex->GetFVarData(child);
int fvarindex = 0;
for (int fvaritem = 0; fvaritem < fvarcount; ++fvaritem) {
const int fvarwidth = mesh->GetFVarWidths()[fvaritem];
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
face->GetEdge(i)->GetFVarSharpness(fvaritem) || face->GetEdge(i)->IsBoundary()) {
// Sharp edge rule
fv.SetWithWeight(face->GetFVarData(i), fvarindex, fvarwidth, 0.5f);
fv.AddWithWeight(face->GetFVarData((i + 1) % 3), fvarindex, fvarwidth, 0.5f);
} else if (!fvIsSmooth || !fv.IsInitialized()) {
// Smooth edge subdivision. Add 0.375 of adjacent vertices
fv.SetWithWeight(face->GetFVarData(i), fvarindex, fvarwidth, 0.375f);
fv.AddWithWeight(face->GetFVarData((i + 1) % 3), fvarindex, fvarwidth, 0.375f);
// Add 0.125 of opposite vertices
fv.AddWithWeight(face->GetFVarData((i + 2) % 3), fvarindex, fvarwidth, 0.125f);
HbrFace<T>* oppFace = face->GetEdge(i)->GetRightFace();
for (int j = 0; j < oppFace->GetNumVertices(); ++j) {
if (oppFace->GetVertex(j) == face->GetVertex(i)) {
fv.AddWithWeight(oppFace->GetFVarData((j+1)%oppFace->GetNumVertices()), fvarindex, fvarwidth, 0.125f);
break;
}
}
}
fvarindex += fvarwidth;
}
fv.SetInitialized();
}
return;
}
HbrHalfedge<T>* edge;
HbrVertex<T>* 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
// infinitely sharp corner. We determine the last by checking the
// propagate corners flag on the mesh; if it's off, we check the
// two edges of this face incident to that vertex and determining
// whether they are facevarying boundary edges - this is analogous
// to what goes on for the interpolateboundary tag (which when set
// to EDGEANDCORNER marks vertices with a valence of two as being
// sharp corners). If propagate corners is on, we check *all*
// faces to see if two edges side by side are facevarying boundary
// edges. The facevarying boundary check ignores geometric
// sharpness, otherwise we may swim at geometric creases which
// aren't actually discontinuous.
//
// We need to make sure that that each of the vertices of the
// child face have the appropriate facevarying storage as
// needed. If there are discontinuities in any facevarying datum,
// the vertex must allocate a new block of facevarying storage
// specific to the child face.
v->GuaranteeNeighbors();
bool fv0IsSmooth, fv1IsSmooth, fv2IsSmooth;
childVertex = child->GetVertex(index);
fv0IsSmooth = v->IsFVarAllSmooth();
if (!fv0IsSmooth) {
childVertex->NewFVarData(child);
}
HbrFVarData<T>& fv0 = childVertex->GetFVarData(child);
edge = face->GetEdge(index);
GuaranteeNeighbor(mesh, edge);
assert(edge->GetOrgVertex() == v);
childVertex = child->GetVertex((index + 1) % 3);
fv1IsSmooth = !edge->IsFVarInfiniteSharpAnywhere();
if (!fv1IsSmooth) {
childVertex->NewFVarData(child);
}
HbrFVarData<T>& fv1 = childVertex->GetFVarData(child);
edge = edge->GetPrev();
GuaranteeNeighbor(mesh, edge);
assert(edge == face->GetEdge((index + 2) % 3));
assert(edge->GetDestVertex() == v);
childVertex = child->GetVertex((index + 2) % 3);
fv2IsSmooth = !edge->IsFVarInfiniteSharpAnywhere();
if (!fv2IsSmooth) {
childVertex->NewFVarData(child);
}
HbrFVarData<T>& 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);
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryEdgeAndCorner) {
if (fvarmask >= HbrVertex<T>::k_Corner) {
infcorner = true;
} else if (mesh->GetFVarPropagateCorners()) {
if (v->IsFVarCorner(fvaritem)) {
infcorner = true;
}
} else {
if (face->GetEdge(index)->GetFVarSharpness(fvaritem, true) && face->GetEdge(index)->GetPrev()->GetFVarSharpness(fvaritem, true)) {
infcorner = true;
}
}
}
// Infinitely sharp vertex rule. Applied if the vertex is:
// - undergoing no facevarying boundary interpolation;
// - at a geometric crease, in either boundary interpolation case; or
// - is an infinitely sharp facevarying vertex, in the EDGEANDCORNER case; or
// - has a mask equal or greater than one, in the "always
// sharp" interpolate boundary case
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
(fvarinterp == HbrMesh<T>::k_InterpolateBoundaryAlwaysSharp &&
fvarmask >= 1) ||
v->GetSharpness() > HbrVertex<T>::k_Smooth ||
infcorner) {
fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 1.0f);
}
// Dart rule: unlike geometric creases, because there's two
// discontinuous values for the one incident edge, we use the
// boundary rule and not the smooth rule
else if (fvarmask == 1) {
// Use 0.75 of the current vert
fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.75f);
// 0.125 of "two adjacent edge vertices", which in actuality
// are the facevarying values of the same vertex but on each
// side of the single incident facevarying sharp edge
HbrHalfedge<T>* start = v->GetIncidentEdge(), *edge, *nextedge;
edge = start;
while (edge) {
if (edge->GetFVarSharpness(fvaritem)) {
break;
}
nextedge = v->GetNextEdge(edge);
if (nextedge == start) {
assert(0); // we should have found it by now
break;
} else if (!nextedge) {
// should never get into this case - if the vertex is
// on a boundary, it can never be a facevarying dart
// vertex
assert(0);
edge = edge->GetPrev();
break;
} else {
edge = nextedge;
}
}
HbrVertex<T>* w = edge->GetDestVertex();
HbrFace<T>* bestface = edge->GetLeftFace();
int j;
for (j = 0; j < bestface->GetNumVertices(); ++j) {
if (bestface->GetVertex(j) == w) break;
}
assert(j != bestface->GetNumVertices());
fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
bestface = edge->GetRightFace();
for (j = 0; j < bestface->GetNumVertices(); ++j) {
if (bestface->GetVertex(j) == w) break;
}
assert(j != bestface->GetNumVertices());
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) {
// Use 0.75 of the current vert
fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.75f);
// Compute 0.125 of two adjacent edge vertices. However the
// two adjacent edge vertices we use must be part of the
// facevarying "boundary". To find the first edge we cycle
// counterclockwise around the current vertex v and look for
// the first boundary edge
HbrFace<T>* bestface = face;
HbrHalfedge<T>* bestedge = face->GetEdge(index)->GetPrev();
HbrHalfedge<T>* starte = bestedge->GetOpposite();
HbrVertex<T>* w = 0;
if (!starte) {
w = face->GetEdge(index)->GetPrev()->GetOrgVertex();
} else {
HbrHalfedge<T>* e = starte, *next;
assert(starte->GetOrgVertex() == v);
do {
if (e->GetFVarSharpness(fvaritem) || !e->GetLeftFace()) {
bestface = e->GetRightFace();
bestedge = e;
break;
}
next = v->GetNextEdge(e);
if (!next) {
bestface = e->GetLeftFace();
w = e->GetPrev()->GetOrgVertex();
break;
}
e = next;
} while (e && e != starte);
}
if (!w) w = bestedge->GetDestVertex();
int j;
for (j = 0; j < bestface->GetNumVertices(); ++j) {
if (bestface->GetVertex(j) == w) break;
}
assert(j != bestface->GetNumVertices());
fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
// Look for the other edge by cycling clockwise around v
bestface = face;
bestedge = face->GetEdge(index);
starte = bestedge;
w = 0;
if (HbrHalfedge<T>* e = starte) {
assert(starte->GetOrgVertex() == v);
do {
if (e->GetFVarSharpness(fvaritem) || !e->GetRightFace()) {
bestface = e->GetLeftFace();
bestedge = e;
break;
}
assert(e->GetOpposite());
e = v->GetPreviousEdge(e);
} while (e && e != starte);
}
if (!w) w = bestedge->GetDestVertex();
for (j = 0; j < bestface->GetNumVertices(); ++j) {
if (bestface->GetVertex(j) == w) break;
}
assert(j != bestface->GetNumVertices());
fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
}
// Smooth rule
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;
beta = beta * beta;
beta = (0.625f - beta) * invvalence;
// Use 1 - beta * valence of the current vertex value
fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 1 - (beta * valence));
// Add beta of surrounding vertices averages. We loop over all
// surrounding faces..
HbrHalfedge<T>* start = v->GetIncidentEdge(), *edge;
edge = start;
while (edge) {
HbrFace<T>* g = edge->GetLeftFace();
// .. and look for the edge on that face whose origin is
// the same as v, and add a contribution from its
// destination vertex value; this takes care of the
// surrounding edge vertex addition.
for (int j = 0; j < g->GetNumVertices(); ++j) {
if (g->GetEdge(j)->GetOrgVertex() == v) {
fv0.AddWithWeight(g->GetFVarData((j + 1) % g->GetNumVertices()), fvarindex, fvarwidth, beta);
break;
}
}
edge = v->GetNextEdge(edge);
if (edge == start) break;
}
}
// Edge subdivision rule
HbrHalfedge<T>* edge = face->GetEdge(index);
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {
// Sharp edge rule
fv1.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.5f);
fv1.AddWithWeight(face->GetFVarData((index + 1) % 3), fvarindex, fvarwidth, 0.5f);
} else if (!fv1IsSmooth || !fv1.IsInitialized()) {
// Smooth edge subdivision. Add 0.375 of adjacent vertices
fv1.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.375f);
fv1.AddWithWeight(face->GetFVarData((index + 1) % 3), fvarindex, fvarwidth, 0.375f);
// Add 0.125 of opposite vertices
fv1.AddWithWeight(face->GetFVarData((index + 2) % 3), fvarindex, fvarwidth, 0.125f);
HbrFace<T>* oppFace = edge->GetRightFace();
for (int j = 0; j < oppFace->GetNumVertices(); ++j) {
if (oppFace->GetVertex(j) == v) {
fv1.AddWithWeight(oppFace->GetFVarData((j+1)%oppFace->GetNumVertices()), fvarindex, fvarwidth, 0.125f);
break;
}
}
}
// Edge subdivision rule
edge = edge->GetPrev();
if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {
// 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
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);
HbrFace<T>* oppFace = edge->GetRightFace();
for (int j = 0; j < oppFace->GetNumVertices(); ++j) {
if (oppFace->GetVertex(j) == v) {
fv2.AddWithWeight(oppFace->GetFVarData((j+2)%oppFace->GetNumVertices()), fvarindex, fvarwidth, 0.125f);
break;
}
}
}
fvarindex += fvarwidth;
}
fv0.SetInitialized();
fv1.SetInitialized();
fv2.SetInitialized();
}
template <class T>
void
HbrLoopSubdivision<T>::transferEditsToChild(HbrFace<T>* face, HbrFace<T>* child, int index) {
// Hand down hole tag
child->SetHole(face->IsHole());
// Hand down pointers to hierarchical edits
if (HbrHierarchicalEdit<T>** edits = face->GetHierarchicalEdits()) {
while (HbrHierarchicalEdit<T>* edit = *edits) {
if (!edit->IsRelevantToFace(face)) break;
if (edit->GetNSubfaces() > face->GetDepth() &&
(edit->GetSubface(face->GetDepth()) == index)) {
child->SetHierarchicalEdits(edits);
break;
}
edits++;
}
}
}
template <class T>
void
HbrLoopSubdivision<T>::Refine(HbrMesh<T>* mesh, HbrFace<T>* face) {
#ifdef HBR_DEBUG
std::cerr << "\n\nRefining face " << *face << "\n";
#endif
assert(face->GetNumVertices() == 3); // or triangulate it?
HbrHalfedge<T>* edge = face->GetFirstEdge();
HbrHalfedge<T>* prevedge = edge->GetPrev();
for (int i = 0; i < 3; ++i) {
HbrVertex<T>* vertex = edge->GetOrgVertex();
if (!face->GetChild(i)) {
#ifdef HBR_DEBUG
std::cerr << "Kid " << i << "\n";
#endif
HbrFace<T>* child;
HbrVertex<T>* 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);
#ifdef HBR_DEBUG
std::cerr << "Creating face " << *child << " during refine\n";
#endif
// Hand down edge sharpness
float sharpness;
HbrHalfedge<T>* childedge;
childedge = child->GetEdge(i);
if ((sharpness = edge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
HbrSubdivision<T>::SubdivideCreaseWeight(
edge, edge->GetOrgVertex(), childedge);
}
childedge->CopyFVarInfiniteSharpness(edge);
childedge = child->GetEdge((i+2)%3);
if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
HbrSubdivision<T>::SubdivideCreaseWeight(
prevedge, prevedge->GetDestVertex(), childedge);
}
childedge->CopyFVarInfiniteSharpness(prevedge);
if (mesh->GetTotalFVarWidth()) {
transferFVarToChild(mesh, face, child, i);
}
transferEditsToChild(face, child, i);
}
prevedge = edge;
edge = edge->GetNext();
}
refineFaceAtMiddle(mesh, face);
}
template <class T>
HbrFace<T>*
HbrLoopSubdivision<T>::RefineFaceAtVertex(HbrMesh<T>* mesh, HbrFace<T>* face, HbrVertex<T>* vertex) {
#ifdef HBR_DEBUG
std::cerr << " forcing refine on " << *face << " at " << *vertex << '\n';
#endif
HbrHalfedge<T>* edge = face->GetFirstEdge();
HbrHalfedge<T>* prevedge = edge->GetPrev();
for (int i = 0; i < 3; ++i) {
if (edge->GetOrgVertex() == vertex) {
if (!face->GetChild(i)) {
#ifdef HBR_DEBUG
std::cerr << "Kid " << i << "\n";
#endif
HbrFace<T>* child;
HbrVertex<T>* 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);
#ifdef HBR_DEBUG
std::cerr << "Creating face " << *child << " during refine\n";
#endif
// Hand down edge sharpness
float sharpness;
HbrHalfedge<T>* childedge;
childedge = child->GetEdge(i);
if ((sharpness = edge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
HbrSubdivision<T>::SubdivideCreaseWeight(
edge, edge->GetOrgVertex(), childedge);
}
childedge->CopyFVarInfiniteSharpness(edge);
childedge = child->GetEdge((i+2)%3);
if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
HbrSubdivision<T>::SubdivideCreaseWeight(
prevedge, prevedge->GetDestVertex(), childedge);
}
childedge->CopyFVarInfiniteSharpness(prevedge);
if (mesh->GetTotalFVarWidth()) {
transferFVarToChild(mesh, face, child, i);
}
transferEditsToChild(face, child, i);
return child;
} else {
return face->GetChild(i);
}
}
prevedge = edge;
edge = edge->GetNext();
}
return 0;
}
template <class T>
void
HbrLoopSubdivision<T>::GuaranteeNeighbor(HbrMesh<T>* mesh, HbrHalfedge<T>* edge) {
if (edge->GetOpposite()) {
return;
}
#ifdef HBR_DEBUG
std::cerr << "\n\nneighbor guarantee at " << *edge << " invoked\n";
#endif
/*
Imagine the following:
X
/ \
/ \
/ \
X \
/\ \
2/ \3 \
/ \ \
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
refining the parent face at one vertex (where the two parent
edges meet) should suffice
*/
HbrHalfedge<T>* parentEdge1 = edge->GetOrgVertex()->GetParentEdge();
HbrHalfedge<T>* parentEdge2 = edge->GetDestVertex()->GetParentEdge();
if (parentEdge1 && parentEdge2) {
#ifdef HBR_DEBUG
std::cerr << "two parent edge situation\n";
#endif
HbrFace<T>* 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";
#endif
HbrVertex<T>* 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";
#endif
HbrVertex<T>* parentVertex1 = edge->GetOrgVertex()->GetParentVertex();
assert(parentVertex1);
RefineFaceAtVertex(mesh, parentEdge2->GetLeftFace(), parentVertex1);
if (parentEdge2->GetRightFace()) {
RefineFaceAtVertex(mesh, parentEdge2->GetRightFace(), parentVertex1);
}
}
}
template <class T>
void
HbrLoopSubdivision<T>::GuaranteeNeighbors(HbrMesh<T>* mesh, HbrVertex<T>* vertex) {
#ifdef HBR_DEBUG
std::cerr << "\n\nneighbor guarantee at " << *vertex << " invoked\n";
#endif
assert(vertex->GetParentFace() == 0);
// The first case: the vertex is a child of an edge. Make sure
// that the parent faces on either side of the parent edge exist,
// 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<T>* parentEdge = vertex->GetParentEdge();
if (parentEdge) {
#ifdef HBR_DEBUG
std::cerr << "parent edge situation " << *parentEdge << "\n";
#endif
HbrVertex<T>* dest = parentEdge->GetDestVertex();
HbrVertex<T>* org = parentEdge->GetOrgVertex();
GuaranteeNeighbor(mesh, parentEdge);
HbrFace<T>* 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<T>* parentVertex = vertex->GetParentVertex();
if (parentVertex) {
#ifdef HBR_DEBUG
std::cerr << "parent vertex situation " << *parentVertex << "\n";
#endif
parentVertex->GuaranteeNeighbors();
// And then we refine all the face neighbors of the parent
// vertex
HbrHalfedge<T>* start = parentVertex->GetIncidentEdge(), *edge;
edge = start;
while (edge) {
HbrFace<T>* f = edge->GetLeftFace();
RefineFaceAtVertex(mesh, f, parentVertex);
edge = parentVertex->GetNextEdge(edge);
if (edge == start) break;
}
}
}
template <class T>
bool
HbrLoopSubdivision<T>::HasLimit(HbrMesh<T>* mesh, HbrFace<T>* 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;
}
}
return true;
}
template <class T>
bool
HbrLoopSubdivision<T>::HasLimit(HbrMesh<T>* mesh, HbrHalfedge<T>* 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.
if (edge->GetSharpness() >= HbrHalfedge<T>::k_InfinitelySharp) return true;
if (!HasLimit(mesh, edge->GetOrgVertex()) || !HasLimit(mesh, edge->GetDestVertex())) return false;
return !edge->IsBoundary();
}
template <class T>
bool
HbrLoopSubdivision<T>::HasLimit(HbrMesh<T>* /* mesh */, HbrVertex<T>* vertex) {
vertex->GuaranteeNeighbors();
switch (vertex->GetMask(false)) {
case HbrVertex<T>::k_Smooth:
case HbrVertex<T>::k_Dart:
return !vertex->OnBoundary();
break;
case HbrVertex<T>::k_Crease:
case HbrVertex<T>::k_Corner:
default:
if (vertex->IsVolatile()) {
// Search for any incident semisharp boundary edge
HbrHalfedge<T>* start = vertex->GetIncidentEdge(), *edge, *next;
edge = start;
while (edge) {
if (edge->IsBoundary() && edge->GetSharpness() < HbrHalfedge<T>::k_InfinitelySharp) {
return false;
}
next = vertex->GetNextEdge(edge);
if (next == start) {
break;
} else if (!next) {
edge = edge->GetPrev();
if (edge->IsBoundary() && edge->GetSharpness() < HbrHalfedge<T>::k_InfinitelySharp) {
return false;
}
break;
} else {
edge = next;
}
}
}
return true;
}
}
template <class T>
HbrVertex<T>*
HbrLoopSubdivision<T>::Subdivide(HbrMesh<T>* /* mesh */, HbrFace<T>* /* face */) {
// In loop subdivision, faces never subdivide
assert(0);
return 0;
}
template <class T>
HbrVertex<T>*
HbrLoopSubdivision<T>::Subdivide(HbrMesh<T>* mesh, HbrHalfedge<T>* edge) {
#ifdef HBR_DEBUG
std::cerr << "Subdividing at " << *edge << "\n";
#endif
// Ensure the opposite face exists.
GuaranteeNeighbor(mesh, edge);
float esharp = edge->GetSharpness();
HbrVertex<T>* 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();
}
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).
// 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<T>* 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);
}
// Varying data is always the average of two end points
data.AddVaryingWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
data.AddVaryingWithWeight(edge->GetDestVertex()->GetData(), 0.5f);
#ifdef HBR_DEBUG
std::cerr << " created " << *v << "\n";
#endif
// Only boundary edges will create extraordinary vertices
if (edge->IsBoundary()) {
v->SetExtraordinary();
}
return v;
}
template <class T>
HbrVertex<T>*
HbrLoopSubdivision<T>::Subdivide(HbrMesh<T>* mesh, HbrVertex<T>* vertex) {
// Ensure the ring of faces around this vertex exists before
// we compute the valence
vertex->GuaranteeNeighbors();
float valence = static_cast<float>(vertex->GetValence());
float invvalence = 1.0f / valence;
HbrVertex<T>* v = mesh->NewVertex();
T& data = v->GetData();
// Due to fractional weights we may need to do two subdivision
// passes
int masks[2];
float weights[2];
int passes;
masks[0] = vertex->GetMask(false);
masks[1] = vertex->GetMask(true);
// If the masks are different, we subdivide twice: once using the
// current mask, once using the mask at the next level of
// 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;
} else {
weights[0] = 1.0f;
weights[1] = 0.0f;
passes = 1;
}
for (int i = 0; i < passes; ++i) {
switch (masks[i]) {
case HbrVertex<T>::k_Smooth:
case HbrVertex<T>::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)));
HbrSubdivision<T>::AddSurroundingVerticesWithWeight(
mesh, vertex, weights[i] * beta, &data);
break;
}
case HbrVertex<T>::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
HbrSubdivision<T>::AddCreaseEdgesWithWeight(
mesh, vertex, i == 1, weights[i] * 0.125f, &data);
break;
}
case HbrVertex<T>::k_Corner:
default: {
// Just copy the old value
data.AddWithWeight(vertex->GetData(), weights[i]);
break;
}
}
}
// Varying data is always just propagated down
data.AddVaryingWithWeight(vertex->GetData(), 1.0f);
#ifdef HBR_DEBUG
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<T>::k_InfinitelySharp) {
v->SetSharpness(HbrVertex<T>::k_InfinitelySharp);
} else if (sharp > HbrVertex<T>::k_Smooth) {
v->SetSharpness(std::max((float) HbrVertex<T>::k_Smooth, sharp - 1.0f));
} else {
v->SetSharpness(HbrVertex<T>::k_Smooth);
}
return v;
}
template <class T>
void
HbrLoopSubdivision<T>::refineFaceAtMiddle(HbrMesh<T>* mesh, HbrFace<T>* face) {
#ifdef HBR_DEBUG
std::cerr << "Refining middle face of " << *face << "\n";
#endif
if (!face->GetChild(3)) {
HbrFace<T>* child;
HbrVertex<T>* 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);
}
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRLOOP_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,293 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRSUBDIVISION_H
#define OPENSUBDIV3_HBRSUBDIVISION_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrFace;
template <class T> class HbrVertex;
template <class T> class HbrHalfedge;
template <class T> class HbrMesh;
template <class T> class HbrSubdivision {
public:
HbrSubdivision<T>()
: creaseSubdivision(k_CreaseNormal) {}
virtual ~HbrSubdivision<T>() {}
virtual HbrSubdivision<T>* Clone() const = 0;
// How to subdivide a face
virtual void Refine(HbrMesh<T>* mesh, HbrFace<T>* face) = 0;
// Subdivide a face only at a particular vertex (creating one child)
virtual HbrFace<T>* RefineFaceAtVertex(HbrMesh<T>* mesh, HbrFace<T>* face, HbrVertex<T>* vertex) = 0;
// Refine all faces around a particular vertex
virtual void RefineAtVertex(HbrMesh<T>* mesh, HbrVertex<T>* vertex);
// Given an edge, try to ensure the edge's opposite exists by
// forcing refinement up the hierarchy
virtual void GuaranteeNeighbor(HbrMesh<T>* mesh, HbrHalfedge<T>* edge) = 0;
// Given an vertex, ensure all faces in the ring around it exist
// by forcing refinement up the hierarchy
virtual void GuaranteeNeighbors(HbrMesh<T>* mesh, HbrVertex<T>* vertex) = 0;
// Returns true if the vertex, edge, or face has a limit point,
// curve, or surface associated with it
virtual bool HasLimit(HbrMesh<T>* /* mesh */, HbrFace<T>* /* face */) { return true; }
virtual bool HasLimit(HbrMesh<T>* /* mesh */, HbrHalfedge<T>* /* edge */) { return true; }
virtual bool HasLimit(HbrMesh<T>* /* mesh */, HbrVertex<T>* /* vertex */) { return true; }
// How to turn faces, edges, and vertices into vertices
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrFace<T>* face) = 0;
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrHalfedge<T>* edge) = 0;
virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrVertex<T>* vertex) = 0;
// Returns true if the vertex is extraordinary in the subdivision scheme
virtual bool VertexIsExtraordinary(HbrMesh<T> const * /* mesh */, HbrVertex<T>* /* vertex */) { return false; }
// Returns true if the face is extraordinary in the subdivision scheme
virtual bool FaceIsExtraordinary(HbrMesh<T> const * /* mesh */, HbrFace<T>* /* 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
// is to simply assign the current edge's crease strength - 1
// to both of the child subedges. The "Chaikin" rule looks at the
// current edge and incident edges to the current edge's end
// vertices, and weighs them; for more information consult
// the Geri's Game paper.
enum CreaseSubdivision {
k_CreaseNormal,
k_CreaseChaikin
};
CreaseSubdivision GetCreaseSubdivisionMethod() const { return creaseSubdivision; }
void SetCreaseSubdivisionMethod(CreaseSubdivision method) { creaseSubdivision = method; }
// Figures out how to assign a crease weight on an edge to its
// subedge. The subedge must be a child of the parent edge
// (either subedge->GetOrgVertex() or subedge->GetDestVertex()
// == edge->Subdivide()). The vertex supplied must NOT be
// a parent of the subedge; it is either the origin or
// destination vertex of edge.
void SubdivideCreaseWeight(HbrHalfedge<T>* edge, HbrVertex<T>* vertex, HbrHalfedge<T>* subedge);
// 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;
// Helper routine for subclasses: for a given vertex, sums
// contributions from surrounding vertices
void AddSurroundingVerticesWithWeight(HbrMesh<T>* mesh, HbrVertex<T>* vertex, float weight, T* data);
// Helper routine for subclasses: for a given vertex with a crease
// mask, adds contributions from the two crease edges
void AddCreaseEdgesWithWeight(HbrMesh<T>* mesh, HbrVertex<T>* vertex, bool next, float weight, T* data);
private:
// Helper class used by AddSurroundingVerticesWithWeight
class SmoothSubdivisionVertexOperator : public HbrVertexOperator<T> {
public:
SmoothSubdivisionVertexOperator(T* data, bool meshHasEdits, float weight)
: m_data(data),
m_meshHasEdits(meshHasEdits),
m_weight(weight)
{
}
virtual void operator() (HbrVertex<T> &vertex) {
// Must ensure vertex edits have been applied
if (m_meshHasEdits) {
vertex.GuaranteeNeighbors();
}
m_data->AddWithWeight(vertex.GetData(), m_weight);
}
private:
T* m_data;
const bool m_meshHasEdits;
const float m_weight;
};
// Helper class used by AddCreaseEdgesWithWeight
class CreaseSubdivisionHalfedgeOperator : public HbrHalfedgeOperator<T> {
public:
CreaseSubdivisionHalfedgeOperator(HbrVertex<T> *vertex, T* data, bool meshHasEdits, bool next, float weight)
: m_vertex(vertex),
m_data(data),
m_meshHasEdits(meshHasEdits),
m_next(next),
m_weight(weight),
m_count(0)
{
}
virtual void operator() (HbrHalfedge<T> &edge) {
if (m_count < 2 && edge.IsSharp(m_next)) {
HbrVertex<T>* a = edge.GetDestVertex();
if (a == m_vertex) a = edge.GetOrgVertex();
// Must ensure vertex edits have been applied
if (m_meshHasEdits) {
a->GuaranteeNeighbors();
}
m_data->AddWithWeight(a->GetData(), m_weight);
m_count++;
}
}
private:
HbrVertex<T>* m_vertex;
T* m_data;
const bool m_meshHasEdits;
const bool m_next;
const float m_weight;
int m_count;
};
private:
// Helper class used by RefineAtVertex.
class RefineFaceAtVertexOperator : public HbrFaceOperator<T> {
public:
RefineFaceAtVertexOperator(HbrSubdivision<T>* subdivision, HbrMesh<T>* mesh, HbrVertex<T> *vertex)
: m_subdivision(subdivision),
m_mesh(mesh),
m_vertex(vertex)
{
}
virtual void operator() (HbrFace<T> &face) {
m_subdivision->RefineFaceAtVertex(m_mesh, &face, m_vertex);
}
private:
HbrSubdivision<T>* const m_subdivision;
HbrMesh<T>* const m_mesh;
HbrVertex<T>* const m_vertex;
};
};
template <class T>
void
HbrSubdivision<T>::RefineAtVertex(HbrMesh<T>* mesh, HbrVertex<T>* vertex) {
GuaranteeNeighbors(mesh, vertex);
RefineFaceAtVertexOperator op(this, mesh, vertex);
vertex->ApplyOperatorSurroundingFaces(op);
}
template <class T>
void
HbrSubdivision<T>::SubdivideCreaseWeight(HbrHalfedge<T>* edge, HbrVertex<T>* vertex, HbrHalfedge<T>* subedge) {
float sharpness = edge->GetSharpness();
// In all methods, if the parent edge is infinitely sharp, the
// child edge is also infinitely sharp
if (sharpness >= HbrHalfedge<T>::k_InfinitelySharp) {
subedge->SetSharpness(HbrHalfedge<T>::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<T>::k_CreaseChaikin) {
float childsharp = 0.0f;
int n = 0;
// Add 1/4 of the sharpness of all crease edges incident to
// the vertex (other than this crease edge)
class ChaikinEdgeCreaseOperator : public HbrHalfedgeOperator<T> {
public:
ChaikinEdgeCreaseOperator(
HbrHalfedge<T> const * edge, float & childsharp, int & count) :
m_edge(edge), m_childsharp(childsharp), m_count(count) { }
virtual void operator() (HbrHalfedge<T> &edge) {
// Skip original edge or it's opposite
if ((&edge==m_edge) || (&edge==m_edge->GetOpposite()))
return;
if (edge.GetSharpness() > HbrHalfedge<T>::k_Smooth) {
m_childsharp += edge.GetSharpness();
++m_count;
}
}
private:
HbrHalfedge<T> const * m_edge;
float & m_childsharp;
int & m_count;
};
ChaikinEdgeCreaseOperator op(edge, childsharp, n);
vertex->GuaranteeNeighbors();
vertex->ApplyOperatorSurroundingEdges(op);
if (n) {
childsharp = childsharp * 0.25f / float(n);
}
// Add 3/4 of the sharpness of this crease edge
childsharp += sharpness * 0.75f;
childsharp -= 1.0f;
if (childsharp < (float) HbrHalfedge<T>::k_Smooth) {
childsharp = (float) HbrHalfedge<T>::k_Smooth;
}
subedge->SetSharpness(childsharp);
} else {
sharpness -= 1.0f;
if (sharpness < (float) HbrHalfedge<T>::k_Smooth) {
sharpness = (float) HbrHalfedge<T>::k_Smooth;
}
subedge->SetSharpness(sharpness);
}
}
template <class T>
void
HbrSubdivision<T>::AddSurroundingVerticesWithWeight(HbrMesh<T>* mesh, HbrVertex<T>* vertex, float weight, T* data) {
SmoothSubdivisionVertexOperator op(data, mesh->HasVertexEdits(), weight);
vertex->ApplyOperatorSurroundingVertices(op);
}
template <class T>
void
HbrSubdivision<T>::AddCreaseEdgesWithWeight(HbrMesh<T>* mesh, HbrVertex<T>* vertex, bool next, float weight, T* data) {
CreaseSubdivisionHalfedgeOperator op(vertex, data, mesh->HasVertexEdits(), next, weight);
vertex->ApplyOperatorSurroundingEdges(op);
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRSUBDIVISION_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,241 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_HBRVERTEXEDIT_H
#define OPENSUBDIV3_HBRVERTEXEDIT_H
#include <algorithm>
#include "../hbr/hierarchicalEdit.h"
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
template <class T> class HbrVertexEdit;
template <class T>
std::ostream& operator<<(std::ostream& out, const HbrVertexEdit<T>& path) {
out << "vertex path = (" << path.faceid << ' ';
for (int i = 0; i < path.nsubfaces; ++i) {
out << static_cast<int>(path.subfaces[i]) << ' ';
}
return out << static_cast<int>(path.vertexid) << "), edit = (" << path.edit[0] << ',' << path.edit[1] << ',' << path.edit[2] << ')';
}
template <class T>
class HbrVertexEdit : public HbrHierarchicalEdit<T> {
public:
HbrVertexEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _vertexid, int _index, int _width, bool _isP, typename HbrHierarchicalEdit<T>::Operation _op, float *_edit)
: HbrHierarchicalEdit<T>(_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<T>::Operation _op, float *_edit)
: HbrHierarchicalEdit<T>(_faceid, _nsubfaces, _subfaces), vertexid(static_cast<unsigned char>(_vertexid)), index(_index), width(_width), isP(_isP), op(_op) {
edit = new float[width];
memcpy(edit, _edit, width * sizeof(float));
}
virtual ~HbrVertexEdit() {
delete[] edit;
}
// Return the vertex id (the last element in the path)
unsigned char GetVertexID() const { return vertexid; }
friend std::ostream& operator<< <T> (std::ostream& out, const HbrVertexEdit<T>& path);
// Return index of variable this edit applies to
int GetIndex() const { return index; }
// 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<T>::Operation GetOperation() const { return op; }
virtual void ApplyEditToFace(HbrFace<T>* face) {
if (HbrHierarchicalEdit<T>::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<T>* face, HbrVertex<T>* vertex) {
if (HbrHierarchicalEdit<T>::GetNSubfaces() == face->GetDepth() &&
face->GetVertex(vertexid) == vertex) {
vertex->GetData().ApplyVertexEdit(*const_cast<const HbrVertexEdit<T>*>(this));
}
}
#ifdef PRMAN
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<T>::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<T>::Add ||
op == HbrHierarchicalEdit<T>::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
private:
const unsigned char vertexid;
int index;
int width;
unsigned isP:1;
typename HbrHierarchicalEdit<T>::Operation op;
float* edit;
};
template <class T>
class HbrMovingVertexEdit : public HbrHierarchicalEdit<T> {
public:
HbrMovingVertexEdit(int _faceid, int _nsubfaces, unsigned char *_subfaces, unsigned char _vertexid, int _index, int _width, bool _isP, typename HbrHierarchicalEdit<T>::Operation _op, float *_edit)
: HbrHierarchicalEdit<T>(_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<T>::Operation _op, float *_edit)
: HbrHierarchicalEdit<T>(_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;
}
// Return the vertex id (the last element in the path)
unsigned char GetVertexID() const { return vertexid; }
friend std::ostream& operator<< <T> (std::ostream& out, const HbrVertexEdit<T>& path);
// Return index of variable this edit applies to
int GetIndex() const { return index; }
// 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<T>::Operation GetOperation() const { return op; }
virtual void ApplyEditToFace(HbrFace<T>* face) {
if (HbrHierarchicalEdit<T>::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<T>* face, HbrVertex<T>* vertex) {
if (HbrHierarchicalEdit<T>::GetNSubfaces() == face->GetDepth() &&
face->GetVertex(vertexid) == vertex) {
vertex->GetData().ApplyMovingVertexEdit(*const_cast<const HbrMovingVertexEdit<T>*>(this));
}
}
#ifdef PRMAN
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<T>::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<T>::Add ||
op == HbrVertexEdit<T>::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
private:
const unsigned char vertexid;
int index;
int width;
unsigned isP:1;
typename HbrHierarchicalEdit<T>::Operation op;
float* edit;
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_HBRVERTEXEDIT_H */

View File

@@ -0,0 +1,45 @@
#
# Copyright 2013 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
# This file is used by the Android NDK to allow OpenSubdiv libraries
# to be imported by client modules.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE = OpenSubdivOsdCPU
LOCAL_SRC_FILES = libs/armeabi-v7a/libosdCPU.so
LOCAL_EXPORT_C_INCLUDES = $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE = OpenSubdivOsdGPU
LOCAL_SRC_FILES = libs/armeabi-v7a/libosdGPU.so
LOCAL_EXPORT_C_INCLUDES = $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)

View File

@@ -0,0 +1,425 @@
#
# Copyright 2013 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# source & headers
set(CPU_SOURCE_FILES
cpuEvaluator.cpp
cpuKernel.cpp
cpuPatchTable.cpp
cpuVertexBuffer.cpp
)
set(GPU_SOURCE_FILES )
set(INC_FILES )
set(PRIVATE_HEADER_FILES
cpuKernel.h
)
set(PUBLIC_HEADER_FILES
bufferDescriptor.h
cpuEvaluator.h
cpuPatchTable.h
cpuVertexBuffer.h
mesh.h
nonCopyable.h
opengl.h
types.h
)
list(APPEND KERNEL_FILES
patchBasisCommonTypes.h
patchBasisCommon.h
patchBasisCommonEval.h
)
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
#-------------------------------------------------------------------------------
set(OPENMP_PUBLIC_HEADERS
ompEvaluator.h
ompKernel.h
)
if(OPENMP_FOUND )
list(APPEND CPU_SOURCE_FILES
ompEvaluator.cpp
ompKernel.cpp
)
list(APPEND PUBLIC_HEADER_FILES ${OPENMP_PUBLIC_HEADERS})
if (CMAKE_COMPILER_IS_GNUCXX)
list(APPEND PLATFORM_CPU_LIBRARIES gomp)
endif()
endif()
list(APPEND DOXY_HEADER_FILES ${OPENMP_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
set(TBB_PUBLIC_HEADERS
tbbEvaluator.h
tbbKernel.h
)
if( TBB_FOUND )
include_directories("${TBB_INCLUDE_DIR}")
list(APPEND CPU_SOURCE_FILES
tbbEvaluator.cpp
tbbKernel.cpp
)
list(APPEND PUBLIC_HEADER_FILES ${TBB_PUBLIC_HEADERS})
if (CMAKE_COMPILER_IS_GNUCXX)
list(APPEND PLATFORM_CPU_LIBRARIES gomp)
endif()
list(APPEND PLATFORM_CPU_LIBRARIES
${TBB_LIBRARIES}
)
endif()
list(APPEND DOXY_HEADER_FILES ${TBB_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
# GL code & dependencies
set(GL_PUBLIC_HEADERS
cpuGLVertexBuffer.h
glLegacyGregoryPatchTable.h
glPatchTable.h
glVertexBuffer.h
glMesh.h
glslPatchShaderSource.h
)
if( (NOT NO_OPENGL) AND (OPENGL_FOUND OR OPENGLES_FOUND) )
list(APPEND GPU_SOURCE_FILES
cpuGLVertexBuffer.cpp
glLegacyGregoryPatchTable.cpp
glPatchTable.cpp
glVertexBuffer.cpp
glslPatchShaderSource.cpp
)
list(APPEND PUBLIC_HEADER_FILES ${GL_PUBLIC_HEADERS})
if ( OPENGL_FOUND )
list(APPEND KERNEL_FILES
glslPatchCommon.glsl
glslPatchCommonTess.glsl
glslPatchBoxSplineTriangle.glsl
glslPatchBSpline.glsl
glslPatchGregory.glsl
glslPatchGregoryBasis.glsl
glslPatchGregoryTriangle.glsl
glslPatchLegacy.glsl
)
endif()
endif()
list(APPEND DOXY_HEADER_FILES ${GL_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
# OpenGL 4.2 dependencies
# note : (GLSL transform feedback kernels require GL 4.2)
set(GL_4_2_PUBLIC_HEADERS
glXFBEvaluator.h
)
if( OPENGL_4_2_FOUND )
list(APPEND GPU_SOURCE_FILES
glXFBEvaluator.cpp
)
list(APPEND PUBLIC_HEADER_FILES ${GL_4_2_PUBLIC_HEADERS})
list(APPEND KERNEL_FILES
glslXFBKernel.glsl
)
list(APPEND PLATFORM_GPU_LIBRARIES
${OPENGL_LOADER_LIBRARIES}
)
endif()
list(APPEND DOXY_HEADER_FILES ${GL_4_2_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
# OpenGL 4.3 dependencies
# note : (GLSL compute shader kernels require GL 4.3)
set(GL_4_3_PUBLIC_HEADERS
glComputeEvaluator.h
)
if( OPENGL_4_3_FOUND )
list(APPEND GPU_SOURCE_FILES
glComputeEvaluator.cpp
)
list(APPEND PUBLIC_HEADER_FILES ${GL_4_3_PUBLIC_HEADERS})
list(APPEND KERNEL_FILES
glslComputeKernel.glsl
)
list(APPEND PLATFORM_GPU_LIBRARIES
${OPENGL_LOADER_LIBRARIES}
)
endif()
list(APPEND DOXY_HEADER_FILES ${GL_4_3_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
# DX11 code & dependencies
set(DXSDK_PUBLIC_HEADERS
cpuD3D11VertexBuffer.h
d3d11ComputeEvaluator.h
d3d11LegacyGregoryPatchTable.h
d3d11PatchTable.h
d3d11VertexBuffer.h
d3d11Mesh.h
hlslPatchShaderSource.h
)
if( DXSDK_FOUND )
list(APPEND GPU_SOURCE_FILES
cpuD3D11VertexBuffer.cpp
d3d11ComputeEvaluator.cpp
d3d11LegacyGregoryPatchTable.cpp
d3d11PatchTable.cpp
d3d11VertexBuffer.cpp
hlslPatchShaderSource.cpp
)
list(APPEND PUBLIC_HEADER_FILES ${DXSDK_PUBLIC_HEADERS})
list(APPEND KERNEL_FILES
hlslComputeKernel.hlsl
hlslPatchCommon.hlsl
hlslPatchCommonTess.hlsl
hlslPatchBoxSplineTriangle.hlsl
hlslPatchBSpline.hlsl
hlslPatchGregory.hlsl
hlslPatchGregoryBasis.hlsl
hlslPatchGregoryTriangle.hlsl
hlslPatchLegacy.hlsl
)
list(APPEND PLATFORM_GPU_LIBRARIES
${DXSDK_LIBRARIES}
)
endif()
list(APPEND DOXY_HEADER_FILES ${DXSDK_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
# Metal code & dependencies
set(METAL_PUBLIC_HEADERS
mtlVertexBuffer.h
mtlComputeEvaluator.h
mtlLegacyGregoryPatchTable.h
mtlPatchTable.h
mtlMesh.h
mtlPatchShaderSource.h
mtlCommon.h
)
if( METAL_FOUND )
set(METAL_SOURCE_FILES
mtlVertexBuffer.mm
mtlComputeEvaluator.mm
mtlLegacyGregoryPatchTable.mm
mtlPatchTable.mm
mtlVertexBuffer.mm
mtlPatchShaderSource.mm
)
set_source_files_properties(
${METAL_SOURCE_FILES}
PROPERTIES
COMPILE_FLAGS
"-fobjc-arc")
list(APPEND GPU_SOURCE_FILES ${METAL_SOURCE_FILES})
list(APPEND PUBLIC_HEADER_FILES ${METAL_PUBLIC_HEADERS})
list(APPEND KERNEL_FILES
mtlComputeKernel.metal
mtlPatchCommon.metal
mtlPatchCommonTess.metal
mtlPatchBoxSplineTriangle.metal
mtlPatchBSpline.metal
mtlPatchGregory.metal
mtlPatchGregoryBasis.metal
mtlPatchGregoryTriangle.metal
mtlPatchLegacy.metal
)
list(APPEND PLATFORM_GPU_LIBRARIES
${METAL_LIBRARIES}
)
endif()
list(APPEND DOXY_HEADER_FILES ${METAL_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
# OpenCL code & dependencies
set(OPENCL_PUBLIC_HEADERS
clEvaluator.h
clPatchTable.h
clVertexBuffer.h
opencl.h
)
if ( OPENCL_FOUND )
list(APPEND GPU_SOURCE_FILES
clEvaluator.cpp
clPatchTable.cpp
clVertexBuffer.cpp
)
list(APPEND PUBLIC_HEADER_FILES ${OPENCL_PUBLIC_HEADERS})
list(APPEND KERNEL_FILES
clKernel.cl
)
list(APPEND PLATFORM_GPU_LIBRARIES
${OPENCL_LIBRARIES}
)
include_directories( "${OPENCL_INCLUDE_DIRS}" )
if ( OPENGL_FOUND )
list(APPEND GPU_SOURCE_FILES clGLVertexBuffer.cpp)
list(APPEND PUBLIC_HEADER_FILES clGLVertexBuffer.h)
endif()
if ( OPENCL_D3D11_INTEROP_FOUND )
list(APPEND GPU_SOURCE_FILES
clD3D11VertexBuffer.cpp
)
list(APPEND PUBLIC_HEADER_FILES
clD3D11VertexBuffer.h
)
endif()
endif()
list(APPEND DOXY_HEADER_FILES ${OPENCL_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
# CUDA code & dependencies
set(CUDA_PUBLIC_HEADERS
cudaEvaluator.h
cudaPatchTable.h
cudaVertexBuffer.h
)
if( CUDA_FOUND )
list(APPEND GPU_SOURCE_FILES
cudaEvaluator.cpp
cudaPatchTable.cpp
cudaVertexBuffer.cpp
)
list(APPEND PUBLIC_HEADER_FILES ${CUDA_PUBLIC_HEADERS})
list(APPEND CUDA_KERNEL_FILES
osd/cudaKernel.cu
)
set (CUDA_KERNEL_FILES ${CUDA_KERNEL_FILES} PARENT_SCOPE)
if ( OPENGL_FOUND )
list(APPEND GPU_SOURCE_FILES
cudaGLVertexBuffer.cpp
)
list(APPEND PUBLIC_HEADER_FILES
cudaGLVertexBuffer.h
)
endif()
if( DXSDK_FOUND )
list(APPEND GPU_SOURCE_FILES
cudaD3D11VertexBuffer.cpp
)
list(APPEND PUBLIC_HEADER_FILES
cudaD3D11VertexBuffer.h
)
endif()
endif()
list(APPEND DOXY_HEADER_FILES ${CUDA_PUBLIC_HEADERS})
#-------------------------------------------------------------------------------
osd_stringify("${KERNEL_FILES}" INC_FILES)
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
#-------------------------------------------------------------------------------
source_group("Kernels" FILES ${KERNEL_FILES})
source_group("Inc" FILES ${INC_FILES})
# Compile objs first for both the CPU and GPU libs -----
add_library(osd_cpu_obj
OBJECT
${CPU_SOURCE_FILES}
${PRIVATE_HEADER_FILES}
${PUBLIC_HEADER_FILES}
)
set_target_properties(osd_cpu_obj
PROPERTIES
FOLDER "opensubdiv"
)
if( GPU_SOURCE_FILES )
add_library(osd_gpu_obj
OBJECT
${GPU_SOURCE_FILES}
${PRIVATE_HEADER_FILES}
${PUBLIC_HEADER_FILES}
${INC_FILES}
)
set_target_properties(osd_gpu_obj
PROPERTIES
FOLDER "opensubdiv"
)
endif()
osd_add_doxy_headers( "${DOXY_HEADER_FILES}" )
install(
FILES
${PUBLIC_HEADER_FILES}
DESTINATION
"${CMAKE_INCDIR_BASE}/osd"
PERMISSIONS
OWNER_READ
GROUP_READ
WORLD_READ )
#if (ANDROID)
# install(
# FILES
# Android.mk
# DESTINATION
# "${LIBRARY_OUTPUT_PATH_ROOT}"
# PERMISSIONS
# OWNER_READ
# GROUP_READ
# WORLD_READ )
#endif()
#-------------------------------------------------------------------------------

View File

@@ -0,0 +1,112 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_BUFFER_DESCRIPTOR_H
#define OPENSUBDIV3_OSD_BUFFER_DESCRIPTOR_H
#include "../version.h"
#include <string.h>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
/// \brief BufferDescriptor is a struct which describes buffer elements in
/// interleaved data buffers. Almost all Osd Evaluator APIs take
/// BufferDescriptors along with device-specific buffer objects.
///
/// The offset of BufferDescriptor can also be used to express a
/// batching offset if the data buffer is combined across multiple
/// objects together.
///
/// * Note that each element has the same data type (float)
///
// example:
// n
// -----+----------------------------------------+-------------------------
// | vertex 0 |
// -----+----------------------------------------+-------------------------
// | X Y Z R G B A Xu Yu Zu Xv Yv Zv |
// -----+----------------------------------------+-------------------------
// <------------- stride = 13 -------------->
//
// - XYZ (offset = n+0, length = 3, stride = 13)
// - RGBA (offset = n+3, length = 4, stride = 13)
// - uTangent (offset = n+7, length = 3, stride = 13)
// - vTangent (offset = n+10, length = 3, stride = 13)
//
struct BufferDescriptor {
/// Default Constructor
BufferDescriptor() : offset(0), length(0), stride(0) { }
/// Constructor
BufferDescriptor(int o, int l, int s) : offset(o), length(l), stride(s) { }
/// Returns the relative offset within a stride
int GetLocalOffset() const {
return stride > 0 ? offset % stride : 0;
}
/// True if the descriptor values are internally consistent
bool IsValid() const {
return ((length > 0) &&
(length <= stride - GetLocalOffset()));
}
/// Resets the descriptor to default
void Reset() {
offset = length = stride = 0;
}
/// True if the descriptors are identical
bool operator == (BufferDescriptor const &other) const {
return (offset == other.offset &&
length == other.length &&
stride == other.stride);
}
/// True if the descriptors are not identical
bool operator != (BufferDescriptor const &other) const {
return !(this->operator==(other));
}
/// offset to desired element data
int offset;
/// number or length of the data
int length;
/// stride to the next element
int stride;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_BUFFER_DESCRIPTOR_H

View File

@@ -0,0 +1,233 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/clD3D11VertexBuffer.h"
#include <cassert>
#include <D3D11.h>
#if defined(OPENSUBDIV_HAS_CL_D3D11_H)
#include <CL/cl_d3d11.h>
#elif defined(OPENSUBDIV_HAS_CL_D3D11_EXT_H)
#include <CL/cl_d3d11_ext.h>
#else
#error "d3d11.h or d3d11_ext.h must be found in cmake"
#endif
#include "../far/error.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
#if defined(OPENSUBDIV_HAS_CL_D3D11_H)
typedef clCreateFromD3D11BufferKHR_fn clCreateFromD3D11Buffer_fn;
typedef clEnqueueAcquireD3D11ObjectsKHR_fn clEnqueueAcquireD3D11Objects_fn;
typedef clEnqueueReleaseD3D11ObjectsKHR_fn clEnqueueReleaseD3D11Objects_fn;
#else
typedef clCreateFromD3D11BufferNV_fn clCreateFromD3D11Buffer_fn;
typedef clEnqueueAcquireD3D11ObjectsNV_fn clEnqueueAcquireD3D11Objects_fn;
typedef clEnqueueReleaseD3D11ObjectsNV_fn clEnqueueReleaseD3D11Objects_fn;
#endif
static clCreateFromD3D11Buffer_fn clCreateFromD3D11Buffer = NULL;
static clEnqueueAcquireD3D11Objects_fn clEnqueueAcquireD3D11Objects = NULL;
static clEnqueueReleaseD3D11Objects_fn clEnqueueReleaseD3D11Objects = NULL;
// XXX: clGetExtensionFunctionAddress is marked as deprecated and
// clGetExtensionFunctionAddressForPlatform should be used,
// however it requires OpenCL 1.2.
// until we bump the requirement to 1.2, mute the deprecated warning.
#if defined(_MSC_VER)
#pragma warning(disable: 4996)
#endif
static void resolveInteropFunctions() {
if (! clCreateFromD3D11Buffer) {
clCreateFromD3D11Buffer =
(clCreateFromD3D11Buffer_fn)
clGetExtensionFunctionAddress("clCreateFromD3D11BufferKHR");
}
if (! clCreateFromD3D11Buffer) {
clCreateFromD3D11Buffer =
(clCreateFromD3D11Buffer_fn)
clGetExtensionFunctionAddress("clCreateFromD3D11BufferNV");
}
if (! clEnqueueAcquireD3D11Objects) {
clEnqueueAcquireD3D11Objects = (clEnqueueAcquireD3D11Objects_fn)
clGetExtensionFunctionAddress("clEnqueueAcquireD3D11ObjectsKHR");
}
if (! clEnqueueAcquireD3D11Objects) {
clEnqueueAcquireD3D11Objects = (clEnqueueAcquireD3D11Objects_fn)
clGetExtensionFunctionAddress("clEnqueueAcquireD3D11ObjectsNV");
}
if (! clEnqueueReleaseD3D11Objects) {
clEnqueueReleaseD3D11Objects = (clEnqueueReleaseD3D11Objects_fn)
clGetExtensionFunctionAddress("clEnqueueReleaseD3D11ObjectsKHR");
}
if (! clEnqueueReleaseD3D11Objects) {
clEnqueueReleaseD3D11Objects = (clEnqueueReleaseD3D11Objects_fn)
clGetExtensionFunctionAddress("clEnqueueReleaseD3D11ObjectsNV");
}
}
CLD3D11VertexBuffer::CLD3D11VertexBuffer(int numElements, int numVertices)
: _numElements(numElements), _numVertices(numVertices),
_d3d11Buffer(NULL), _clMemory(NULL), _clQueue(NULL), _clMapped(false) {
}
CLD3D11VertexBuffer::~CLD3D11VertexBuffer() {
unmap();
clReleaseMemObject(_clMemory);
_d3d11Buffer->Release();
}
CLD3D11VertexBuffer *
CLD3D11VertexBuffer::Create(int numElements, int numVertices,
cl_context clContext,
ID3D11DeviceContext *deviceContext) {
CLD3D11VertexBuffer *instance =
new CLD3D11VertexBuffer(numElements, numVertices);
ID3D11Device *device;
deviceContext->GetDevice(&device);
if (instance->allocate(clContext, device)) return instance;
delete instance;
return NULL;
}
void
CLD3D11VertexBuffer::UpdateData(const float *src, int startVertex,
int numVertices, cl_command_queue queue) {
size_t size = numVertices * _numElements * sizeof(float);
size_t offset = startVertex * _numElements * sizeof(float);
map(queue);
clEnqueueWriteBuffer(queue, _clMemory, true, offset, size, src, 0, NULL, NULL);
}
int
CLD3D11VertexBuffer::GetNumElements() const {
return _numElements;
}
int
CLD3D11VertexBuffer::GetNumVertices() const {
return _numVertices;
}
cl_mem
CLD3D11VertexBuffer::BindCLBuffer(cl_command_queue queue) {
map(queue);
return _clMemory;
}
ID3D11Buffer *
CLD3D11VertexBuffer::BindD3D11Buffer(ID3D11DeviceContext *deviceContext) {
unmap();
return _d3d11Buffer;
}
bool
CLD3D11VertexBuffer::allocate(cl_context clContext, ID3D11Device *device) {
D3D11_BUFFER_DESC hBufferDesc;
hBufferDesc.ByteWidth = _numElements * _numVertices * sizeof(float);
hBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
hBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_SHADER_RESOURCE;
hBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hBufferDesc.MiscFlags = 0;
hBufferDesc.StructureByteStride = sizeof(float);
HRESULT hr;
hr = device->CreateBuffer(&hBufferDesc, NULL, &_d3d11Buffer);
if (FAILED(hr)) {
Far::Error(Far::FAR_RUNTIME_ERROR, "Fail in CreateBuffer\n");
return false;
}
if (! clCreateFromD3D11Buffer) {
resolveInteropFunctions();
if (! clCreateFromD3D11Buffer) {
return false;
}
}
// register d3d11buffer as cl memory
cl_int err;
_clMemory = clCreateFromD3D11Buffer(clContext, CL_MEM_READ_WRITE, _d3d11Buffer, &err);
if (err != CL_SUCCESS) return false;
return true;
}
void
CLD3D11VertexBuffer::map(cl_command_queue queue) {
if (_clMapped) return;
_clQueue = queue;
if (! clEnqueueAcquireD3D11Objects) {
resolveInteropFunctions();
if (! clEnqueueAcquireD3D11Objects) {
return;
}
}
clEnqueueAcquireD3D11Objects(queue, 1, &_clMemory, 0, 0, 0);
_clMapped = true;
}
void
CLD3D11VertexBuffer::unmap() {
if (! _clMapped) return;
if (! clEnqueueReleaseD3D11Objects) {
resolveInteropFunctions();
if (! clEnqueueReleaseD3D11Objects) {
return;
}
}
clEnqueueReleaseD3D11Objects(_clQueue, 1, &_clMemory, 0, 0, 0);
_clMapped = false;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,143 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CL_D3D11_VERTEX_BUFFER_H
#define OPENSUBDIV3_OSD_CL_D3D11_VERTEX_BUFFER_H
#include "../version.h"
#include "../osd/opencl.h"
struct ID3D11VertexShader;
struct ID3D11HullShader;
struct ID3D11DomainShader;
struct ID3D11GeometryShader;
struct ID3D11PixelShader;
struct ID3D11Buffer;
struct ID3D11ShaderResourceView;
struct ID3D11Device;
struct ID3D11DeviceContext;
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
///
/// \brief Concrete vertex buffer class for OpenCL subdivision and DirectX
/// drawing.
///
/// D3D11VertexBuffer implements CLVertexBufferInterface and
/// D3D11VertexBufferInterface.
///
/// An instance of this buffer class can be passed to D3D11ComputeEvaluator.
///
class CLD3D11VertexBuffer {
public:
/// Creator. Returns NULL if error.
static CLD3D11VertexBuffer * Create(int numElements, int numVertices,
cl_context clContext,
ID3D11DeviceContext *deviceContext);
/// template version for custom context (OpenCL) used by OsdMesh
template<typename DEVICE_CONTEXT>
static CLD3D11VertexBuffer * Create(int numElements, int numVertices,
DEVICE_CONTEXT context) {
return Create(numElements, numVertices,
context->GetContext(),
context->GetDeviceContext());
}
/// Destructor.
virtual ~CLD3D11VertexBuffer();
/// This method is meant to be used in client code in order to provide coarse
/// vertices data to Osd.
void UpdateData(const float *src, int startVertex, int numVertices,
cl_command_queue clQueue);
/// template version for custom context (OpenCL) used by OsdMesh
template<typename DEVICE_CONTEXT>
void UpdateData(const float *src, int startVertex, int numVertices,
DEVICE_CONTEXT context) {
UpdateData(src, startVertex, numVertices, context->GetCommandQueue());
}
/// Returns how many elements defined in this vertex buffer.
int GetNumElements() const;
/// Returns how many vertices allocated in this vertex buffer.
int GetNumVertices() const;
/// Returns the CL memory object.
cl_mem BindCLBuffer(cl_command_queue queue);
/// Returns the D3D11 buffer object.
ID3D11Buffer *BindD3D11Buffer(ID3D11DeviceContext *deviceContext);
/// Returns the D3D11 buffer object (for Osd::Mesh interface)
ID3D11Buffer *BindVBO(ID3D11DeviceContext *deviceContext) {
return BindD3D11Buffer(deviceContext);
}
/// template version for custom context (OpenCL) used by OsdMesh
template<typename DEVICE_CONTEXT>
ID3D11Buffer *BindVBO(DEVICE_CONTEXT context) {
return BindD3D11Buffer(context->GetDeviceContext());
}
protected:
/// Constructor.
CLD3D11VertexBuffer(int numElements, int numVertices);
/// Allocates this buffer and registers as a cl resource.
/// Returns true if success.
bool allocate(cl_context clContext, ID3D11Device *device);
/// Acquire a resource from DirectX.
void map(cl_command_queue queue);
/// Releases a resource to DirectX.
void unmap();
private:
int _numElements;
int _numVertices;
ID3D11Buffer *_d3d11Buffer;
cl_command_queue _clQueue;
cl_mem _clMemory;
bool _clMapped;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CL_D3D11_VERTEX_BUFFER_H

View File

@@ -0,0 +1,541 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/clEvaluator.h"
#include <sstream>
#include <string>
#include <vector>
#include <cstdio>
#include "../osd/opencl.h"
#include "../far/error.h"
#include "../far/stencilTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
static const char *clSource =
#include "clKernel.gen.h"
;
static const char *patchBasisTypesSource =
#include "patchBasisCommonTypes.gen.h"
;
static const char *patchBasisSource =
#include "patchBasisCommon.gen.h"
;
static const char *patchBasisEvalSource =
#include "patchBasisCommonEval.gen.h"
;
// ----------------------------------------------------------------------------
template <class T> cl_mem
createCLBuffer(std::vector<T> const & src, cl_context clContext) {
if (src.empty()) {
return NULL;
}
cl_int errNum = 0;
cl_mem devicePtr = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
src.size()*sizeof(T),
(void*)(&src.at(0)),
&errNum);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", errNum);
}
return devicePtr;
}
// ----------------------------------------------------------------------------
CLStencilTable::CLStencilTable(Far::StencilTable const *stencilTable,
cl_context clContext) {
_numStencils = stencilTable->GetNumStencils();
if (_numStencils > 0) {
_sizes = createCLBuffer(stencilTable->GetSizes(), clContext);
_offsets = createCLBuffer(stencilTable->GetOffsets(), clContext);
_indices = createCLBuffer(stencilTable->GetControlIndices(),
clContext);
_weights = createCLBuffer(stencilTable->GetWeights(), clContext);
_duWeights = _dvWeights = NULL;
_duuWeights = _duvWeights = _dvvWeights = NULL;
} else {
_sizes = _offsets = _indices = _weights = NULL;
_duWeights = _dvWeights = NULL;
_duuWeights = _duvWeights = _dvvWeights = NULL;
}
}
CLStencilTable::CLStencilTable(Far::LimitStencilTable const *limitStencilTable,
cl_context clContext) {
_numStencils = limitStencilTable->GetNumStencils();
if (_numStencils > 0) {
_sizes = createCLBuffer(limitStencilTable->GetSizes(), clContext);
_offsets = createCLBuffer(limitStencilTable->GetOffsets(), clContext);
_indices = createCLBuffer(limitStencilTable->GetControlIndices(),
clContext);
_weights = createCLBuffer(limitStencilTable->GetWeights(), clContext);
_duWeights = createCLBuffer(
limitStencilTable->GetDuWeights(), clContext);
_dvWeights = createCLBuffer(
limitStencilTable->GetDvWeights(), clContext);
_duuWeights = createCLBuffer(
limitStencilTable->GetDuuWeights(), clContext);
_duvWeights = createCLBuffer(
limitStencilTable->GetDuvWeights(), clContext);
_dvvWeights = createCLBuffer(
limitStencilTable->GetDvvWeights(), clContext);
} else {
_sizes = _offsets = _indices = _weights = NULL;
_duWeights = _dvWeights = NULL;
_duuWeights = _duvWeights = _dvvWeights = NULL;
}
}
CLStencilTable::~CLStencilTable() {
if (_sizes) clReleaseMemObject(_sizes);
if (_offsets) clReleaseMemObject(_offsets);
if (_indices) clReleaseMemObject(_indices);
if (_weights) clReleaseMemObject(_weights);
if (_duWeights) clReleaseMemObject(_duWeights);
if (_dvWeights) clReleaseMemObject(_dvWeights);
if (_duuWeights) clReleaseMemObject(_duuWeights);
if (_duvWeights) clReleaseMemObject(_duvWeights);
if (_dvvWeights) clReleaseMemObject(_dvvWeights);
}
// ---------------------------------------------------------------------------
CLEvaluator::CLEvaluator(cl_context context, cl_command_queue queue)
: _clContext(context), _clCommandQueue(queue),
_program(NULL), _stencilKernel(NULL), _stencilDerivKernel(NULL),
_patchKernel(NULL) {
}
CLEvaluator::~CLEvaluator() {
if (_stencilKernel) clReleaseKernel(_stencilKernel);
if (_stencilDerivKernel) clReleaseKernel(_stencilDerivKernel);
if (_patchKernel) clReleaseKernel(_patchKernel);
if (_program) clReleaseProgram(_program);
}
bool
CLEvaluator::Compile(BufferDescriptor const &srcDesc,
BufferDescriptor const &dstDesc,
BufferDescriptor const & /*duDesc*/,
BufferDescriptor const & /*dvDesc*/,
BufferDescriptor const & /*duuDesc*/,
BufferDescriptor const & /*duvDesc*/,
BufferDescriptor const & /*dvvDesc*/) {
if (srcDesc.length > dstDesc.length) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"srcDesc length must be less than or equal to "
"dstDesc length.\n");
return false;
}
cl_int errNum;
std::ostringstream defines;
defines << "#define LENGTH " << srcDesc.length << "\n"
<< "#define SRC_STRIDE " << srcDesc.stride << "\n"
<< "#define DST_STRIDE " << dstDesc.stride << "\n"
<< "#define OSD_PATCH_BASIS_OPENCL\n";
std::string defineStr = defines.str();
const char *sources[] = { defineStr.c_str(),
patchBasisTypesSource,
patchBasisSource,
patchBasisEvalSource,
clSource };
_program = clCreateProgramWithSource(_clContext, 5, sources, 0, &errNum);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"clCreateProgramWithSource (%d)", errNum);
}
errNum = clBuildProgram(_program, 0, NULL, NULL, NULL, NULL);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clBuildProgram (%d) \n", errNum);
cl_int numDevices = 0;
clGetContextInfo(
_clContext, CL_CONTEXT_NUM_DEVICES,
sizeof(cl_uint), &numDevices, NULL);
cl_device_id *devices = new cl_device_id[numDevices];
clGetContextInfo(_clContext, CL_CONTEXT_DEVICES,
sizeof(cl_device_id)*numDevices, devices, NULL);
for (int i = 0; i < numDevices; ++i) {
char cBuildLog[10240];
clGetProgramBuildInfo(
_program, devices[i],
CL_PROGRAM_BUILD_LOG, sizeof(cBuildLog), cBuildLog, NULL);
Far::Error(Far::FAR_RUNTIME_ERROR, cBuildLog);
}
delete[] devices;
return false;
}
_stencilKernel = clCreateKernel(_program, "computeStencils", &errNum);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "buildKernel (%d)\n", errNum);
return false;
}
_stencilDerivKernel = clCreateKernel(_program,
"computeStencilsDerivatives", &errNum);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "buildKernel (%d)\n", errNum);
return false;
}
_patchKernel = clCreateKernel(_program, "computePatches", &errNum);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "buildKernel (%d)\n", errNum);
return false;
}
return true;
}
bool
CLEvaluator::EvalStencils(cl_mem src, BufferDescriptor const &srcDesc,
cl_mem dst, BufferDescriptor const &dstDesc,
cl_mem sizes,
cl_mem offsets,
cl_mem indices,
cl_mem weights,
int start, int end,
unsigned int numStartEvents,
const cl_event* startEvents,
cl_event* endEvent) const {
if (end <= start) return true;
size_t globalWorkSize = (size_t)(end - start);
clSetKernelArg(_stencilKernel, 0, sizeof(cl_mem), &src);
clSetKernelArg(_stencilKernel, 1, sizeof(int), &srcDesc.offset);
clSetKernelArg(_stencilKernel, 2, sizeof(cl_mem), &dst);
clSetKernelArg(_stencilKernel, 3, sizeof(int), &dstDesc.offset);
clSetKernelArg(_stencilKernel, 4, sizeof(cl_mem), &sizes);
clSetKernelArg(_stencilKernel, 5, sizeof(cl_mem), &offsets);
clSetKernelArg(_stencilKernel, 6, sizeof(cl_mem), &indices);
clSetKernelArg(_stencilKernel, 7, sizeof(cl_mem), &weights);
clSetKernelArg(_stencilKernel, 8, sizeof(int), &start);
clSetKernelArg(_stencilKernel, 9, sizeof(int), &end);
cl_int errNum = clEnqueueNDRangeKernel(
_clCommandQueue, _stencilKernel, 1, NULL,
&globalWorkSize, NULL, numStartEvents, startEvents, endEvent);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"ApplyStencilKernel (%d) ", errNum);
return false;
}
if (endEvent == NULL)
{
clFinish(_clCommandQueue);
}
return true;
}
bool
CLEvaluator::EvalStencils(cl_mem src, BufferDescriptor const &srcDesc,
cl_mem dst, BufferDescriptor const &dstDesc,
cl_mem du, BufferDescriptor const &duDesc,
cl_mem dv, BufferDescriptor const &dvDesc,
cl_mem sizes,
cl_mem offsets,
cl_mem indices,
cl_mem weights,
cl_mem duWeights,
cl_mem dvWeights,
int start, int end,
unsigned int numStartEvents,
const cl_event* startEvents,
cl_event* endEvent) const {
if (end <= start) return true;
size_t globalWorkSize = (size_t)(end - start);
BufferDescriptor empty;
clSetKernelArg(_stencilDerivKernel, 0, sizeof(cl_mem), &src);
clSetKernelArg(_stencilDerivKernel, 1, sizeof(int), &srcDesc.offset);
clSetKernelArg(_stencilDerivKernel, 2, sizeof(cl_mem), &dst);
clSetKernelArg(_stencilDerivKernel, 3, sizeof(int), &dstDesc.offset);
clSetKernelArg(_stencilDerivKernel, 4, sizeof(cl_mem), &du);
clSetKernelArg(_stencilDerivKernel, 5, sizeof(int), &duDesc.offset);
clSetKernelArg(_stencilDerivKernel, 6, sizeof(int), &duDesc.stride);
clSetKernelArg(_stencilDerivKernel, 7, sizeof(cl_mem), &dv);
clSetKernelArg(_stencilDerivKernel, 8, sizeof(int), &dvDesc.offset);
clSetKernelArg(_stencilDerivKernel, 9, sizeof(int), &dvDesc.stride);
clSetKernelArg(_stencilDerivKernel, 10, sizeof(cl_mem), NULL);
clSetKernelArg(_stencilDerivKernel, 11, sizeof(int), &empty.offset);
clSetKernelArg(_stencilDerivKernel, 12, sizeof(int), &empty.stride);
clSetKernelArg(_stencilDerivKernel, 13, sizeof(cl_mem), NULL);
clSetKernelArg(_stencilDerivKernel, 14, sizeof(int), &empty.offset);
clSetKernelArg(_stencilDerivKernel, 15, sizeof(int), &empty.stride);
clSetKernelArg(_stencilDerivKernel, 16, sizeof(cl_mem), NULL);
clSetKernelArg(_stencilDerivKernel, 17, sizeof(int), &empty.offset);
clSetKernelArg(_stencilDerivKernel, 18, sizeof(int), &empty.stride);
clSetKernelArg(_stencilDerivKernel, 19, sizeof(cl_mem), &sizes);
clSetKernelArg(_stencilDerivKernel, 20, sizeof(cl_mem), &offsets);
clSetKernelArg(_stencilDerivKernel, 21, sizeof(cl_mem), &indices);
clSetKernelArg(_stencilDerivKernel, 22, sizeof(cl_mem), &weights);
clSetKernelArg(_stencilDerivKernel, 23, sizeof(cl_mem), &duWeights);
clSetKernelArg(_stencilDerivKernel, 24, sizeof(cl_mem), &dvWeights);
clSetKernelArg(_stencilDerivKernel, 25, sizeof(cl_mem), NULL);
clSetKernelArg(_stencilDerivKernel, 26, sizeof(cl_mem), NULL);
clSetKernelArg(_stencilDerivKernel, 27, sizeof(cl_mem), NULL);
clSetKernelArg(_stencilDerivKernel, 28, sizeof(int), &start);
clSetKernelArg(_stencilDerivKernel, 29, sizeof(int), &end);
cl_int errNum = clEnqueueNDRangeKernel(
_clCommandQueue, _stencilDerivKernel, 1, NULL,
&globalWorkSize, NULL, numStartEvents, startEvents, endEvent);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"ApplyStencilKernel (%d) ", errNum);
return false;
}
if (endEvent == NULL) {
clFinish(_clCommandQueue);
}
return true;
}
bool
CLEvaluator::EvalStencils(cl_mem src, BufferDescriptor const &srcDesc,
cl_mem dst, BufferDescriptor const &dstDesc,
cl_mem du, BufferDescriptor const &duDesc,
cl_mem dv, BufferDescriptor const &dvDesc,
cl_mem duu, BufferDescriptor const &duuDesc,
cl_mem duv, BufferDescriptor const &duvDesc,
cl_mem dvv, BufferDescriptor const &dvvDesc,
cl_mem sizes,
cl_mem offsets,
cl_mem indices,
cl_mem weights,
cl_mem duWeights,
cl_mem dvWeights,
cl_mem duuWeights,
cl_mem duvWeights,
cl_mem dvvWeights,
int start, int end,
unsigned int numStartEvents,
const cl_event* startEvents,
cl_event* endEvent) const {
if (end <= start) return true;
size_t globalWorkSize = (size_t)(end - start);
clSetKernelArg(_stencilDerivKernel, 0, sizeof(cl_mem), &src);
clSetKernelArg(_stencilDerivKernel, 1, sizeof(int), &srcDesc.offset);
clSetKernelArg(_stencilDerivKernel, 2, sizeof(cl_mem), &dst);
clSetKernelArg(_stencilDerivKernel, 3, sizeof(int), &dstDesc.offset);
clSetKernelArg(_stencilDerivKernel, 4, sizeof(cl_mem), &du);
clSetKernelArg(_stencilDerivKernel, 5, sizeof(int), &duDesc.offset);
clSetKernelArg(_stencilDerivKernel, 6, sizeof(int), &duDesc.stride);
clSetKernelArg(_stencilDerivKernel, 7, sizeof(cl_mem), &dv);
clSetKernelArg(_stencilDerivKernel, 8, sizeof(int), &dvDesc.offset);
clSetKernelArg(_stencilDerivKernel, 9, sizeof(int), &dvDesc.stride);
clSetKernelArg(_stencilDerivKernel, 10, sizeof(cl_mem), &duu);
clSetKernelArg(_stencilDerivKernel, 11, sizeof(int), &duuDesc.offset);
clSetKernelArg(_stencilDerivKernel, 12, sizeof(int), &duuDesc.stride);
clSetKernelArg(_stencilDerivKernel, 13, sizeof(cl_mem), &duv);
clSetKernelArg(_stencilDerivKernel, 14, sizeof(int), &duvDesc.offset);
clSetKernelArg(_stencilDerivKernel, 15, sizeof(int), &duvDesc.stride);
clSetKernelArg(_stencilDerivKernel, 16, sizeof(cl_mem), &dvv);
clSetKernelArg(_stencilDerivKernel, 17, sizeof(int), &dvvDesc.offset);
clSetKernelArg(_stencilDerivKernel, 18, sizeof(int), &dvvDesc.stride);
clSetKernelArg(_stencilDerivKernel, 19, sizeof(cl_mem), &sizes);
clSetKernelArg(_stencilDerivKernel, 20, sizeof(cl_mem), &offsets);
clSetKernelArg(_stencilDerivKernel, 21, sizeof(cl_mem), &indices);
clSetKernelArg(_stencilDerivKernel, 22, sizeof(cl_mem), &weights);
clSetKernelArg(_stencilDerivKernel, 23, sizeof(cl_mem), &duWeights);
clSetKernelArg(_stencilDerivKernel, 24, sizeof(cl_mem), &dvWeights);
clSetKernelArg(_stencilDerivKernel, 25, sizeof(cl_mem), &duuWeights);
clSetKernelArg(_stencilDerivKernel, 26, sizeof(cl_mem), &duvWeights);
clSetKernelArg(_stencilDerivKernel, 27, sizeof(cl_mem), &dvvWeights);
clSetKernelArg(_stencilDerivKernel, 28, sizeof(int), &start);
clSetKernelArg(_stencilDerivKernel, 29, sizeof(int), &end);
cl_int errNum = clEnqueueNDRangeKernel(
_clCommandQueue, _stencilDerivKernel, 1, NULL,
&globalWorkSize, NULL, numStartEvents, startEvents, endEvent);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"ApplyStencilKernel (%d) ", errNum);
return false;
}
if (endEvent == NULL) {
clFinish(_clCommandQueue);
}
return true;
}
bool
CLEvaluator::EvalPatches(cl_mem src, BufferDescriptor const &srcDesc,
cl_mem dst, BufferDescriptor const &dstDesc,
cl_mem du, BufferDescriptor const &duDesc,
cl_mem dv, BufferDescriptor const &dvDesc,
int numPatchCoords,
cl_mem patchCoordsBuffer,
cl_mem patchArrayBuffer,
cl_mem patchIndexBuffer,
cl_mem patchParamBuffer,
unsigned int numStartEvents,
const cl_event* startEvents,
cl_event* endEvent) const {
size_t globalWorkSize = (size_t)(numPatchCoords);
BufferDescriptor empty;
clSetKernelArg(_patchKernel, 0, sizeof(cl_mem), &src);
clSetKernelArg(_patchKernel, 1, sizeof(int), &srcDesc.offset);
clSetKernelArg(_patchKernel, 2, sizeof(cl_mem), &dst);
clSetKernelArg(_patchKernel, 3, sizeof(int), &dstDesc.offset);
clSetKernelArg(_patchKernel, 4, sizeof(cl_mem), &du);
clSetKernelArg(_patchKernel, 5, sizeof(int), &duDesc.offset);
clSetKernelArg(_patchKernel, 6, sizeof(int), &duDesc.stride);
clSetKernelArg(_patchKernel, 7, sizeof(cl_mem), &dv);
clSetKernelArg(_patchKernel, 8, sizeof(int), &dvDesc.offset);
clSetKernelArg(_patchKernel, 9, sizeof(int), &dvDesc.stride);
clSetKernelArg(_patchKernel, 10, sizeof(cl_mem), NULL);
clSetKernelArg(_patchKernel, 11, sizeof(int), &empty.offset);
clSetKernelArg(_patchKernel, 12, sizeof(int), &empty.stride);
clSetKernelArg(_patchKernel, 13, sizeof(cl_mem), NULL);
clSetKernelArg(_patchKernel, 14, sizeof(int), &empty.offset);
clSetKernelArg(_patchKernel, 15, sizeof(int), &empty.stride);
clSetKernelArg(_patchKernel, 16, sizeof(cl_mem), NULL);
clSetKernelArg(_patchKernel, 17, sizeof(int), &empty.offset);
clSetKernelArg(_patchKernel, 18, sizeof(int), &empty.stride);
clSetKernelArg(_patchKernel, 19, sizeof(cl_mem), &patchCoordsBuffer);
clSetKernelArg(_patchKernel, 20, sizeof(cl_mem), &patchArrayBuffer);
clSetKernelArg(_patchKernel, 21, sizeof(cl_mem), &patchIndexBuffer);
clSetKernelArg(_patchKernel, 22, sizeof(cl_mem), &patchParamBuffer);
cl_int errNum = clEnqueueNDRangeKernel(
_clCommandQueue, _patchKernel, 1, NULL,
&globalWorkSize, NULL, numStartEvents, startEvents, endEvent);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"ApplyPatchKernel (%d) ", errNum);
return false;
}
if (endEvent == NULL) {
clFinish(_clCommandQueue);
}
return true;
}
bool
CLEvaluator::EvalPatches(cl_mem src, BufferDescriptor const &srcDesc,
cl_mem dst, BufferDescriptor const &dstDesc,
cl_mem du, BufferDescriptor const &duDesc,
cl_mem dv, BufferDescriptor const &dvDesc,
cl_mem duu, BufferDescriptor const &duuDesc,
cl_mem duv, BufferDescriptor const &duvDesc,
cl_mem dvv, BufferDescriptor const &dvvDesc,
int numPatchCoords,
cl_mem patchCoordsBuffer,
cl_mem patchArrayBuffer,
cl_mem patchIndexBuffer,
cl_mem patchParamBuffer,
unsigned int numStartEvents,
const cl_event* startEvents,
cl_event* endEvent) const {
size_t globalWorkSize = (size_t)(numPatchCoords);
clSetKernelArg(_patchKernel, 0, sizeof(cl_mem), &src);
clSetKernelArg(_patchKernel, 1, sizeof(int), &srcDesc.offset);
clSetKernelArg(_patchKernel, 2, sizeof(cl_mem), &dst);
clSetKernelArg(_patchKernel, 3, sizeof(int), &dstDesc.offset);
clSetKernelArg(_patchKernel, 4, sizeof(cl_mem), &du);
clSetKernelArg(_patchKernel, 5, sizeof(int), &duDesc.offset);
clSetKernelArg(_patchKernel, 6, sizeof(int), &duDesc.stride);
clSetKernelArg(_patchKernel, 7, sizeof(cl_mem), &dv);
clSetKernelArg(_patchKernel, 8, sizeof(int), &dvDesc.offset);
clSetKernelArg(_patchKernel, 9, sizeof(int), &dvDesc.stride);
clSetKernelArg(_patchKernel, 10, sizeof(cl_mem), &duu);
clSetKernelArg(_patchKernel, 11, sizeof(int), &duuDesc.offset);
clSetKernelArg(_patchKernel, 12, sizeof(int), &duuDesc.stride);
clSetKernelArg(_patchKernel, 13, sizeof(cl_mem), &duv);
clSetKernelArg(_patchKernel, 14, sizeof(int), &duvDesc.offset);
clSetKernelArg(_patchKernel, 15, sizeof(int), &duvDesc.stride);
clSetKernelArg(_patchKernel, 16, sizeof(cl_mem), &dvv);
clSetKernelArg(_patchKernel, 17, sizeof(int), &dvvDesc.offset);
clSetKernelArg(_patchKernel, 18, sizeof(int), &dvvDesc.stride);
clSetKernelArg(_patchKernel, 19, sizeof(cl_mem), &patchCoordsBuffer);
clSetKernelArg(_patchKernel, 20, sizeof(cl_mem), &patchArrayBuffer);
clSetKernelArg(_patchKernel, 21, sizeof(cl_mem), &patchIndexBuffer);
clSetKernelArg(_patchKernel, 22, sizeof(cl_mem), &patchParamBuffer);
cl_int errNum = clEnqueueNDRangeKernel(
_clCommandQueue, _patchKernel, 1, NULL,
&globalWorkSize, NULL, numStartEvents, startEvents, endEvent);
if (errNum != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"ApplyPatchKernel (%d) ", errNum);
return false;
}
if (endEvent == NULL) {
clFinish(_clCommandQueue);
}
return true;
}
/* static */
void
CLEvaluator::Synchronize(cl_command_queue clCommandQueue) {
clFinish(clCommandQueue);
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "glLoader.h"
#include "../osd/clGLVertexBuffer.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CLGLVertexBuffer::CLGLVertexBuffer(int numElements,
int numVertices,
cl_context /* clContext */)
: _numElements(numElements), _numVertices(numVertices),
_vbo(0), _clQueue(0), _clMemory(0), _clMapped(false) {
// Initialize internal OpenGL loader library if necessary
OpenSubdiv::internal::GLLoader::libraryInitializeGL();
}
CLGLVertexBuffer::~CLGLVertexBuffer() {
unmap();
clReleaseMemObject(_clMemory);
glDeleteBuffers(1, &_vbo);
}
CLGLVertexBuffer *
CLGLVertexBuffer::Create(int numElements, int numVertices, cl_context clContext)
{
CLGLVertexBuffer *instance =
new CLGLVertexBuffer(numElements, numVertices, clContext);
if (instance->allocate(clContext)) return instance;
delete instance;
return NULL;
}
void
CLGLVertexBuffer::UpdateData(const float *src, int startVertex, int numVertices,
cl_command_queue queue) {
size_t size = numVertices * _numElements * sizeof(float);
size_t offset = startVertex * _numElements * sizeof(float);
map(queue);
clEnqueueWriteBuffer(queue, _clMemory, true, offset, size, src, 0, NULL, NULL);
}
int
CLGLVertexBuffer::GetNumElements() const {
return _numElements;
}
int
CLGLVertexBuffer::GetNumVertices() const {
return _numVertices;
}
cl_mem
CLGLVertexBuffer::BindCLBuffer(cl_command_queue queue) {
map(queue);
return _clMemory;
}
GLuint
CLGLVertexBuffer::BindVBO(void * /*deviceContext*/) {
unmap();
return _vbo;
}
bool
CLGLVertexBuffer::allocate(cl_context clContext) {
assert(clContext);
// create GL buffer first
int size = _numElements * _numVertices * sizeof(float);
#if defined(GL_ARB_direct_state_access)
if (OSD_OPENGL_HAS(ARB_direct_state_access)) {
glCreateBuffers(1, &_vbo);
glNamedBufferData(_vbo, size, 0, GL_DYNAMIC_DRAW);
} else
#endif
{
GLint prev = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prev);
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, prev);
}
// register vbo as cl memory
cl_int err;
_clMemory = clCreateFromGLBuffer(clContext,
CL_MEM_READ_WRITE, _vbo, &err);
if (err != CL_SUCCESS) return false;
return true;
}
void
CLGLVertexBuffer::map(cl_command_queue queue) {
if (_clMapped) return; // XXX: what if another queue is given?
_clQueue = queue;
clEnqueueAcquireGLObjects(queue, 1, &_clMemory, 0, 0, 0);
_clMapped = true;
}
void
CLGLVertexBuffer::unmap() {
if (! _clMapped) return;
clEnqueueReleaseGLObjects(_clQueue, 1, &_clMemory, 0, 0, 0);
_clMapped = false;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,119 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CL_GL_VERTEX_BUFFER_H
#define OPENSUBDIV3_OSD_CL_GL_VERTEX_BUFFER_H
#include "../version.h"
#include "../osd/opengl.h"
#include "../osd/opencl.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
///
/// \brief Concrete vertex buffer class for OpenCL subdivision and OpenGL drawing.
///
/// CLGLVertexBuffer implements CLVertexBufferInterface and
/// GLVertexBufferInterface.
///
/// The buffer interop between OpenCL and GL is handled automatically when a
/// client calls BindCLBuffer and BindVBO methods.
///
class CLGLVertexBuffer {
public:
/// Creator. Returns NULL if error.
static CLGLVertexBuffer * Create(int numElements,
int numVertices,
cl_context clContext);
template <typename DEVICE_CONTEXT>
static CLGLVertexBuffer * Create(int numElements, int numVertices,
DEVICE_CONTEXT context) {
return Create(numElements, numVertices, context->GetContext());
}
/// Destructor.
~CLGLVertexBuffer();
/// This method is meant to be used in client code in order to provide
/// coarse vertices data to Osd.
void UpdateData(const float *src, int startVertex, int numVertices,
cl_command_queue clQueue);
template<typename DEVICE_CONTEXT>
void UpdateData(const float *src, int startVertex, int numVertices,
DEVICE_CONTEXT context) {
UpdateData(src, startVertex, numVertices, context->GetCommandQueue());
}
/// Returns how many elements defined in this vertex buffer.
int GetNumElements() const;
/// Returns how many vertices allocated in this vertex buffer.
int GetNumVertices() const;
/// Returns the CL memory object. GL buffer will be mapped to CL memory
/// space if necessary.
cl_mem BindCLBuffer(cl_command_queue queue);
/// Returns the GL buffer object. If the buffer is mapped to CL memory
/// space, it will be unmapped back to GL.
GLuint BindVBO(void *deviceContext = NULL);
protected:
/// Constructor.
CLGLVertexBuffer(int numElements, int numVertices, cl_context clContext);
/// Allocates VBO for this buffer and register as a CL resource.
/// Returns true if success.
bool allocate(cl_context clContext);
/// Acquires a resource from GL.
void map(cl_command_queue queue);
/// Releases a resource to GL.
void unmap();
private:
int _numElements;
int _numVertices;
GLuint _vbo;
cl_command_queue _clQueue;
cl_mem _clMemory;
bool _clMapped;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CL_GL_VERTEX_BUFFER_H

View File

@@ -0,0 +1,249 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
struct Vertex {
float v[LENGTH];
};
static void clear(struct Vertex *vertex) {
for (int i = 0; i < LENGTH; i++) {
vertex->v[i] = 0.0f;
}
}
static void addWithWeight(struct Vertex *dst,
__global float *srcOrigin,
int index, float weight) {
__global float *src = srcOrigin + index * SRC_STRIDE;
for (int i = 0; i < LENGTH; ++i) {
dst->v[i] += src[i] * weight;
}
}
static void writeVertex(__global float *dstOrigin,
int index,
struct Vertex *src) {
__global float *dst = dstOrigin + index * DST_STRIDE;
for (int i = 0; i < LENGTH; ++i) {
dst[i] = src->v[i];
}
}
static void writeVertexStride(__global float *dstOrigin,
int index,
struct Vertex *src,
int stride) {
__global float *dst = dstOrigin + index * stride;
for (int i = 0; i < LENGTH; ++i) {
dst[i] = src->v[i];
}
}
__kernel void computeStencils(
__global float * src, int srcOffset,
__global float * dst, int dstOffset,
__global int * sizes,
__global int * offsets,
__global int * indices,
__global float * weights,
int batchStart, int batchEnd) {
int current = get_global_id(0) + batchStart;
if (current>=batchEnd) {
return;
}
struct Vertex v;
clear(&v);
int size = sizes[current],
offset = offsets[current];
src += srcOffset;
dst += dstOffset;
for (int i=0; i<size; ++i) {
addWithWeight(&v, src, indices[offset+i], weights[offset+i]);
}
writeVertex(dst, current, &v);
}
__kernel void computeStencilsDerivatives(
__global float * src, int srcOffset,
__global float * dst, int dstOffset,
__global float * du, int duOffset, int duStride,
__global float * dv, int dvOffset, int dvStride,
__global float * duu, int duuOffset, int duuStride,
__global float * duv, int duvOffset, int duvStride,
__global float * dvv, int dvvOffset, int dvvStride,
__global int * sizes,
__global int * offsets,
__global int * indices,
__global float * weights,
__global float * duWeights,
__global float * dvWeights,
__global float * duuWeights,
__global float * duvWeights,
__global float * dvvWeights,
int batchStart, int batchEnd) {
int current = get_global_id(0) + batchStart;
if (current>=batchEnd) {
return;
}
struct Vertex v, vdu, vdv, vduu, vduv, vdvv;
clear(&v);
clear(&vdu);
clear(&vdv);
clear(&vduu);
clear(&vduv);
clear(&vdvv);
int size = sizes[current],
offset = offsets[current];
if (src) src += srcOffset;
if (dst) dst += dstOffset;
if (du) du += duOffset;
if (dv) dv += dvOffset;
if (duu) duu += duuOffset;
if (duv) duv += duvOffset;
if (dvv) dvv += dvvOffset;
for (int i=0; i<size; ++i) {
int ofs = offset + i;
int vid = indices[ofs];
if (weights) addWithWeight( &v, src, vid, weights[ofs]);
if (duWeights) addWithWeight(&vdu, src, vid, duWeights[ofs]);
if (dvWeights) addWithWeight(&vdv, src, vid, dvWeights[ofs]);
if (duuWeights) addWithWeight(&vduu, src, vid, duuWeights[ofs]);
if (duvWeights) addWithWeight(&vduv, src, vid, duvWeights[ofs]);
if (dvvWeights) addWithWeight(&vdvv, src, vid, dvvWeights[ofs]);
}
if (dst) writeVertex (dst, current, &v);
if (du) writeVertexStride(du, current, &vdu, duStride);
if (dv) writeVertexStride(dv, current, &vdv, dvStride);
if (duu) writeVertexStride(duu, current, &vduu, duuStride);
if (duv) writeVertexStride(duv, current, &vduv, duvStride);
if (dvv) writeVertexStride(dvv, current, &vdvv, dvvStride);
}
// ---------------------------------------------------------------------------
__kernel void computePatches(__global float *src, int srcOffset,
__global float *dst, int dstOffset,
__global float *du, int duOffset, int duStride,
__global float *dv, int dvOffset, int dvStride,
__global float *duu, int duuOffset, int duuStride,
__global float *duv, int duvOffset, int duvStride,
__global float *dvv, int dvvOffset, int dvvStride,
__global struct OsdPatchCoord *patchCoords,
__global struct OsdPatchArray *patchArrayBuffer,
__global int *patchIndexBuffer,
__global struct OsdPatchParam *patchParamBuffer) {
int current = get_global_id(0);
if (src) src += srcOffset;
if (dst) dst += dstOffset;
if (du) du += duOffset;
if (dv) dv += dvOffset;
if (duu) duu += duuOffset;
if (duv) duv += duvOffset;
if (dvv) dvv += dvvOffset;
struct OsdPatchCoord coord = patchCoords[current];
struct OsdPatchArray array = patchArrayBuffer[coord.arrayIndex];
struct OsdPatchParam param = patchParamBuffer[coord.patchIndex];
int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
int nPoints = OsdEvaluatePatchBasis(patchType, param,
coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
int indexBase = array.indexBase + array.stride *
(coord.patchIndex - array.primitiveIdBase);
struct Vertex v;
clear(&v);
for (int i = 0; i < nPoints; ++i) {
int index = patchIndexBuffer[indexBase + i];
addWithWeight(&v, src, index, wP[i]);
}
writeVertex(dst, current, &v);
if (du) {
struct Vertex vdu;
clear(&vdu);
for (int i = 0; i < nPoints; ++i) {
int index = patchIndexBuffer[indexBase + i];
addWithWeight(&vdu, src, index, wDu[i]);
}
writeVertexStride(du, current, &vdu, duStride);
}
if (dv) {
struct Vertex vdv;
clear(&vdv);
for (int i = 0; i < nPoints; ++i) {
int index = patchIndexBuffer[indexBase + i];
addWithWeight(&vdv, src, index, wDv[i]);
}
writeVertexStride(dv, current, &vdv, dvStride);
}
if (duu) {
struct Vertex vduu;
clear(&vduu);
for (int i = 0; i < nPoints; ++i) {
int index = patchIndexBuffer[indexBase + i];
addWithWeight(&vduu, src, index, wDuu[i]);
}
writeVertexStride(duu, current, &vduu, duuStride);
}
if (duv) {
struct Vertex vduv;
clear(&vduv);
for (int i = 0; i < nPoints; ++i) {
int index = patchIndexBuffer[indexBase + i];
addWithWeight(&vduv, src, index, wDuv[i]);
}
writeVertexStride(duv, current, &vduv, duvStride);
}
if (dvv) {
struct Vertex vdvv;
clear(&vdvv);
for (int i = 0; i < nPoints; ++i) {
int index = patchIndexBuffer[indexBase + i];
addWithWeight(&vdvv, src, index, wDvv[i]);
}
writeVertexStride(dvv, current, &vdvv, dvvStride);
}
}

View File

@@ -0,0 +1,170 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/clPatchTable.h"
#include "../far/error.h"
#include "../far/patchTable.h"
#include "../osd/opencl.h"
#include "../osd/cpuPatchTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CLPatchTable::CLPatchTable() :
_patchArrays(NULL), _indexBuffer(NULL), _patchParamBuffer(NULL) {
}
CLPatchTable::~CLPatchTable() {
if (_patchArrays) clReleaseMemObject(_patchArrays);
if (_indexBuffer) clReleaseMemObject(_indexBuffer);
if (_patchParamBuffer) clReleaseMemObject(_patchParamBuffer);
if (_varyingPatchArrays) clReleaseMemObject(_varyingPatchArrays);
if (_varyingIndexBuffer) clReleaseMemObject(_varyingIndexBuffer);
for (int fvc=0; fvc<(int)_fvarPatchArrays.size(); ++fvc) {
if (_fvarPatchArrays[fvc]) clReleaseMemObject(_fvarPatchArrays[fvc]);
}
for (int fvc=0; fvc<(int)_fvarIndexBuffers.size(); ++fvc) {
if (_fvarIndexBuffers[fvc]) clReleaseMemObject(_fvarIndexBuffers[fvc]);
}
for (int fvc=0; fvc<(int)_fvarParamBuffers.size(); ++fvc) {
if (_fvarParamBuffers[fvc]) clReleaseMemObject(_fvarParamBuffers[fvc]);
}
}
CLPatchTable *
CLPatchTable::Create(Far::PatchTable const *farPatchTable,
cl_context clContext) {
CLPatchTable *instance = new CLPatchTable();
if (instance->allocate(farPatchTable, clContext)) return instance;
delete instance;
return 0;
}
bool
CLPatchTable::allocate(Far::PatchTable const *farPatchTable, cl_context clContext) {
CpuPatchTable patchTable(farPatchTable);
size_t numPatchArrays = patchTable.GetNumPatchArrays();
size_t indexSize = patchTable.GetPatchIndexSize();
size_t patchParamSize = patchTable.GetPatchParamSize();
cl_int err = 0;
_patchArrays = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
numPatchArrays * sizeof(Osd::PatchArray),
(void*)patchTable.GetPatchArrayBuffer(),
&err);
if (err != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", err);
return false;
}
_indexBuffer = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
indexSize * sizeof(int),
(void*)patchTable.GetPatchIndexBuffer(),
&err);
if (err != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", err);
return false;
}
_patchParamBuffer = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
patchParamSize * sizeof(Osd::PatchParam),
(void*)patchTable.GetPatchParamBuffer(),
&err);
if (err != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", err);
return false;
}
_varyingPatchArrays = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
numPatchArrays * sizeof(Osd::PatchArray),
(void*)patchTable.GetVaryingPatchArrayBuffer(),
&err);
if (err != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", err);
return false;
}
_varyingIndexBuffer = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
patchTable.GetVaryingPatchIndexSize() * sizeof(int),
(void*)patchTable.GetVaryingPatchIndexBuffer(),
&err);
if (err != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", err);
return false;
}
size_t numFVarChannels = patchTable.GetNumFVarChannels();
_fvarPatchArrays.resize(numFVarChannels, 0);
_fvarIndexBuffers.resize(numFVarChannels, 0);
_fvarParamBuffers.resize(numFVarChannels, 0);
for (int fvc=0; fvc<(int)numFVarChannels; ++fvc) {
_fvarPatchArrays[fvc] = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
numPatchArrays * sizeof(Osd::PatchArray),
(void*)patchTable.GetFVarPatchArrayBuffer(fvc),
&err);
if (err != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", err);
return false;
}
_fvarIndexBuffers[fvc] = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
patchTable.GetFVarPatchIndexSize(fvc) * sizeof(int),
(void*)patchTable.GetFVarPatchIndexBuffer(fvc),
&err);
if (err != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", err);
return false;
}
_fvarParamBuffers[fvc] = clCreateBuffer(clContext,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
patchTable.GetFVarPatchParamSize(fvc) * sizeof(Osd::PatchParam),
(void*)patchTable.GetFVarPatchParamBuffer(fvc),
&err);
if (err != CL_SUCCESS) {
Far::Error(Far::FAR_RUNTIME_ERROR, "clCreateBuffer: %d", err);
return false;
}
}
return true;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,119 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CL_PATCH_TABLE_H
#define OPENSUBDIV3_OSD_CL_PATCH_TABLE_H
#include "../version.h"
#include "../osd/opencl.h"
#include "../osd/nonCopyable.h"
#include "../osd/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far{
class PatchTable;
};
namespace Osd {
/// \brief CL patch table
///
/// This class is a CL buffer representation of Far::PatchTable.
///
/// CLEvaluator consumes this table to evaluate on the patches.
///
///
class CLPatchTable : private NonCopyable<CLPatchTable> {
public:
/// Creator. Returns NULL if error
static CLPatchTable *Create(Far::PatchTable const *patchTable,
cl_context clContext);
template <typename DEVICE_CONTEXT>
static CLPatchTable * Create(Far::PatchTable const *patchTable,
DEVICE_CONTEXT context) {
return Create(patchTable, context->GetContext());
}
/// Destructor
~CLPatchTable();
/// Returns the CL memory of the array of Osd::PatchArray buffer
cl_mem GetPatchArrayBuffer() const { return _patchArrays; }
/// Returns the CL memory of the patch control vertices
cl_mem GetPatchIndexBuffer() const { return _indexBuffer; }
/// Returns the CL memory of the array of Osd::PatchParam buffer
cl_mem GetPatchParamBuffer() const { return _patchParamBuffer; }
/// Returns the CL memory of the array of Osd::PatchArray buffer
cl_mem GetVaryingPatchArrayBuffer() const { return _varyingPatchArrays; }
/// Returns the CL memory of the varying control vertices
cl_mem GetVaryingPatchIndexBuffer() const { return _varyingIndexBuffer; }
/// Returns the number of face-varying channel buffers
int GetNumFVarChannels() const { return (int)_fvarPatchArrays.size(); }
/// Returns the CL memory of the array of Osd::PatchArray buffer
cl_mem GetFVarPatchArrayBuffer(int fvarChannel = 0) const { return _fvarPatchArrays[fvarChannel]; }
/// Returns the CL memory of the face-varying control vertices
cl_mem GetFVarPatchIndexBuffer(int fvarChannel = 0) const { return _fvarIndexBuffers[fvarChannel]; }
/// Returns the CL memory of the array of Osd::PatchParam buffer
cl_mem GetFVarPatchParamBuffer(int fvarChannel = 0) const { return _fvarParamBuffers[fvarChannel]; }
protected:
CLPatchTable();
bool allocate(Far::PatchTable const *patchTable, cl_context clContext);
cl_mem _patchArrays;
cl_mem _indexBuffer;
cl_mem _patchParamBuffer;
cl_mem _varyingPatchArrays;
cl_mem _varyingIndexBuffer;
std::vector<cl_mem> _fvarPatchArrays;
std::vector<cl_mem> _fvarIndexBuffers;
std::vector<cl_mem> _fvarParamBuffers;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CL_PATCH_TABLE_H

View File

@@ -0,0 +1,102 @@
//
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/clVertexBuffer.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CLVertexBuffer::CLVertexBuffer(int numElements, int numVertices,
cl_context /* clContext */)
: _numElements(numElements), _numVertices(numVertices), _clMemory(NULL) {
}
CLVertexBuffer::~CLVertexBuffer() {
clReleaseMemObject(_clMemory);
}
CLVertexBuffer *
CLVertexBuffer::Create(int numElements, int numVertices,
cl_context clContext) {
CLVertexBuffer *instance =
new CLVertexBuffer(numElements, numVertices, clContext);
if (instance->allocate(clContext)) return instance;
delete instance;
return NULL;
}
void
CLVertexBuffer::UpdateData(const float *src, int startVertex, int numVertices, cl_command_queue queue,
cl_event* startEvents, unsigned int numStartEvents, cl_event* endEvent) {
size_t size = _numElements * numVertices * sizeof(float);
size_t offset = startVertex * _numElements * sizeof(float);
cl_bool blocking = (endEvent == NULL) ? CL_TRUE : CL_FALSE;
cl_int err = clEnqueueWriteBuffer(queue, _clMemory, blocking, offset, size, src, numStartEvents, startEvents, endEvent);
assert(err == CL_SUCCESS);
}
int
CLVertexBuffer::GetNumElements() const {
return _numElements;
}
int
CLVertexBuffer::GetNumVertices() const {
return _numVertices;
}
cl_mem
CLVertexBuffer::BindCLBuffer(cl_command_queue /* queue */) {
return _clMemory;
}
bool
CLVertexBuffer::allocate(cl_context clContext) {
assert(clContext);
int size = _numVertices * _numElements * sizeof(float);
cl_int err;
_clMemory = clCreateBuffer(clContext, CL_MEM_READ_WRITE, size, NULL, &err);
if (err != CL_SUCCESS) return false;
return true;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,99 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CL_VERTEX_BUFFER_H
#define OPENSUBDIV3_OSD_CL_VERTEX_BUFFER_H
#include "../version.h"
#include "../osd/opencl.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
///
/// \brief Concrete vertex buffer class for OpenCL subdivision.
///
/// CLVertexBuffer implements CLVertexBufferInterface. An instance of this
/// buffer class can be passed to CLEvaluator
///
class CLVertexBuffer {
public:
/// Creator. Returns NULL if error.
static CLVertexBuffer * Create(int numElements, int numVertices, cl_context clContext);
template <typename DEVICE_CONTEXT>
static CLVertexBuffer * Create(int numElements, int numVertices,
DEVICE_CONTEXT context) {
return Create(numElements, numVertices, context->GetContext());
}
/// Destructor.
~CLVertexBuffer();
/// This method is meant to be used in client code in order to provide coarse
/// vertices data to Osd.
void UpdateData(const float *src, int startVertex, int numVertices, cl_command_queue clQueue,
cl_event* startEvents = NULL, unsigned int numStartEvents = 0, cl_event* endEvent = NULL);
template<typename DEVICE_CONTEXT>
void UpdateData(const float *src, int startVertex, int numVertices,
DEVICE_CONTEXT context,
cl_event* startEvents = NULL, unsigned int numStartEvents = 0, cl_event* endEvent = NULL) {
UpdateData(src, startVertex, numVertices, context->GetCommandQueue(), startEvents, numStartEvents, endEvent);
}
/// Returns how many elements defined in this vertex buffer.
int GetNumElements() const;
/// Returns how many vertices allocated in this vertex buffer.
int GetNumVertices() const;
/// Returns the CL memory object.
cl_mem BindCLBuffer(cl_command_queue queue);
protected:
/// Constructor.
CLVertexBuffer(int numElements, int numVertices, cl_context clContext);
/// Allocates CL memory for this buffer.
/// Returns true if success.
bool allocate(cl_context clContext);
private:
int _numElements;
int _numVertices;
cl_mem _clMemory;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CL_VERTEX_BUFFER_H

View File

@@ -0,0 +1,140 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/cpuD3D11VertexBuffer.h"
#include "../far/error.h"
#include <D3D11.h>
#include <cassert>
#include <string.h>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CpuD3D11VertexBuffer::CpuD3D11VertexBuffer(int numElements, int numVertices)
: _numElements(numElements), _numVertices(numVertices),
_d3d11Buffer(NULL), _cpuBuffer(NULL) {
}
CpuD3D11VertexBuffer::~CpuD3D11VertexBuffer() {
delete[] _cpuBuffer;
if (_d3d11Buffer) _d3d11Buffer->Release();
}
CpuD3D11VertexBuffer *
CpuD3D11VertexBuffer::Create(int numElements, int numVertices,
ID3D11DeviceContext *deviceContext) {
CpuD3D11VertexBuffer *instance =
new CpuD3D11VertexBuffer(numElements, numVertices);
ID3D11Device *device;
deviceContext->GetDevice(&device);
if (instance->allocate(device)) return instance;
delete instance;
return NULL;
}
void
CpuD3D11VertexBuffer::UpdateData(const float *src, int startVertex,
int numVertices,
void * /*deviceContext*/) {
memcpy(_cpuBuffer + startVertex * _numElements, src,
_numElements * numVertices * sizeof(float));
}
int
CpuD3D11VertexBuffer::GetNumElements() const {
return _numElements;
}
int
CpuD3D11VertexBuffer::GetNumVertices() const {
return _numVertices;
}
float*
CpuD3D11VertexBuffer::BindCpuBuffer() {
return _cpuBuffer;
}
ID3D11Buffer *
CpuD3D11VertexBuffer::BindD3D11Buffer(ID3D11DeviceContext *deviceContext) {
assert(deviceContext);
D3D11_MAPPED_SUBRESOURCE resource;
HRESULT hr = deviceContext->Map(_d3d11Buffer, 0,
D3D11_MAP_WRITE_DISCARD, 0, &resource);
if (FAILED(hr)) {
Far::Error(Far::FAR_RUNTIME_ERROR, "Fail to map buffer\n");
return NULL;
}
int size = _numElements * _numVertices * sizeof(float);
memcpy(resource.pData, _cpuBuffer, size);
deviceContext->Unmap(_d3d11Buffer, 0);
return _d3d11Buffer;
}
bool
CpuD3D11VertexBuffer::allocate(ID3D11Device *device) {
_cpuBuffer = new float[_numElements * _numVertices];
// XXX: should move this constructor to factory for error handling
D3D11_BUFFER_DESC hBufferDesc;
hBufferDesc.ByteWidth = _numElements * _numVertices * sizeof(float);
hBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
hBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_SHADER_RESOURCE;
hBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hBufferDesc.MiscFlags = 0;
hBufferDesc.StructureByteStride = sizeof(float); // XXX ?
HRESULT hr;
hr = device->CreateBuffer(&hBufferDesc, NULL, &_d3d11Buffer);
if (FAILED(hr)) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"Fail in CreateBuffer\n");
return false;
}
return true;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,99 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CPU_D3D11_VERTEX_BUFFER_H
#define OPENSUBDIV3_OSD_CPU_D3D11_VERTEX_BUFFER_H
#include "../version.h"
#include <cstddef>
struct ID3D11Buffer;
struct ID3D11Device;
struct ID3D11DeviceContext;
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
///
/// \brief Concrete vertex buffer class for Cpu subdivision and DirectX drawing.
///
/// CpuD3D11VertexBuffer implements CpuVertexBufferInterface and
/// D3D11VertexBufferInterface.
///
/// An instance of this buffer class can be passed to CpuEvaluator.
///
class CpuD3D11VertexBuffer {
public:
/// Creator. Returns NULL if error.
static CpuD3D11VertexBuffer * Create(int numElements, int numVertices,
ID3D11DeviceContext *deviceContext);
/// Destructor.
virtual ~CpuD3D11VertexBuffer();
/// This method is meant to be used in client code in order to provide coarse
/// vertices data to Osd.
void UpdateData(const float *src, int startVertex, int numVertices,
void *deviceContext = NULL);
/// Returns how many elements defined in this vertex buffer.
int GetNumElements() const;
/// Returns how many vertices allocated in this vertex buffer.
int GetNumVertices() const;
/// Returns the address of CPU buffer.
float * BindCpuBuffer();
/// Returns the D3D11 buffer object.
ID3D11Buffer *BindD3D11Buffer(ID3D11DeviceContext *deviceContext);
/// Returns the D3D11 buffer object (for Osd::Mesh interface)
ID3D11Buffer *BindVBO(ID3D11DeviceContext *deviceContext) {
return BindD3D11Buffer(deviceContext);
}
protected:
/// Constructor.
CpuD3D11VertexBuffer(int numElements, int numVertices);
bool allocate(ID3D11Device *device);
private:
int _numElements;
int _numVertices;
ID3D11Buffer *_d3d11Buffer;
float *_cpuBuffer;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CPU_D3D11_VERTEX_BUFFER_H

View File

@@ -0,0 +1,389 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/cpuEvaluator.h"
#include "../osd/cpuKernel.h"
#include "../osd/patchBasisCommonTypes.h"
#include "../osd/patchBasisCommon.h"
#include "../osd/patchBasisCommonEval.h"
#include <cstdlib>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
/* static */
bool
CpuEvaluator::EvalStencils(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
const int * sizes,
const int * offsets,
const int * indices,
const float * weights,
int start, int end) {
if (end <= start) return true;
if (srcDesc.length != dstDesc.length) return false;
// XXX: we can probably expand cpuKernel.cpp to here.
CpuEvalStencils(src, srcDesc, dst, dstDesc,
sizes, offsets, indices, weights, start, end);
return true;
}
/* static */
bool
CpuEvaluator::EvalStencils(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
float *du, BufferDescriptor const &duDesc,
float *dv, BufferDescriptor const &dvDesc,
const int * sizes,
const int * offsets,
const int * indices,
const float * weights,
const float * duWeights,
const float * dvWeights,
int start, int end) {
if (end <= start) return true;
if (srcDesc.length != dstDesc.length) return false;
if (srcDesc.length != duDesc.length) return false;
if (srcDesc.length != dvDesc.length) return false;
CpuEvalStencils(src, srcDesc,
dst, dstDesc,
du, duDesc,
dv, dvDesc,
sizes, offsets, indices,
weights, duWeights, dvWeights,
start, end);
return true;
}
/* static */
bool
CpuEvaluator::EvalStencils(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
float *du, BufferDescriptor const &duDesc,
float *dv, BufferDescriptor const &dvDesc,
float *duu, BufferDescriptor const &duuDesc,
float *duv, BufferDescriptor const &duvDesc,
float *dvv, BufferDescriptor const &dvvDesc,
const int * sizes,
const int * offsets,
const int * indices,
const float * weights,
const float * duWeights,
const float * dvWeights,
const float * duuWeights,
const float * duvWeights,
const float * dvvWeights,
int start, int end) {
if (end <= start) return true;
if (srcDesc.length != dstDesc.length) return false;
if (srcDesc.length != duDesc.length) return false;
if (srcDesc.length != dvDesc.length) return false;
if (srcDesc.length != duuDesc.length) return false;
if (srcDesc.length != duvDesc.length) return false;
if (srcDesc.length != dvvDesc.length) return false;
CpuEvalStencils(src, srcDesc,
dst, dstDesc,
du, duDesc,
dv, dvDesc,
duu, duuDesc,
duv, duvDesc,
dvv, dvvDesc,
sizes, offsets, indices,
weights, duWeights, dvWeights,
duuWeights, duvWeights, dvvWeights,
start, end);
return true;
}
template <typename T>
struct BufferAdapter {
BufferAdapter(T *p, int length, int stride) :
_p(p), _length(length), _stride(stride) { }
void Clear() {
for (int i = 0; i < _length; ++i) _p[i] = 0;
}
void AddWithWeight(T const *src, float w) {
if (_p) {
for (int i = 0; i < _length; ++i) {
_p[i] += src[i] * w;
}
}
}
const T *operator[] (int index) const {
return _p + _stride * index;
}
BufferAdapter<T> & operator ++() {
if (_p) {
_p += _stride;
}
return *this;
}
T *_p;
int _length;
int _stride;
};
/* static */
bool
CpuEvaluator::EvalPatches(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
int numPatchCoords,
const PatchCoord *patchCoords,
const PatchArray *patchArrays,
const int *patchIndexBuffer,
const PatchParam *patchParamBuffer) {
if (src) {
src += srcDesc.offset;
} else {
return false;
}
if (dst) {
dst += dstDesc.offset;
if (srcDesc.length != dstDesc.length) return false;
} else {
return false;
}
BufferAdapter<const float> srcT(src, srcDesc.length, srcDesc.stride);
BufferAdapter<float> dstT(dst, dstDesc.length, dstDesc.stride);
float wP[20];
for (int i = 0; i < numPatchCoords; ++i) {
PatchCoord const &coord = patchCoords[i];
PatchArray const &array = patchArrays[coord.handle.arrayIndex];
Osd::PatchParam const & paramStruct =
patchParamBuffer[coord.handle.patchIndex];
OsdPatchParam param = OsdPatchParamInit(
paramStruct.field0, paramStruct.field1, paramStruct.sharpness);
int patchType = OsdPatchParamIsRegular(param)
? array.GetPatchTypeRegular()
: array.GetPatchTypeIrregular();
int nPoints = OsdEvaluatePatchBasis(patchType, param,
coord.s, coord.t, wP, 0, 0, 0, 0, 0);
int indexBase = array.GetIndexBase() + array.GetStride() *
(coord.handle.patchIndex - array.GetPrimitiveIdBase());
const int *cvs = &patchIndexBuffer[indexBase];
dstT.Clear();
for (int j = 0; j < nPoints; ++j) {
dstT.AddWithWeight(srcT[cvs[j]], wP[j]);
}
++dstT;
}
return true;
}
/* static */
bool
CpuEvaluator::EvalPatches(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
float *du, BufferDescriptor const &duDesc,
float *dv, BufferDescriptor const &dvDesc,
int numPatchCoords,
const PatchCoord *patchCoords,
const PatchArray *patchArrays,
const int *patchIndexBuffer,
const PatchParam *patchParamBuffer) {
if (src) {
src += srcDesc.offset;
} else {
return false;
}
if (dst) {
if (srcDesc.length != dstDesc.length) return false;
dst += dstDesc.offset;
}
if (du) {
du += duDesc.offset;
if (srcDesc.length != duDesc.length) return false;
}
if (dv) {
dv += dvDesc.offset;
if (srcDesc.length != dvDesc.length) return false;
}
BufferAdapter<const float> srcT(src, srcDesc.length, srcDesc.stride);
BufferAdapter<float> dstT(dst, dstDesc.length, dstDesc.stride);
BufferAdapter<float> duT(du, duDesc.length, duDesc.stride);
BufferAdapter<float> dvT(dv, dvDesc.length, dvDesc.stride);
float wP[20], wDs[20], wDt[20];
for (int i = 0; i < numPatchCoords; ++i) {
PatchCoord const &coord = patchCoords[i];
PatchArray const &array = patchArrays[coord.handle.arrayIndex];
Osd::PatchParam const & paramStruct =
patchParamBuffer[coord.handle.patchIndex];
OsdPatchParam param = OsdPatchParamInit(
paramStruct.field0, paramStruct.field1, paramStruct.sharpness);
int patchType = OsdPatchParamIsRegular(param)
? array.GetPatchTypeRegular()
: array.GetPatchTypeIrregular();
int nPoints = OsdEvaluatePatchBasis(patchType, param,
coord.s, coord.t, wP, wDs, wDt, 0, 0, 0);
int indexBase = array.GetIndexBase() + array.GetStride() *
(coord.handle.patchIndex - array.GetPrimitiveIdBase());
const int *cvs = &patchIndexBuffer[indexBase];
dstT.Clear();
duT.Clear();
dvT.Clear();
for (int j = 0; j < nPoints; ++j) {
dstT.AddWithWeight(srcT[cvs[j]], wP[j]);
duT.AddWithWeight (srcT[cvs[j]], wDs[j]);
dvT.AddWithWeight (srcT[cvs[j]], wDt[j]);
}
++dstT;
++duT;
++dvT;
}
return true;
}
/* static */
bool
CpuEvaluator::EvalPatches(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
float *du, BufferDescriptor const &duDesc,
float *dv, BufferDescriptor const &dvDesc,
float *duu, BufferDescriptor const &duuDesc,
float *duv, BufferDescriptor const &duvDesc,
float *dvv, BufferDescriptor const &dvvDesc,
int numPatchCoords,
const PatchCoord *patchCoords,
const PatchArray *patchArrays,
const int *patchIndexBuffer,
const PatchParam *patchParamBuffer) {
if (src) {
src += srcDesc.offset;
} else {
return false;
}
if (dst) {
if (srcDesc.length != dstDesc.length) return false;
dst += dstDesc.offset;
}
if (du) {
du += duDesc.offset;
if (srcDesc.length != duDesc.length) return false;
}
if (dv) {
dv += dvDesc.offset;
if (srcDesc.length != dvDesc.length) return false;
}
if (duu) {
duu += duuDesc.offset;
if (srcDesc.length != duuDesc.length) return false;
}
if (duv) {
duv += duvDesc.offset;
if (srcDesc.length != duvDesc.length) return false;
}
if (dvv) {
dvv += dvvDesc.offset;
if (srcDesc.length != dvvDesc.length) return false;
}
BufferAdapter<const float> srcT(src, srcDesc.length, srcDesc.stride);
BufferAdapter<float> dstT(dst, dstDesc.length, dstDesc.stride);
BufferAdapter<float> duT(du, duDesc.length, duDesc.stride);
BufferAdapter<float> dvT(dv, dvDesc.length, dvDesc.stride);
BufferAdapter<float> duuT(duu, duuDesc.length, duuDesc.stride);
BufferAdapter<float> duvT(duv, duvDesc.length, duvDesc.stride);
BufferAdapter<float> dvvT(dvv, dvvDesc.length, dvvDesc.stride);
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
for (int i = 0; i < numPatchCoords; ++i) {
PatchCoord const &coord = patchCoords[i];
PatchArray const &array = patchArrays[coord.handle.arrayIndex];
Osd::PatchParam const & paramStruct =
patchParamBuffer[coord.handle.patchIndex];
OsdPatchParam param = OsdPatchParamInit(
paramStruct.field0, paramStruct.field1, paramStruct.sharpness);
int patchType = OsdPatchParamIsRegular(param)
? array.GetPatchTypeRegular()
: array.GetPatchTypeIrregular();
int nPoints = OsdEvaluatePatchBasis(patchType, param,
coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
int indexBase = array.GetIndexBase() + array.GetStride() *
(coord.handle.patchIndex - array.GetPrimitiveIdBase());
const int *cvs = &patchIndexBuffer[indexBase];
dstT.Clear();
duT.Clear();
dvT.Clear();
duuT.Clear();
duvT.Clear();
dvvT.Clear();
for (int j = 0; j < nPoints; ++j) {
dstT.AddWithWeight(srcT[cvs[j]], wP[j]);
duT.AddWithWeight (srcT[cvs[j]], wDu[j]);
dvT.AddWithWeight (srcT[cvs[j]], wDv[j]);
duuT.AddWithWeight (srcT[cvs[j]], wDuu[j]);
duvT.AddWithWeight (srcT[cvs[j]], wDuv[j]);
dvvT.AddWithWeight (srcT[cvs[j]], wDvv[j]);
}
++dstT;
++duT;
++dvT;
++duuT;
++duvT;
++dvvT;
}
return true;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "glLoader.h"
#include "../osd/cpuGLVertexBuffer.h"
#include <string.h>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CpuGLVertexBuffer::CpuGLVertexBuffer(int numElements, int numVertices)
: _numElements(numElements), _numVertices(numVertices),
_vbo(0), _cpuBuffer(0), _dataDirty(true) {
// Initialize internal OpenGL loader library if necessary
OpenSubdiv::internal::GLLoader::libraryInitializeGL();
}
CpuGLVertexBuffer::~CpuGLVertexBuffer() {
delete[] _cpuBuffer;
if (_vbo) {
glDeleteBuffers(1, &_vbo);
}
}
CpuGLVertexBuffer *
CpuGLVertexBuffer::Create(int numElements, int numVertices, void *) {
CpuGLVertexBuffer *instance =
new CpuGLVertexBuffer(numElements, numVertices);
if (instance->allocate()) return instance;
delete instance;
return NULL;
}
void
CpuGLVertexBuffer::UpdateData(const float *src,
int startVertex, int numVertices,
void * /*deviceContext*/) {
memcpy(_cpuBuffer + startVertex * GetNumElements(), src,
GetNumElements() * numVertices * sizeof(float));
_dataDirty = true;
}
int
CpuGLVertexBuffer::GetNumElements() const {
return _numElements;
}
int
CpuGLVertexBuffer::GetNumVertices() const {
return _numVertices;
}
float*
CpuGLVertexBuffer::BindCpuBuffer() {
_dataDirty = true; // caller might modify data
return _cpuBuffer;
}
GLuint
CpuGLVertexBuffer::BindVBO(void * /*deviceContext*/) {
if (! _dataDirty)
return _vbo;
int size = GetNumElements() * GetNumVertices() * sizeof(float);
if (! _vbo) {
glGenBuffers(1, &_vbo);
}
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, size, _cpuBuffer, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
_dataDirty = false;
return _vbo;
}
bool
CpuGLVertexBuffer::allocate() {
_cpuBuffer = new float[GetNumElements() * GetNumVertices()];
_dataDirty = true;
return true;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,97 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CPU_GL_VERTEX_BUFFER_H
#define OPENSUBDIV3_OSD_CPU_GL_VERTEX_BUFFER_H
#include "../version.h"
#include <cstddef>
#include "../osd/opengl.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
///
/// \brief Concrete vertex buffer class for cpu subdivision and OpenGL drawing.
///
/// CpuGLVertexBuffer implements CpuVertexBufferInterface and
/// GLVertexBufferInterface.
///
/// The buffer interop between Cpu and GL is handled automatically when a
/// client calls BindCpuBuffer and BindVBO methods.
///
class CpuGLVertexBuffer {
public:
/// Creator. Returns NULL if error.
static CpuGLVertexBuffer * Create(int numElements, int numVertices,
void *deviceContext = NULL);
/// Destructor.
~CpuGLVertexBuffer();
/// This method is meant to be used in client code in order to provide
/// coarse vertices data to Osd.
void UpdateData(const float *src, int startVertex, int numVertices,
void *deviceContext = NULL);
/// Returns how many elements defined in this vertex buffer.
int GetNumElements() const;
/// Returns how many vertices allocated in this vertex buffer.
int GetNumVertices() const;
/// Returns cpu memory. GL buffer will be mapped to cpu address
/// if necessary.
float * BindCpuBuffer();
/// Returns the name of GL buffer object. If the buffer is mapped
/// to cpu address, it will be unmapped back to GL.
GLuint BindVBO(void *deviceContext = NULL);
protected:
/// Constructor.
CpuGLVertexBuffer(int numElements, int numVertices);
/// Allocates VBO for this buffer. Returns true if success.
bool allocate();
private:
int _numElements;
int _numVertices;
GLuint _vbo;
float *_cpuBuffer;
bool _dataDirty;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CPU_GL_VERTEX_BUFFER_H

View File

@@ -0,0 +1,245 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/cpuKernel.h"
#include "../osd/bufferDescriptor.h"
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
template <class T> T *
elementAtIndex(T * src, int index, BufferDescriptor const &desc) {
return src + index * desc.stride;
}
static inline void
clear(float *dst, BufferDescriptor const &desc) {
assert(dst);
memset(dst, 0, desc.length*sizeof(float));
}
static inline void
addWithWeight(float *dst, const float *src, int srcIndex, float weight,
BufferDescriptor const &desc) {
assert(src && dst);
src = elementAtIndex(src, srcIndex, desc);
for (int k = 0; k < desc.length; ++k) {
dst[k] += src[k] * weight;
}
}
static inline void
copy(float *dst, int dstIndex, const float *src, BufferDescriptor const &desc) {
assert(src && dst);
dst = elementAtIndex(dst, dstIndex, desc);
memcpy(dst, src, desc.length*sizeof(float));
}
void
CpuEvalStencils(float const * src, BufferDescriptor const &srcDesc,
float * dst, BufferDescriptor const &dstDesc,
int const * sizes,
int const * offsets,
int const * indices,
float const * weights,
int start, int end) {
assert(start>=0 && start<end);
if (start>0) {
sizes += start;
indices += offsets[start];
weights += offsets[start];
}
src += srcDesc.offset;
dst += dstDesc.offset;
if (srcDesc.length == 4 && dstDesc.length == 4 &&
srcDesc.stride == 4 && dstDesc.stride == 4) {
// SIMD fast path for aligned primvar data (4 floats)
ComputeStencilKernel<4>(src, dst,
sizes, indices, weights, start, end);
} else if (srcDesc.length == 8 && dstDesc.length == 8 &&
srcDesc.stride == 8 && dstDesc.stride == 8) {
// SIMD fast path for aligned primvar data (8 floats)
ComputeStencilKernel<8>(src, dst,
sizes, indices, weights, start, end);
} else {
// Slow path for non-aligned data
float * result = (float*)alloca(srcDesc.length * sizeof(float));
int nstencils = end-start;
for (int i=0; i<nstencils; ++i, ++sizes) {
clear(result, srcDesc);
for (int j=0; j<*sizes; ++j) {
addWithWeight(result, src, *indices++, *weights++, srcDesc);
}
copy(dst, i, result, dstDesc);
}
}
}
void
CpuEvalStencils(float const * src, BufferDescriptor const &srcDesc,
float * dst, BufferDescriptor const &dstDesc,
float * dstDu, BufferDescriptor const &dstDuDesc,
float * dstDv, BufferDescriptor const &dstDvDesc,
int const * sizes,
int const * offsets,
int const * indices,
float const * weights,
float const * duWeights,
float const * dvWeights,
int start, int end) {
if (start > 0) {
sizes += start;
indices += offsets[start];
weights += offsets[start];
duWeights += offsets[start];
dvWeights += offsets[start];
}
src += srcDesc.offset;
dst += dstDesc.offset;
dstDu += dstDuDesc.offset;
dstDv += dstDvDesc.offset;
int nOutLength = dstDesc.length + dstDuDesc.length + dstDvDesc.length;
float * result = (float*)alloca(nOutLength * sizeof(float));
float * resultDu = result + dstDesc.length;
float * resultDv = resultDu + dstDuDesc.length;
int nStencils = end - start;
for (int i = 0; i < nStencils; ++i, ++sizes) {
// clear
memset(result, 0, nOutLength * sizeof(float));
for (int j=0; j<*sizes; ++j) {
addWithWeight(result, src, *indices, *weights++, srcDesc);
addWithWeight(resultDu, src, *indices, *duWeights++, srcDesc);
addWithWeight(resultDv, src, *indices, *dvWeights++, srcDesc);
++indices;
}
copy(dst, i, result, dstDesc);
copy(dstDu, i, resultDu, dstDuDesc);
copy(dstDv, i, resultDv, dstDvDesc);
}
}
void
CpuEvalStencils(float const * src, BufferDescriptor const &srcDesc,
float * dst, BufferDescriptor const &dstDesc,
float * dstDu, BufferDescriptor const &dstDuDesc,
float * dstDv, BufferDescriptor const &dstDvDesc,
float * dstDuu, BufferDescriptor const &dstDuuDesc,
float * dstDuv, BufferDescriptor const &dstDuvDesc,
float * dstDvv, BufferDescriptor const &dstDvvDesc,
int const * sizes,
int const * offsets,
int const * indices,
float const * weights,
float const * duWeights,
float const * dvWeights,
float const * duuWeights,
float const * duvWeights,
float const * dvvWeights,
int start, int end) {
if (start > 0) {
sizes += start;
indices += offsets[start];
weights += offsets[start];
duWeights += offsets[start];
dvWeights += offsets[start];
duuWeights += offsets[start];
duvWeights += offsets[start];
dvvWeights += offsets[start];
}
src += srcDesc.offset;
dst += dstDesc.offset;
dstDu += dstDuDesc.offset;
dstDv += dstDvDesc.offset;
dstDuu += dstDuuDesc.offset;
dstDuv += dstDuvDesc.offset;
dstDvv += dstDvvDesc.offset;
int nOutLength = dstDesc.length + dstDuDesc.length + dstDvDesc.length
+ dstDuuDesc.length + dstDuvDesc.length + dstDvvDesc.length;
float * result = (float*)alloca(nOutLength * sizeof(float));
float * resultDu = result + dstDesc.length;
float * resultDv = resultDu + dstDuDesc.length;
float * resultDuu = resultDv + dstDvDesc.length;
float * resultDuv = resultDuu + dstDuuDesc.length;
float * resultDvv = resultDuv + dstDuvDesc.length;
int nStencils = end - start;
for (int i = 0; i < nStencils; ++i, ++sizes) {
// clear
memset(result, 0, nOutLength * sizeof(float));
for (int j=0; j<*sizes; ++j) {
addWithWeight(result, src, *indices, *weights++, srcDesc);
addWithWeight(resultDu, src, *indices, *duWeights++, srcDesc);
addWithWeight(resultDv, src, *indices, *dvWeights++, srcDesc);
addWithWeight(resultDuu, src, *indices, *duuWeights++, srcDesc);
addWithWeight(resultDuv, src, *indices, *duvWeights++, srcDesc);
addWithWeight(resultDvv, src, *indices, *dvvWeights++, srcDesc);
++indices;
}
copy(dst, i, result, dstDesc);
copy(dstDu, i, resultDu, dstDuDesc);
copy(dstDv, i, resultDv, dstDvDesc);
copy(dstDuu, i, resultDuu, dstDuuDesc);
copy(dstDuv, i, resultDuv, dstDuvDesc);
copy(dstDvv, i, resultDvv, dstDvvDesc);
}
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,150 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CPU_KERNEL_H
#define OPENSUBDIV3_OSD_CPU_KERNEL_H
#include "../version.h"
#include <cstring>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
struct BufferDescriptor;
void
CpuEvalStencils(float const * src, BufferDescriptor const &srcDesc,
float * dst, BufferDescriptor const &dstDesc,
int const * sizes,
int const * offsets,
int const * indices,
float const * weights,
int start, int end);
void
CpuEvalStencils(float const * src, BufferDescriptor const &srcDesc,
float * dst, BufferDescriptor const &dstDesc,
float * dstDu, BufferDescriptor const &dstDuDesc,
float * dstDv, BufferDescriptor const &dstDvDesc,
int const * sizes,
int const * offsets,
int const * indices,
float const * weights,
float const * duWeights,
float const * dvWeights,
int start, int end);
void
CpuEvalStencils(float const * src, BufferDescriptor const &srcDesc,
float * dst, BufferDescriptor const &dstDesc,
float * dstDu, BufferDescriptor const &dstDuDesc,
float * dstDv, BufferDescriptor const &dstDvDesc,
float * dstDuu, BufferDescriptor const &dstDuuDesc,
float * dstDuv, BufferDescriptor const &dstDuvDesc,
float * dstDvv, BufferDescriptor const &dstDvvDesc,
int const * sizes,
int const * offsets,
int const * indices,
float const * weights,
float const * duWeights,
float const * dvWeights,
float const * duuWeights,
float const * duvWeights,
float const * dvvWeights,
int start, int end);
//
// SIMD ICC optimization of the stencil kernel
//
#if defined ( __INTEL_COMPILER ) || defined ( __ICC )
#define __ALIGN_DATA __declspec(align(32))
#else
#define __ALIGN_DATA
#endif
// Note : this function is re-used in the TBB Compute kernel
template <int numElems> void
ComputeStencilKernel(float const * vertexSrc,
float * vertexDst,
int const * sizes,
int const * indices,
float const * weights,
int start,
int end) {
__ALIGN_DATA float result[numElems],
result1[numElems];
float const * src;
float * dst, weight;
for (int i=start; i<end; ++i) {
// Clear
#if defined ( __INTEL_COMPILER ) || defined ( __ICC )
#pragma simd
#pragma vector aligned
#endif
for (int k = 0; k<numElems; ++k)
result[k] = 0.0f;
for (int j=0; j<sizes[i]; ++j, ++indices, ++weights) {
src = vertexSrc + (*indices)*numElems;
weight = *weights;
// AddWithWeight
#if defined ( __INTEL_COMPILER ) || defined ( __ICC )
#pragma simd
#pragma vector aligned
#endif
for (int k=0; k<numElems; ++k) {
result[k] += src[k] * weight;
}
}
#if defined ( __INTEL_COMPILER ) || defined ( __ICC )
#pragma simd
#pragma vector aligned
#endif
for (int k=0; k<numElems; ++k) {
result1[k] = result[k];
}
dst = vertexDst + i*numElems;
memcpy(dst, result1, numElems*sizeof(float));
}
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CPU_KERNEL_H

View File

@@ -0,0 +1,164 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/cpuPatchTable.h"
#include "../far/patchDescriptor.h"
#include <iostream>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CpuPatchTable::CpuPatchTable(const Far::PatchTable *farPatchTable) {
int nPatchArrays = farPatchTable->GetNumPatchArrays();
// count
int numPatches = 0;
int numIndices = 0;
for (int j = 0; j < nPatchArrays; ++j) {
int nPatch = farPatchTable->GetNumPatches(j);
int nCV = farPatchTable->GetPatchArrayDescriptor(j).GetNumControlVertices();
numPatches += nPatch;
numIndices += nPatch * nCV;
}
_patchArrays.reserve(nPatchArrays);
_indexBuffer.reserve(numIndices);
_varyingPatchArrays.reserve(nPatchArrays);
_varyingIndexBuffer.reserve(
numPatches*farPatchTable->GetVaryingPatchDescriptor().GetNumControlVertices());
_fvarPatchArrays.resize(farPatchTable->GetNumFVarChannels());
_fvarIndexBuffers.resize(farPatchTable->GetNumFVarChannels());
_fvarParamBuffers.resize(farPatchTable->GetNumFVarChannels());
for (int fvc=0; fvc<farPatchTable->GetNumFVarChannels(); ++fvc) {
_fvarPatchArrays[fvc].reserve(nPatchArrays);
_fvarIndexBuffers[fvc].reserve(
numPatches * farPatchTable->GetFVarValueStride(fvc));
_fvarParamBuffers[fvc].reserve(numPatches);
}
_patchParamBuffer.reserve(numPatches);
// for each patchArray
for (int j = 0; j < nPatchArrays; ++j) {
int numPatchesThisArray = farPatchTable->GetNumPatches(j);
// create vertex array and append indices to buffer:
PatchArray patchArray(
farPatchTable->GetPatchArrayDescriptor(j),
numPatchesThisArray,
(int)_indexBuffer.size(), (int)_patchParamBuffer.size());
_patchArrays.push_back(patchArray);
Far::ConstIndexArray indices = farPatchTable->GetPatchArrayVertices(j);
_indexBuffer.insert(_indexBuffer.end(), indices.begin(), indices.end());
// create varying array and append indices to buffer:
PatchArray varyingPatchArray(
farPatchTable->GetVaryingPatchDescriptor(),
numPatchesThisArray,
(int)_varyingIndexBuffer.size(), (int)_patchParamBuffer.size());
_varyingPatchArrays.push_back(varyingPatchArray);
Far::ConstIndexArray
varyingIndices = farPatchTable->GetPatchArrayVaryingVertices(j);
_varyingIndexBuffer.insert(_varyingIndexBuffer.end(),
varyingIndices.begin(), varyingIndices.end());
// create face-varying arrays for each channel:
for (int fvc=0; fvc<farPatchTable->GetNumFVarChannels(); ++fvc) {
// create face-varying array and append indices to buffer:
PatchArray fvarPatchArray(
farPatchTable->GetFVarPatchDescriptorRegular(fvc),
farPatchTable->GetFVarPatchDescriptorIrregular(fvc),
numPatchesThisArray,
(int)_fvarIndexBuffers[fvc].size(), (int)_fvarParamBuffers[fvc].size());
_fvarPatchArrays[fvc].push_back(fvarPatchArray);
Far::ConstIndexArray
fvarIndices = farPatchTable->GetPatchArrayFVarValues(j, fvc);
_fvarIndexBuffers[fvc].insert(_fvarIndexBuffers[fvc].end(),
fvarIndices.begin(), fvarIndices.end());
// append face-varying patch params (converting Far PatchParams to Osd)
Far::ConstPatchParamArray
fvarParam = farPatchTable->GetPatchArrayFVarPatchParams(j, fvc);
for (int k = 0; k < numPatchesThisArray; ++k) {
PatchParam param;
//param.patchParam = patchParamTable[patchIndex];
param.field0 = fvarParam[k].field0;
param.field1 = fvarParam[k].field1;
param.sharpness = 0.0f;
_fvarParamBuffers[fvc].push_back(param);
}
}
// patchParams bundling
// XXX: this process won't be needed if Far::PatchParam includes
// sharpness.
#if 0
// XXX: we need sharpness interface for patcharray or put sharpness
// into patchParam.
Far::ConstPatchParamArray patchParams =
farPatchTable->GetPatchParams(j);
for (int k = 0; k < patchParams.size(); ++k) {
float sharpness = 0.0;
_patchParamBuffer.push_back(patchParams[k].field0);
_patchParamBuffer.push_back(patchParams[k].field1);
_patchParamBuffer.push_back(*((unsigned int *)&sharpness));
}
#else
// XXX: workaround. GetPatchParamTable() will be deprecated though.
Far::PatchParamTable const & patchParamTable =
farPatchTable->GetPatchParamTable();
std::vector<Far::Index> const &sharpnessIndexTable =
farPatchTable->GetSharpnessIndexTable();
int numPatchesJ = farPatchTable->GetNumPatches(j);
for (int k = 0; k < numPatchesJ; ++k) {
float sharpness = 0.0;
int patchIndex = (int)_patchParamBuffer.size();
if (patchIndex < (int)sharpnessIndexTable.size()) {
int sharpnessIndex = sharpnessIndexTable[patchIndex];
if (sharpnessIndex >= 0)
sharpness = farPatchTable->GetSharpnessValues()[sharpnessIndex];
}
PatchParam param;
//param.patchParam = patchParamTable[patchIndex];
param.field0 = patchParamTable[patchIndex].field0;
param.field1 = patchParamTable[patchIndex].field1;
param.sharpness = sharpness;
_patchParamBuffer.push_back(param);
}
#endif
}
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,145 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CPU_PATCH_TABLE_H
#define OPENSUBDIV3_OSD_CPU_PATCH_TABLE_H
#include "../version.h"
#include <vector>
#include "../far/patchDescriptor.h"
#include "../osd/nonCopyable.h"
#include "../osd/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far{
class PatchTable;
};
namespace Osd {
/// \brief Cpu patch table
///
/// XXX: We can use just Far::PatchTable for typical CpuEval use cases.
///
/// Currently this class exists because of the template resolution
/// for the CpuEvaluator's generic interface functions
/// (glEvalLimit example uses), and
/// device-specific patch tables such as GLPatchTables internally use
/// as a staging buffer to splice patcharray and interleave sharpnesses.
///
/// Ideally Far::PatchTables should have the same data representation
/// and accessors so that we don't have to copy data unnecessarily.
///
class CpuPatchTable {
public:
static CpuPatchTable *Create(const Far::PatchTable *patchTable,
void *deviceContext = NULL) {
(void)deviceContext; // unused
return new CpuPatchTable(patchTable);
}
explicit CpuPatchTable(const Far::PatchTable *patchTable);
~CpuPatchTable() {}
const PatchArray *GetPatchArrayBuffer() const {
return &_patchArrays[0];
}
const int *GetPatchIndexBuffer() const {
return &_indexBuffer[0];
}
const PatchParam *GetPatchParamBuffer() const {
return &_patchParamBuffer[0];
}
size_t GetNumPatchArrays() const {
return _patchArrays.size();
}
size_t GetPatchIndexSize() const {
return _indexBuffer.size();
}
size_t GetPatchParamSize() const {
return _patchParamBuffer.size();
}
const PatchArray *GetVaryingPatchArrayBuffer() const {
if (_varyingPatchArrays.empty()) {
return NULL;
}
return &_varyingPatchArrays[0];
}
const int *GetVaryingPatchIndexBuffer() const {
if (_varyingIndexBuffer.empty()) {
return NULL;
}
return &_varyingIndexBuffer[0];
}
size_t GetVaryingPatchIndexSize() const {
return _varyingIndexBuffer.size();
}
int GetNumFVarChannels() const {
return (int)_fvarPatchArrays.size();
}
const PatchArray *GetFVarPatchArrayBuffer(int fvarChannel = 0) const {
return &_fvarPatchArrays[fvarChannel][0];
}
const int *GetFVarPatchIndexBuffer(int fvarChannel = 0) const {
return &_fvarIndexBuffers[fvarChannel][0];
}
size_t GetFVarPatchIndexSize(int fvarChannel = 0) const {
return _fvarIndexBuffers[fvarChannel].size();
}
const PatchParam *GetFVarPatchParamBuffer(int fvarChannel= 0) const {
return &_fvarParamBuffers[fvarChannel][0];
}
size_t GetFVarPatchParamSize(int fvarChannel = 0) const {
return _fvarParamBuffers[fvarChannel].size();
}
protected:
PatchArrayVector _patchArrays;
std::vector<int> _indexBuffer;
PatchParamVector _patchParamBuffer;
PatchArrayVector _varyingPatchArrays;
std::vector<int> _varyingIndexBuffer;
std::vector< PatchArrayVector > _fvarPatchArrays;
std::vector< std::vector<int> > _fvarIndexBuffers;
std::vector< PatchParamVector > _fvarParamBuffers;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CPU_PATCH_TABLE_H

View File

@@ -0,0 +1,84 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/cpuVertexBuffer.h"
#include <string.h>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CpuVertexBuffer::CpuVertexBuffer(int numElements, int numVertices)
: _numElements(numElements),
_numVertices(numVertices),
_cpuBuffer(NULL) {
_cpuBuffer = new float[numElements * numVertices];
}
CpuVertexBuffer::~CpuVertexBuffer() {
delete[] _cpuBuffer;
}
CpuVertexBuffer *
CpuVertexBuffer::Create(int numElements, int numVertices,
void * /*deviceContext*/) {
return new CpuVertexBuffer(numElements, numVertices);
}
void
CpuVertexBuffer::UpdateData(const float *src, int startVertex, int numVertices,
void * /*deviceContext*/) {
memcpy(_cpuBuffer + startVertex * _numElements,
src, GetNumElements() * numVertices * sizeof(float));
}
int
CpuVertexBuffer::GetNumElements() const {
return _numElements;
}
int
CpuVertexBuffer::GetNumVertices() const {
return _numVertices;
}
float*
CpuVertexBuffer::BindCpuBuffer() {
return _cpuBuffer;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,83 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CPU_VERTEX_BUFFER_H
#define OPENSUBDIV3_OSD_CPU_VERTEX_BUFFER_H
#include "../version.h"
#include <cstddef>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
/// \brief Concrete vertex buffer class for CPU subdivision.
///
/// CpuVertexBuffer implements the VertexBufferInterface. An instance
/// of this buffer class can be passed to CpuEvaluator
///
class CpuVertexBuffer {
public:
/// Creator. Returns NULL if error.
static CpuVertexBuffer * Create(int numElements, int numVertices,
void *deviceContext = NULL);
/// Destructor.
~CpuVertexBuffer();
/// This method is meant to be used in client code in order to provide
/// coarse vertices data to Osd.
void UpdateData(const float *src, int startVertex, int numVertices,
void *deviceContext = NULL);
/// Returns how many elements defined in this vertex buffer.
int GetNumElements() const;
/// Returns how many vertices allocated in this vertex buffer.
int GetNumVertices() const;
/// Returns the address of CPU buffer
float * BindCpuBuffer();
protected:
/// Constructor.
CpuVertexBuffer(int numElements, int numVertices);
private:
int _numElements;
int _numVertices;
float *_cpuBuffer;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CPU_VERTEX_BUFFER_H

View File

@@ -0,0 +1,151 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/cudaD3D11VertexBuffer.h"
#include "../far/error.h"
#include <D3D11.h>
#include <cuda_runtime.h>
#include <cuda_d3d11_interop.h>
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CudaD3D11VertexBuffer::CudaD3D11VertexBuffer(int numElements, int numVertices)
: _numElements(numElements), _numVertices(numVertices),
_d3d11Buffer(NULL), _cudaBuffer(NULL), _cudaResource(NULL) {
}
CudaD3D11VertexBuffer::~CudaD3D11VertexBuffer() {
unmap();
cudaGraphicsUnregisterResource(_cudaResource);
_d3d11Buffer->Release();
}
CudaD3D11VertexBuffer *
CudaD3D11VertexBuffer::Create(int numElements, int numVertices,
ID3D11DeviceContext *deviceContext) {
CudaD3D11VertexBuffer *instance =
new CudaD3D11VertexBuffer(numElements, numVertices);
ID3D11Device *device;
deviceContext->GetDevice(&device);
if (instance->allocate(device)) return instance;
delete instance;
return NULL;
}
void
CudaD3D11VertexBuffer::UpdateData(const float *src,
int startVertex, int numVertices,
void * /*deviceContext*/) {
map();
cudaMemcpy((float*)_cudaBuffer + _numElements * startVertex,
src, _numElements * numVertices * sizeof(float),
cudaMemcpyHostToDevice);
}
int
CudaD3D11VertexBuffer::GetNumElements() const {
return _numElements;
}
int
CudaD3D11VertexBuffer::GetNumVertices() const {
return _numVertices;
}
float *
CudaD3D11VertexBuffer::BindCudaBuffer() {
map();
return (float*)_cudaBuffer;
}
ID3D11Buffer *
CudaD3D11VertexBuffer::BindD3D11Buffer(ID3D11DeviceContext *deviceContext) {
unmap();
return _d3d11Buffer;
}
bool
CudaD3D11VertexBuffer::allocate(ID3D11Device *device) {
D3D11_BUFFER_DESC hBufferDesc;
hBufferDesc.ByteWidth = _numElements * _numVertices * sizeof(float);
hBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
hBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_SHADER_RESOURCE;
hBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hBufferDesc.MiscFlags = 0;
hBufferDesc.StructureByteStride = sizeof(float);
HRESULT hr;
hr = device->CreateBuffer(&hBufferDesc, NULL, &_d3d11Buffer);
if(FAILED(hr)) {
Far::Error(Far::FAR_RUNTIME_ERROR,
"Fail in CreateBuffer\n");
return false;
}
// register d3d11buffer as cuda resource
cudaError_t err = cudaGraphicsD3D11RegisterResource(
&_cudaResource, _d3d11Buffer, cudaGraphicsRegisterFlagsNone);
if (err != cudaSuccess) return false;
return true;
}
void
CudaD3D11VertexBuffer::map() {
if (_cudaBuffer) return;
size_t num_bytes;
void *ptr;
cudaGraphicsMapResources(1, &_cudaResource, 0);
cudaGraphicsResourceGetMappedPointer(&ptr, &num_bytes, _cudaResource);
_cudaBuffer = ptr;
}
void
CudaD3D11VertexBuffer::unmap() {
if (_cudaBuffer == NULL) return;
cudaGraphicsUnmapResources(1, &_cudaResource, 0);
_cudaBuffer = NULL;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,109 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CUDA_D3D11_VERTEX_BUFFER_H
#define OPENSUBDIV3_OSD_CUDA_D3D11_VERTEX_BUFFER_H
#include "../version.h"
struct cudaGraphicsResource;
struct ID3D11Buffer;
struct ID3D11Device;
struct ID3D11DeviceContext;
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
/// \brief Concrete vertex buffer class for cuda subdivision and D3D11 drawing.
///
/// CudaD3D11VertexBuffer implements CudaVertexBufferInterface and
/// D3D11VertexBufferInterface.
///
/// The buffer interop between Cuda and D3D is handled automatically when a
/// client calls BindCudaBuffer and BindVBO methods.
///
class CudaD3D11VertexBuffer {
public:
/// Creator. Returns NULL if error.
static CudaD3D11VertexBuffer * Create(int numElements,
int numVertices,
ID3D11DeviceContext *deviceContext);
/// Destructor.
virtual ~CudaD3D11VertexBuffer();
/// This method is meant to be used in client code in order to provide coarse
/// vertices data to Osd.
void UpdateData(const float *src, int startVertex, int numVertices,
void * /*deviceContext*/);
/// Returns how many elements defined in this vertex buffer.
int GetNumElements() const;
/// Returns how many vertices allocated in this vertex buffer.
int GetNumVertices() const;
/// Returns cuda memory. DX buffer will be mapped to cuda resource
/// if necessary.
float * BindCudaBuffer();
/// Returns the D3D11 buffer object.
ID3D11Buffer *BindD3D11Buffer(ID3D11DeviceContext *deviceContext);
/// Returns the D3D11 buffer object (for Osd::Mesh interface)
ID3D11Buffer *BindVBO(ID3D11DeviceContext *deviceContext) {
return BindD3D11Buffer(deviceContext);
}
protected:
/// Constructor.
CudaD3D11VertexBuffer(int numElements, int numVertices);
bool allocate(ID3D11Device *device);
// Acquires a cuda resource from DX11
void map();
// Releases a cuda resource to DX11
void unmap();
private:
int _numElements;
int _numVertices;
ID3D11Buffer *_d3d11Buffer;
void *_cudaBuffer;
cudaGraphicsResource *_cudaResource;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CUDA_D3D11_VERTEX_BUFFER_H

View File

@@ -0,0 +1,385 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/cudaEvaluator.h"
#include <cuda_runtime.h>
#include <vector>
#include "../far/stencilTable.h"
#include "../osd/types.h"
extern "C" {
void CudaEvalStencils(const float *src,
float *dst,
int length,
int srcStride,
int dstStride,
const int * sizes,
const int * offsets,
const int * indices,
const float * weights,
int start,
int end);
void CudaEvalPatches(
const float *src, float *dst,
int length, int srcStride, int dstStride,
int numPatchCoords,
const void *patchCoords,
const void *patchArrays,
const int *patchIndices,
const void *patchParams);
void CudaEvalPatchesWithDerivatives(
const float *src, float *dst,
float *du, float *dv,
float *duu, float *duv, float *dvv,
int length, int srcStride, int dstStride,
int duStride, int dvStride,
int duuStride, int duvStride, int dvvStride,
int numPatchCoords,
const void *patchCoords,
const void *patchArrays,
const int *patchIndices,
const void *patchParams);
}
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
template <class T> void *
createCudaBuffer(std::vector<T> const & src) {
if (src.empty()) {
return NULL;
}
void * devicePtr = 0;
size_t size = src.size()*sizeof(T);
cudaError_t err = cudaMalloc(&devicePtr, size);
if (err != cudaSuccess) {
return devicePtr;
}
err = cudaMemcpy(devicePtr, &src.at(0), size, cudaMemcpyHostToDevice);
if (err != cudaSuccess) {
cudaFree(devicePtr);
return 0;
}
return devicePtr;
}
// ----------------------------------------------------------------------------
CudaStencilTable::CudaStencilTable(Far::StencilTable const *stencilTable) {
_numStencils = stencilTable->GetNumStencils();
if (_numStencils > 0) {
_sizes = createCudaBuffer(stencilTable->GetSizes());
_offsets = createCudaBuffer(stencilTable->GetOffsets());
_indices = createCudaBuffer(stencilTable->GetControlIndices());
_weights = createCudaBuffer(stencilTable->GetWeights());
_duWeights = _dvWeights = NULL;
_duuWeights = _duvWeights = _dvvWeights = NULL;
} else {
_sizes = _offsets = _indices = _weights = NULL;
_duWeights = _dvWeights = NULL;
_duuWeights = _duvWeights = _dvvWeights = NULL;
}
}
CudaStencilTable::CudaStencilTable(Far::LimitStencilTable const *limitStencilTable) {
_numStencils = limitStencilTable->GetNumStencils();
if (_numStencils > 0) {
_sizes = createCudaBuffer(limitStencilTable->GetSizes());
_offsets = createCudaBuffer(limitStencilTable->GetOffsets());
_indices = createCudaBuffer(limitStencilTable->GetControlIndices());
_weights = createCudaBuffer(limitStencilTable->GetWeights());
_duWeights = createCudaBuffer(limitStencilTable->GetDuWeights());
_dvWeights = createCudaBuffer(limitStencilTable->GetDvWeights());
_duuWeights = createCudaBuffer(limitStencilTable->GetDuuWeights());
_duvWeights = createCudaBuffer(limitStencilTable->GetDuvWeights());
_dvvWeights = createCudaBuffer(limitStencilTable->GetDvvWeights());
} else {
_sizes = _offsets = _indices = _weights = NULL;
_duWeights = _dvWeights = NULL;
_duuWeights = _duvWeights = _dvvWeights = NULL;
}
}
CudaStencilTable::~CudaStencilTable() {
if (_sizes) cudaFree(_sizes);
if (_offsets) cudaFree(_offsets);
if (_indices) cudaFree(_indices);
if (_weights) cudaFree(_weights);
if (_duWeights) cudaFree(_duWeights);
if (_dvWeights) cudaFree(_dvWeights);
if (_duuWeights) cudaFree(_duuWeights);
if (_duvWeights) cudaFree(_duvWeights);
if (_dvvWeights) cudaFree(_dvvWeights);
}
// ---------------------------------------------------------------------------
/* static */
bool
CudaEvaluator::EvalStencils(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
const int * sizes,
const int * offsets,
const int * indices,
const float * weights,
int start,
int end) {
if (dst == NULL) return false;
CudaEvalStencils(src + srcDesc.offset,
dst + dstDesc.offset,
srcDesc.length,
srcDesc.stride,
dstDesc.stride,
sizes, offsets, indices, weights,
start, end);
return true;
}
/* static */
bool
CudaEvaluator::EvalStencils(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
float *du, BufferDescriptor const &duDesc,
float *dv, BufferDescriptor const &dvDesc,
const int * sizes,
const int * offsets,
const int * indices,
const float * weights,
const float * duWeights,
const float * dvWeights,
int start,
int end) {
// PERFORMANCE: need to combine 3 launches together
if (dst) {
CudaEvalStencils(src + srcDesc.offset,
dst + dstDesc.offset,
srcDesc.length,
srcDesc.stride,
dstDesc.stride,
sizes, offsets, indices, weights,
start, end);
}
if (du) {
CudaEvalStencils(src + srcDesc.offset,
du + duDesc.offset,
srcDesc.length,
srcDesc.stride,
duDesc.stride,
sizes, offsets, indices, duWeights,
start, end);
}
if (dv) {
CudaEvalStencils(src + srcDesc.offset,
dv + dvDesc.offset,
srcDesc.length,
srcDesc.stride,
dvDesc.stride,
sizes, offsets, indices, dvWeights,
start, end);
}
return true;
}
/* static */
bool
CudaEvaluator::EvalStencils(const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
float *du, BufferDescriptor const &duDesc,
float *dv, BufferDescriptor const &dvDesc,
float *duu, BufferDescriptor const &duuDesc,
float *duv, BufferDescriptor const &duvDesc,
float *dvv, BufferDescriptor const &dvvDesc,
const int * sizes,
const int * offsets,
const int * indices,
const float * weights,
const float * duWeights,
const float * dvWeights,
const float * duuWeights,
const float * duvWeights,
const float * dvvWeights,
int start,
int end) {
// PERFORMANCE: need to combine 3 launches together
if (dst) {
CudaEvalStencils(src + srcDesc.offset,
dst + dstDesc.offset,
srcDesc.length,
srcDesc.stride,
dstDesc.stride,
sizes, offsets, indices, weights,
start, end);
}
if (du) {
CudaEvalStencils(src + srcDesc.offset,
du + duDesc.offset,
srcDesc.length,
srcDesc.stride,
duDesc.stride,
sizes, offsets, indices, duWeights,
start, end);
}
if (dv) {
CudaEvalStencils(src + srcDesc.offset,
dv + dvDesc.offset,
srcDesc.length,
srcDesc.stride,
dvDesc.stride,
sizes, offsets, indices, dvWeights,
start, end);
}
if (duu) {
CudaEvalStencils(src + srcDesc.offset,
duu + duuDesc.offset,
srcDesc.length,
srcDesc.stride,
duuDesc.stride,
sizes, offsets, indices, duuWeights,
start, end);
}
if (duv) {
CudaEvalStencils(src + srcDesc.offset,
duv + duvDesc.offset,
srcDesc.length,
srcDesc.stride,
duvDesc.stride,
sizes, offsets, indices, duvWeights,
start, end);
}
if (dvv) {
CudaEvalStencils(src + srcDesc.offset,
dvv + dvvDesc.offset,
srcDesc.length,
srcDesc.stride,
dvvDesc.stride,
sizes, offsets, indices, dvvWeights,
start, end);
}
return true;
}
/* static */
bool
CudaEvaluator::EvalPatches(const float *src,
BufferDescriptor const &srcDesc,
float *dst,
BufferDescriptor const &dstDesc,
int numPatchCoords,
const PatchCoord *patchCoords,
const PatchArray *patchArrays,
const int *patchIndices,
const PatchParam *patchParams) {
if (src) src += srcDesc.offset;
if (dst) dst += dstDesc.offset;
CudaEvalPatches(src, dst,
srcDesc.length, srcDesc.stride, dstDesc.stride,
numPatchCoords, patchCoords, patchArrays, patchIndices, patchParams);
return true;
}
/* static */
bool
CudaEvaluator::EvalPatches(
const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
float *du, BufferDescriptor const &duDesc,
float *dv, BufferDescriptor const &dvDesc,
int numPatchCoords,
const PatchCoord *patchCoords,
const PatchArray *patchArrays,
const int *patchIndices,
const PatchParam *patchParams) {
if (src) src += srcDesc.offset;
if (dst) dst += dstDesc.offset;
if (du) du += duDesc.offset;
if (dv) dv += dvDesc.offset;
CudaEvalPatchesWithDerivatives(
src, dst, du, dv, NULL, NULL, NULL,
srcDesc.length, srcDesc.stride, dstDesc.stride,
duDesc.stride, dvDesc.stride, 0, 0, 0,
numPatchCoords, patchCoords, patchArrays, patchIndices, patchParams);
return true;
}
/* static */
bool
CudaEvaluator::EvalPatches(
const float *src, BufferDescriptor const &srcDesc,
float *dst, BufferDescriptor const &dstDesc,
float *du, BufferDescriptor const &duDesc,
float *dv, BufferDescriptor const &dvDesc,
float *duu, BufferDescriptor const &duuDesc,
float *duv, BufferDescriptor const &duvDesc,
float *dvv, BufferDescriptor const &dvvDesc,
int numPatchCoords,
const PatchCoord *patchCoords,
const PatchArray *patchArrays,
const int *patchIndices,
const PatchParam *patchParams) {
if (src) src += srcDesc.offset;
if (dst) dst += dstDesc.offset;
if (du) du += duDesc.offset;
if (dv) dv += dvDesc.offset;
if (duu) duu += duuDesc.offset;
if (duv) duv += duvDesc.offset;
if (dvv) dvv += dvvDesc.offset;
CudaEvalPatchesWithDerivatives(
src, dst, du, dv, duu, duv, dvv,
srcDesc.length, srcDesc.stride, dstDesc.stride,
duDesc.stride, dvDesc.stride,
duuDesc.stride, duvDesc.stride, dvvDesc.stride,
numPatchCoords, patchCoords, patchArrays, patchIndices, patchParams);
return true;
}
/* static */
void
CudaEvaluator::Synchronize(void * /*deviceContext*/) {
cudaThreadSynchronize();
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,173 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "glLoader.h"
#include "../osd/cudaGLVertexBuffer.h"
#include "../far/error.h"
#include <cuda_runtime.h>
#include <cuda_gl_interop.h>
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CudaGLVertexBuffer::CudaGLVertexBuffer(int numElements, int numVertices)
: _numElements(numElements), _numVertices(numVertices),
_vbo(0), _devicePtr(0), _cudaResource(0) {
// Initialize internal OpenGL loader library if necessary
OpenSubdiv::internal::GLLoader::libraryInitializeGL();
}
CudaGLVertexBuffer::~CudaGLVertexBuffer() {
unmap();
cudaGraphicsUnregisterResource(_cudaResource);
glDeleteBuffers(1, &_vbo);
}
CudaGLVertexBuffer *
CudaGLVertexBuffer::Create(int numElements, int numVertices, void *) {
CudaGLVertexBuffer *instance =
new CudaGLVertexBuffer(numElements, numVertices);
if (instance->allocate()) return instance;
Far::Error(Far::FAR_RUNTIME_ERROR, "CudaGLVertexBuffer::Create failed.\n");
delete instance;
return NULL;
}
void
CudaGLVertexBuffer::UpdateData(const float *src,
int startVertex, int numVertices,
void * /*deviceContext*/) {
map();
cudaError_t err = cudaMemcpy((float*)_devicePtr + _numElements * startVertex,
src,
_numElements * numVertices * sizeof(float),
cudaMemcpyHostToDevice);
if (err != cudaSuccess)
Far::Error(Far::FAR_RUNTIME_ERROR,
"CudaGLVertexBuffer::UpdateData failed. : %s\n",
cudaGetErrorString(err));
}
int
CudaGLVertexBuffer::GetNumElements() const {
return _numElements;
}
int
CudaGLVertexBuffer::GetNumVertices() const {
return _numVertices;
}
float *
CudaGLVertexBuffer::BindCudaBuffer() {
map();
return static_cast<float*>(_devicePtr);
}
GLuint
CudaGLVertexBuffer::BindVBO(void * /*deviceContext*/) {
unmap();
return _vbo;
}
bool
CudaGLVertexBuffer::allocate() {
int size = _numElements * _numVertices * sizeof(float);
#if defined(GL_ARB_direct_state_access)
if (OSD_OPENGL_HAS(ARB_direct_state_access)) {
glCreateBuffers(1, &_vbo);
glNamedBufferData(_vbo, size, 0, GL_DYNAMIC_DRAW);
} else
#endif
{
GLint prev = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prev);
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, prev);
}
// register vbo as cuda resource
cudaError_t err = cudaGraphicsGLRegisterBuffer(
&_cudaResource, _vbo, cudaGraphicsMapFlagsWriteDiscard);
if (err != cudaSuccess) return false;
return true;
}
void
CudaGLVertexBuffer::map() {
if (_devicePtr) return;
size_t num_bytes;
void *ptr;
cudaError_t err = cudaGraphicsMapResources(1, &_cudaResource, 0);
if (err != cudaSuccess)
Far::Error(Far::FAR_RUNTIME_ERROR,
"CudaGLVertexBuffer::map failed.\n%s\n",
cudaGetErrorString(err));
err = cudaGraphicsResourceGetMappedPointer(&ptr, &num_bytes, _cudaResource);
if (err != cudaSuccess)
Far::Error(Far::FAR_RUNTIME_ERROR,
"CudaGLVertexBuffer::map failed.\n%s\n",
cudaGetErrorString(err));
_devicePtr = ptr;
}
void
CudaGLVertexBuffer::unmap() {
if (_devicePtr == NULL) return;
cudaError_t err = cudaGraphicsUnmapResources(1, &_cudaResource, 0);
if (err != cudaSuccess)
Far::Error(Far::FAR_RUNTIME_ERROR,
"CudaGLVertexBuffer::unmap failed.\n%s\n",
cudaGetErrorString(err));
_devicePtr = NULL;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,108 @@
//
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CUDA_GL_VERTEX_BUFFER_H
#define OPENSUBDIV3_OSD_CUDA_GL_VERTEX_BUFFER_H
#include "../version.h"
#include <cstddef>
#include <cuda_runtime.h>
#include "../osd/opengl.h" // needed before cuda_gl_interop.h
#include <cuda_gl_interop.h>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
/// \brief Concrete vertex buffer class for cuda subdivision and OpenGL drawing.
///
/// CudaGLVertexBuffer implements CudaVertexBufferInterface and
/// GLVertexBufferInterface.
///
/// The buffer interop between Cuda and GL is handled automatically when a
/// client calls BindCudaBuffer and BindVBO methods.
///
class CudaGLVertexBuffer {
public:
/// Creator. Returns NULL if error.
static CudaGLVertexBuffer * Create(int numElements, int numVertices,
void *deviceContext = NULL);
/// Destructor.
~CudaGLVertexBuffer();
/// This method is meant to be used in client code in order to provide
/// coarse vertices data to Osd.
void UpdateData(const float *src, int startVertex, int numVertices,
void *deviceContext = NULL);
/// Returns how many elements defined in this vertex buffer.
int GetNumElements() const;
/// Returns how many vertices allocated in this vertex buffer.
int GetNumVertices() const;
/// Returns cuda memory. GL buffer will be mapped to cuda resource
/// if necessary.
float * BindCudaBuffer();
/// Returns the GL buffer object. If the buffer is mapped as a cuda
/// resource, it will be unmapped back to GL.
GLuint BindVBO(void *deviceContext = NULL);
protected:
/// Constructor.
CudaGLVertexBuffer(int numElements, int numVertices);
/// Allocates VBO for this buffer and register as a cuda resource.
/// Returns true if success.
bool allocate();
/// Acquire a cuda resource from GL.
void map();
/// Release a cuda resource to GL.
void unmap();
private:
int _numElements;
int _numVertices;
GLuint _vbo;
void *_devicePtr;
struct cudaGraphicsResource *_cudaResource;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CUDA_GL_VERTEX_BUFFER_H

View File

@@ -0,0 +1,427 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include <assert.h>
#define OSD_PATCH_BASIS_CUDA
#include "../osd/patchBasisCommonTypes.h"
#include "../osd/patchBasisCommon.h"
#include "../osd/patchBasisCommonEval.h"
// -----------------------------------------------------------------------------
template<int N> struct DeviceVertex {
float v[N];
__device__ void addWithWeight(DeviceVertex<N> const & src, float weight) {
#pragma unroll
for(int i = 0; i < N; ++i){
v[i] += src.v[i] * weight;
}
}
__device__ void clear() {
#pragma unroll
for(int i = 0; i < N; ++i){
v[i] = 0.0f;
}
}
};
// Specialize DeviceVertex for N=0 to avoid compile error:
// "flexible array member in otherwise empty struct"
template<> struct DeviceVertex<0> {
__device__ void addWithWeight(DeviceVertex<0> &src, float weight) {}
__device__ void clear() {}
};
// -----------------------------------------------------------------------------
__device__ void clear(float *dst, int count)
{
for(int i = 0; i < count; ++i) dst[i] = 0;
}
__device__ void addWithWeight(float *dst, float const *src, float weight, int count)
{
for(int i = 0; i < count; ++i) dst[i] += src[i] * weight;
}
// --------------------------------------------------------------------------------------------
template <int NUM_ELEMENTS> __global__ void
computeStencils(float const * cvs, float * vbuffer,
int const * sizes,
int const * offsets,
int const * indices,
float const * weights,
int start, int end) {
DeviceVertex<NUM_ELEMENTS> const * src =
(DeviceVertex<NUM_ELEMENTS> const *)cvs;
DeviceVertex<NUM_ELEMENTS> * verts =
(DeviceVertex<NUM_ELEMENTS> *)vbuffer;
int first = start + threadIdx.x + blockIdx.x*blockDim.x;
for (int i=first; i<end; i += blockDim.x * gridDim.x) {
int const * lindices = indices + offsets[i];
float const * lweights = weights + offsets[i];
DeviceVertex<NUM_ELEMENTS> dst;
dst.clear();
for (int j=0; j<sizes[i]; ++j) {
dst.addWithWeight(src[lindices[j]], lweights[j]);
}
verts[i] = dst;
}
}
__global__ void
computeStencils(float const * cvs, float * dst,
int length,
int srcStride,
int dstStride,
int const * sizes,
int const * offsets,
int const * indices,
float const * weights,
int start, int end) {
int first = start + threadIdx.x + blockIdx.x*blockDim.x;
for (int i=first; i<end; i += blockDim.x * gridDim.x) {
int const * lindices = indices + offsets[i];
float const * lweights = weights + offsets[i];
float * dstVert = dst + i*dstStride;
clear(dstVert, length);
for (int j=0; j<sizes[i]; ++j) {
float const * srcVert = cvs + lindices[j]*srcStride;
addWithWeight(dstVert, srcVert, lweights[j], length);
}
}
}
// -----------------------------------------------------------------------------
#define USE_NVIDIA_OPTIMIZATION
#ifdef USE_NVIDIA_OPTIMIZATION
template< int NUM_ELEMENTS, int NUM_THREADS_PER_BLOCK >
__global__ void computeStencilsNv(float const *__restrict cvs,
float * vbuffer,
int const *__restrict sizes,
int const *__restrict offsets,
int const *__restrict indices,
float const *__restrict weights,
int start,
int end)
{
// Shared memory to stage indices/weights.
__shared__ int smem_indices_buffer[NUM_THREADS_PER_BLOCK];
__shared__ float smem_weights_buffer[NUM_THREADS_PER_BLOCK];
// The size of a single warp.
const int WARP_SIZE = 32;
// The number of warps per block.
const int NUM_WARPS_PER_BLOCK = NUM_THREADS_PER_BLOCK / WARP_SIZE;
// The number of outputs computed by a single warp.
const int NUM_OUTPUTS_PER_WARP = WARP_SIZE / NUM_ELEMENTS;
// The number of outputs computed by a block of threads.
const int NUM_OUTPUTS_PER_BLOCK = NUM_OUTPUTS_PER_WARP*NUM_WARPS_PER_BLOCK;
// The number of active threads in a warp.
const int NUM_ACTIVE_THREADS_PER_WARP = NUM_OUTPUTS_PER_WARP * NUM_ELEMENTS;
// The number of the warp inside the block.
const int warpId = threadIdx.x / WARP_SIZE;
const int laneId = threadIdx.x % WARP_SIZE;
// We use NUM_ELEMENTS threads per output. Find which output/element a thread works on.
int outputIdx = warpId*NUM_OUTPUTS_PER_WARP + laneId/NUM_ELEMENTS, elementIdx = laneId%NUM_ELEMENTS;
// Each output corresponds to a section of shared memory.
volatile int *smem_indices = &smem_indices_buffer[warpId*WARP_SIZE + (laneId/NUM_ELEMENTS)*NUM_ELEMENTS];
volatile float *smem_weights = &smem_weights_buffer[warpId*WARP_SIZE + (laneId/NUM_ELEMENTS)*NUM_ELEMENTS];
// Disable threads that have nothing to do inside the warp.
int i = end;
if( laneId < NUM_ACTIVE_THREADS_PER_WARP )
i = start + blockIdx.x*NUM_OUTPUTS_PER_BLOCK + outputIdx;
// Iterate over the vertices.
for( ; i < end ; i += gridDim.x*NUM_OUTPUTS_PER_BLOCK )
{
// Each thread computes an element of the final vertex.
float x = 0.f;
// Load the offset and the size for each vertex. We have NUM_THREADS_PER_VERTEX threads loading the same value.
const int offset_i = offsets[i], size_i = sizes[i];
// Iterate over the stencil.
for( int j = offset_i, j_end = offset_i+size_i ; j < j_end ; )
{
int j_it = j + elementIdx;
// Load some indices and some weights. The transaction is coalesced.
smem_indices[elementIdx] = j_it < j_end ? indices[j_it] : 0;
smem_weights[elementIdx] = j_it < j_end ? weights[j_it] : 0.f;
// Thread now collaborates to load the vertices.
#pragma unroll
for( int k = 0 ; k < NUM_ELEMENTS ; ++k, ++j )
if( j < j_end )
x += smem_weights[k] * cvs[smem_indices[k]*NUM_ELEMENTS + elementIdx];
}
// Store the vertex.
vbuffer[NUM_ELEMENTS*i + elementIdx] = x;
}
}
template< int NUM_THREADS_PER_BLOCK >
__global__ void computeStencilsNv_v4(float const *__restrict cvs,
float * vbuffer,
int const *__restrict sizes,
int const *__restrict offsets,
int const *__restrict indices,
float const *__restrict weights,
int start,
int end)
{
// Iterate over the vertices.
for( int i = start + blockIdx.x*NUM_THREADS_PER_BLOCK + threadIdx.x ; i < end ; i += gridDim.x*NUM_THREADS_PER_BLOCK )
{
// Each thread computes an element of the final vertex.
float4 x = make_float4(0.f, 0.f, 0.f, 0.f);
// Iterate over the stencil.
for( int j = offsets[i], j_end = offsets[i]+sizes[i] ; j < j_end ; ++j )
{
float w = weights[j];
float4 tmp = reinterpret_cast<const float4 *>(cvs)[indices[j]];
x.x += w*tmp.x;
x.y += w*tmp.y;
x.z += w*tmp.z;
x.w += w*tmp.w;
}
// Store the vertex.
reinterpret_cast<float4*>(vbuffer)[i] = x;
}
}
#endif // USE_NVIDIA_OPTIMIZATION
// -----------------------------------------------------------------------------
__global__ void
computePatches(const float *src, float *dst,
float *dstDu, float *dstDv,
float *dstDuu, float *dstDuv, float *dstDvv,
int length, int srcStride, int dstStride,
int dstDuStride, int dstDvStride,
int dstDuuStride, int dstDuvStride, int dstDvvStride,
int numPatchCoords, const OsdPatchCoord *patchCoords,
const OsdPatchArray *patchArrayBuffer,
const int *patchIndexBuffer,
const OsdPatchParam *patchParamBuffer) {
int first = threadIdx.x + blockIdx.x * blockDim.x;
// PERFORMANCE: not yet optimized
for (int i = first; i < numPatchCoords; i += blockDim.x * gridDim.x) {
OsdPatchCoord const &coord = patchCoords[i];
int arrayIndex = coord.arrayIndex;
int patchIndex = coord.patchIndex;
OsdPatchArray const &array = patchArrayBuffer[arrayIndex];
OsdPatchParam const &param = patchParamBuffer[patchIndex];
int patchType = OsdPatchParamIsRegular(param)
? array.regDesc : array.desc;
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
int nPoints = OsdEvaluatePatchBasis(patchType, param,
coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
int indexBase = array.indexBase + array.stride *
(patchIndex - array.primitiveIdBase);
const int *cvs = patchIndexBuffer + indexBase;
float * dstVert = dst + i * dstStride;
clear(dstVert, length);
for (int j = 0; j < nPoints; ++j) {
const float * srcVert = src + cvs[j] * srcStride;
addWithWeight(dstVert, srcVert, wP[j], length);
}
if (dstDu) {
float *d = dstDu + i * dstDuStride;
clear(d, length);
for (int j = 0; j < nPoints; ++j) {
const float * srcVert = src + cvs[j] * srcStride;
addWithWeight(d, srcVert, wDu[j], length);
}
}
if (dstDv) {
float *d = dstDv + i * dstDvStride;
clear(d, length);
for (int j = 0; j < nPoints; ++j) {
const float * srcVert = src + cvs[j] * srcStride;
addWithWeight(d, srcVert, wDv[j], length);
}
}
if (dstDuu) {
float *d = dstDuu + i * dstDuuStride;
clear(d, length);
for (int j = 0; j < nPoints; ++j) {
const float * srcVert = src + cvs[j] * srcStride;
addWithWeight(d, srcVert, wDuu[j], length);
}
}
if (dstDuv) {
float *d = dstDuv + i * dstDuvStride;
clear(d, length);
for (int j = 0; j < nPoints; ++j) {
const float * srcVert = src + cvs[j] * srcStride;
addWithWeight(d, srcVert, wDuv[j], length);
}
}
if (dstDvv) {
float *d = dstDvv + i * dstDvvStride;
clear(d, length);
for (int j = 0; j < nPoints; ++j) {
const float * srcVert = src + cvs[j] * srcStride;
addWithWeight(d, srcVert, wDvv[j], length);
}
}
}
}
// -----------------------------------------------------------------------------
#include "../version.h"
#define OPT_KERNEL(NUM_ELEMENTS, KERNEL, X, Y, ARG) \
if (length==NUM_ELEMENTS && srcStride==length && dstStride==length) { \
KERNEL<NUM_ELEMENTS><<<X,Y>>>ARG; \
return; \
}
#ifdef USE_NVIDIA_OPTIMIZATION
#define OPT_KERNEL_NVIDIA(NUM_ELEMENTS, KERNEL, X, Y, ARG) \
if (length==NUM_ELEMENTS && srcStride==length && dstStride==length) { \
int gridDim = min(X, (end-start+Y-1)/Y); \
KERNEL<NUM_ELEMENTS, Y><<<gridDim, Y>>>ARG; \
return; \
}
#endif
extern "C" {
void CudaEvalStencils(
const float *src, float *dst,
int length, int srcStride, int dstStride,
const int * sizes, const int * offsets, const int * indices,
const float * weights,
int start, int end) {
if (length == 0 || srcStride == 0 || dstStride == 0 || (end <= start)) {
return;
}
#ifdef USE_NVIDIA_OPTIMIZATION
OPT_KERNEL_NVIDIA(3, computeStencilsNv, 2048, 256,
(src, dst, sizes, offsets, indices, weights, start, end));
//OPT_KERNEL_NVIDIA(4, computeStencilsNv, 2048, 256,
// (cvs, dst, sizes, offsets, indices, weights, start, end));
if (length == 4 && srcStride == length && dstStride == length) {
int gridDim = min(2048, (end-start+256-1)/256);
computeStencilsNv_v4<256><<<gridDim, 256>>>(
src, dst, sizes, offsets, indices, weights, start, end);
return;
}
#else
OPT_KERNEL(3, computeStencils, 512, 32,
(src, dst, sizes, offsets, indices, weights, start, end));
OPT_KERNEL(4, computeStencils, 512, 32,
(src, dst, sizes, offsets, indices, weights, start, end));
#endif
// generic case (slow)
computeStencils <<<512, 32>>>(
src, dst, length, srcStride, dstStride,
sizes, offsets, indices, weights, start, end);
}
// -----------------------------------------------------------------------------
void CudaEvalPatches(
const float *src, float *dst,
int length, int srcStride, int dstStride,
int numPatchCoords, const OsdPatchCoord *patchCoords,
const OsdPatchArray *patchArrayBuffer,
const int *patchIndexBuffer,
const OsdPatchParam *patchParamBuffer) {
// PERFORMANCE: not optimized at all
computePatches <<<512, 32>>>(
src, dst, NULL, NULL, NULL, NULL, NULL,
length, srcStride, dstStride, 0, 0, 0, 0, 0,
numPatchCoords, patchCoords,
patchArrayBuffer, patchIndexBuffer, patchParamBuffer);
}
void CudaEvalPatchesWithDerivatives(
const float *src, float *dst,
float *dstDu, float *dstDv,
float *dstDuu, float *dstDuv, float *dstDvv,
int length, int srcStride, int dstStride,
int dstDuStride, int dstDvStride,
int dstDuuStride, int dstDuvStride, int dstDvvStride,
int numPatchCoords, const OsdPatchCoord *patchCoords,
const OsdPatchArray *patchArrayBuffer,
const int *patchIndexBuffer,
const OsdPatchParam *patchParamBuffer) {
// PERFORMANCE: not optimized at all
computePatches <<<512, 32>>>(
src, dst, dstDu, dstDv, dstDuu, dstDuv, dstDvv,
length, srcStride, dstStride,
dstDuStride, dstDvStride, dstDuuStride, dstDuvStride, dstDvvStride,
numPatchCoords, patchCoords,
patchArrayBuffer, patchIndexBuffer, patchParamBuffer);
}
} /* extern "C" */

View File

@@ -0,0 +1,169 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../osd/cudaPatchTable.h"
#include <cuda_runtime.h>
#include "../far/patchTable.h"
#include "../osd/cpuPatchTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Osd {
CudaPatchTable::CudaPatchTable() :
_patchArrays(NULL), _indexBuffer(NULL), _patchParamBuffer(NULL),
_varyingPatchArrays(NULL), _varyingIndexBuffer(NULL) {
}
CudaPatchTable::~CudaPatchTable() {
if (_patchArrays) cudaFree(_patchArrays);
if (_indexBuffer) cudaFree(_indexBuffer);
if (_patchParamBuffer) cudaFree(_patchParamBuffer);
if (_varyingPatchArrays) cudaFree(_varyingPatchArrays);
if (_varyingIndexBuffer) cudaFree(_varyingIndexBuffer);
for (int fvc=0; fvc<(int)_fvarPatchArrays.size(); ++fvc) {
if (_fvarPatchArrays[fvc]) cudaFree(_fvarPatchArrays[fvc]);
}
for (int fvc=0; fvc<(int)_fvarIndexBuffers.size(); ++fvc) {
if (_fvarIndexBuffers[fvc]) cudaFree(_fvarIndexBuffers[fvc]);
}
for (int fvc=0; fvc<(int)_fvarParamBuffers.size(); ++fvc) {
if (_fvarParamBuffers[fvc]) cudaFree(_fvarParamBuffers[fvc]);
}
}
CudaPatchTable *
CudaPatchTable::Create(Far::PatchTable const *farPatchTable,
void * /*deviceContext*/) {
CudaPatchTable *instance = new CudaPatchTable();
if (instance->allocate(farPatchTable)) return instance;
delete instance;
return 0;
}
bool
CudaPatchTable::allocate(Far::PatchTable const *farPatchTable) {
CpuPatchTable patchTable(farPatchTable);
size_t numPatchArrays = patchTable.GetNumPatchArrays();
size_t indexSize = patchTable.GetPatchIndexSize();
size_t patchParamSize = patchTable.GetPatchParamSize();
cudaError_t err;
err = cudaMalloc(&_patchArrays, numPatchArrays * sizeof(Osd::PatchArray));
if (err != cudaSuccess) return false;
err = cudaMalloc(&_indexBuffer, indexSize * sizeof(int));
if (err != cudaSuccess) return false;
err = cudaMalloc(&_patchParamBuffer, patchParamSize * sizeof(Osd::PatchParam));
if (err != cudaSuccess) return false;
err = cudaMalloc(&_varyingPatchArrays, numPatchArrays * sizeof(Osd::PatchArray));
if (err != cudaSuccess) return false;
size_t varyingIndexSize = patchTable.GetVaryingPatchIndexSize();
err = cudaMalloc(&_varyingIndexBuffer, varyingIndexSize * sizeof(int));
if (err != cudaSuccess) return false;
size_t numFVarChannels = patchTable.GetNumFVarChannels();
_fvarPatchArrays.resize(numFVarChannels, 0);
_fvarIndexBuffers.resize(numFVarChannels, 0);
_fvarParamBuffers.resize(numFVarChannels, 0);
for (int fvc=0; fvc<(int)numFVarChannels; ++fvc) {
err = cudaMalloc(&_fvarPatchArrays[fvc], numPatchArrays * sizeof(Osd::PatchArray));
if (err != cudaSuccess) return false;
err = cudaMemcpy(_fvarPatchArrays[fvc],
patchTable.GetFVarPatchArrayBuffer(fvc),
numPatchArrays * sizeof(Osd::PatchArray),
cudaMemcpyHostToDevice);
if (err != cudaSuccess) return false;
size_t fvarIndexSize = patchTable.GetFVarPatchIndexSize(fvc);
err = cudaMalloc(&_fvarIndexBuffers[fvc], fvarIndexSize * sizeof(int));
if (err != cudaSuccess) return false;
err = cudaMemcpy(_fvarIndexBuffers[fvc],
patchTable.GetFVarPatchIndexBuffer(fvc),
fvarIndexSize * sizeof(int),
cudaMemcpyHostToDevice);
if (err != cudaSuccess) return false;
size_t fvarParamSize = patchTable.GetFVarPatchParamSize(fvc);
err = cudaMalloc(&_fvarParamBuffers[fvc], fvarParamSize * sizeof(Osd::PatchParam));
if (err != cudaSuccess) return false;
err = cudaMemcpy(_fvarParamBuffers[fvc],
patchTable.GetFVarPatchParamBuffer(fvc),
patchParamSize * sizeof(PatchParam),
cudaMemcpyHostToDevice);
if (err != cudaSuccess) return false;
}
// copy patch array
err = cudaMemcpy(_patchArrays,
patchTable.GetPatchArrayBuffer(),
numPatchArrays * sizeof(Osd::PatchArray),
cudaMemcpyHostToDevice);
if (err != cudaSuccess) return false;
// copy index buffer
err = cudaMemcpy(_indexBuffer,
patchTable.GetPatchIndexBuffer(),
indexSize * sizeof(int),
cudaMemcpyHostToDevice);
if (err != cudaSuccess) return false;
// patch param buffer
err = cudaMemcpy(_patchParamBuffer,
patchTable.GetPatchParamBuffer(),
patchParamSize * sizeof(Osd::PatchParam),
cudaMemcpyHostToDevice);
if (err != cudaSuccess) return false;
// copy varying patch arrays and index buffer
err = cudaMemcpy(_varyingPatchArrays,
patchTable.GetVaryingPatchArrayBuffer(),
numPatchArrays * sizeof(Osd::PatchArray),
cudaMemcpyHostToDevice);
if (err != cudaSuccess) return false;
err = cudaMemcpy(_varyingIndexBuffer,
patchTable.GetVaryingPatchIndexBuffer(),
varyingIndexSize * sizeof(int),
cudaMemcpyHostToDevice);
if (err != cudaSuccess) return false;
return true;
}
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,119 @@
//
// Copyright 2015 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef OPENSUBDIV3_OSD_CUDA_PATCH_TABLE_H
#define OPENSUBDIV3_OSD_CUDA_PATCH_TABLE_H
#include "../version.h"
#include "../osd/nonCopyable.h"
#include "../osd/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far{
class PatchTable;
};
namespace Osd {
/// \brief CUDA patch table
///
/// This class is a cuda buffer representation of Far::PatchTable.
///
/// CudaEvaluator consumes this table to evaluate on the patches.
///
///
class CudaPatchTable : private NonCopyable<CudaPatchTable> {
public:
/// Creator. Returns NULL if error
static CudaPatchTable *Create(Far::PatchTable const *patchTable,
void *deviceContext = NULL);
/// Destructor
~CudaPatchTable();
/// Returns the cuda memory of the array of Osd::PatchArray buffer
void *GetPatchArrayBuffer() const { return _patchArrays; }
/// Returns the cuda memory of the patch control vertices
void *GetPatchIndexBuffer() const { return _indexBuffer; }
/// Returns the cuda memory of the array of Osd::PatchParam buffer
void *GetPatchParamBuffer() const { return _patchParamBuffer; }
/// Returns the cuda memory of the array of Osd::PatchArray buffer
void *GetVaryingPatchArrayBuffer() const {
return _varyingPatchArrays;
}
/// Returns the cuda memory of the array of varying control vertices
void *GetVaryingPatchIndexBuffer() const {
return _varyingIndexBuffer;
}
/// Returns the number of face-varying channels buffers
int GetNumFVarChannels() const { return (int)_fvarPatchArrays.size(); }
/// Returns the cuda memory of the array of Osd::PatchArray buffer
void *GetFVarPatchArrayBuffer(int fvarChannel) const {
return _fvarPatchArrays[fvarChannel];
}
/// Returns the cuda memory of the array of face-varying control vertices
void *GetFVarPatchIndexBuffer(int fvarChannel = 0) const {
return _fvarIndexBuffers[fvarChannel];
}
/// Returns the cuda memory of the array of face-varying param
void *GetFVarPatchParamBuffer(int fvarChannel = 0) const {
return _fvarParamBuffers[fvarChannel];
}
protected:
CudaPatchTable();
bool allocate(Far::PatchTable const *patchTable);
void *_patchArrays;
void *_indexBuffer;
void *_patchParamBuffer;
void *_varyingPatchArrays;
void *_varyingIndexBuffer;
std::vector<void *> _fvarPatchArrays;
std::vector<void *> _fvarIndexBuffers;
std::vector<void *> _fvarParamBuffers;
};
} // end namespace Osd
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_OSD_CUDA_PATCH_TABLE_H

Some files were not shown because too many files have changed in this diff Show More