├── .gitignore ├── subdivgui ├── clean.h ├── robust_bbw.h ├── torus.tgf ├── subdiv_weights.h ├── Mesh.h ├── torus.obj ├── robust_bbw.cpp ├── CMakeLists.txt ├── clean.cpp ├── subdiv_weights.cpp ├── Mesh.cpp ├── refine.cpp ├── subdivision_skinning_wrapper.h ├── subdivision_skinning_wrapper.cpp └── subdivision_skinning_wrapper_test.cpp ├── CMakeLists.txt ├── lib ├── Timing.hpp ├── save_obj.h ├── subdivision_matrix_types.h ├── save_obj.cpp ├── subdivision_limit_mesh.h ├── subdivision_matrices_osd_eigen.h ├── Timing.cpp ├── subdivision_matrices_eigen.h ├── subdivision_matrices_osd.h ├── CMakeLists.txt ├── subdivision_matrices.h ├── subdivision_engine.h ├── ColorMap.h ├── subdivision_skinning.h ├── subdivision_skinning_wrapper.h ├── subdivision_matrices_osd_eigen.cpp ├── subdivision_matrices_eigen.cpp ├── subdivision_skinning_wrapper.cpp ├── subdivision_limit_mesh.cpp ├── subdivision_skinning_wrapper_test.cpp ├── subdivision_engine.cpp ├── subdivision_matrices_osd.cpp └── subdivision_matrices.cpp ├── cmake └── FindEigen3.cmake ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /OpenSubdiv 3 | /libigl 4 | -------------------------------------------------------------------------------- /subdivgui/clean.h: -------------------------------------------------------------------------------- 1 | #include 2 | bool clean( 3 | const Eigen::MatrixXd & V, 4 | const Eigen::MatrixXi & F, 5 | Eigen::MatrixXd & CV, 6 | Eigen::MatrixXi & CF, 7 | Eigen::VectorXi & IM); 8 | 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 2.8.6) 3 | 4 | ## The path to the FindEigen file. 5 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") 6 | 7 | ## Build the library. 8 | add_subdirectory(lib) 9 | 10 | ## Build the GUI 11 | add_subdirectory(subdivgui) 12 | -------------------------------------------------------------------------------- /subdivgui/robust_bbw.h: -------------------------------------------------------------------------------- 1 | #ifndef ROBUST_BBW_H 2 | #define ROBUST_BBW_H 3 | #include 4 | bool robust_bbw( 5 | const Eigen::MatrixXd & V, 6 | const Eigen::MatrixXi & F, 7 | const Eigen::MatrixXd & C, 8 | const Eigen::MatrixXi & BE, 9 | Eigen::MatrixXd & W); 10 | #endif 11 | -------------------------------------------------------------------------------- /lib/Timing.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __Timing_hpp__ 2 | #define __Timing_hpp__ 3 | 4 | void tic( const char* label = 0 ) ; 5 | void toc( const char* label = 0 ) ; 6 | 7 | struct Tick 8 | { 9 | Tick( const char* alabel = 0 ) : label( alabel ) { tic( label ); } 10 | ~Tick() { toc( label ); } 11 | 12 | const char* label; 13 | }; 14 | 15 | #endif /* __Timing_hpp__ */ 16 | -------------------------------------------------------------------------------- /subdivgui/torus.tgf: -------------------------------------------------------------------------------- 1 | 1 -0.97850775718688965 -0.44190680980682373 0 2 | 2 -0.97500056090718179 0.49802185198690269 0 3 | 3 0.92239266633987427 -0.45593556761741638 0 4 | 4 0.92239260537065337 0.57518016513747017 2.3841862018575111e-07 5 | 5 0.35773405348396409 1.0556660886616451 1.1920922876756005e-07 6 | # 7 | 1 2 8 | 3 4 9 | 4 5 10 | # 11 | -------------------------------------------------------------------------------- /subdivgui/subdiv_weights.h: -------------------------------------------------------------------------------- 1 | #include 2 | enum WeightsType 3 | { 4 | WEIGHTS_TYPE_BBW = 0, 5 | WEIGHTS_TYPE_BONE_HEAT = 1, 6 | NUM_WEIGHTS_TYPES = 2 7 | }; 8 | bool subdiv_weights( 9 | const Eigen::MatrixXd & CV, 10 | const Eigen::MatrixXi & CQ, 11 | const Eigen::MatrixXd & C, 12 | const Eigen::MatrixXi & BE, 13 | const int computation_level, 14 | const int evaluation_level, 15 | const WeightsType type, 16 | Eigen::MatrixXd & W); 17 | 18 | -------------------------------------------------------------------------------- /lib/save_obj.h: -------------------------------------------------------------------------------- 1 | #ifndef __save_obj_h__ 2 | #define __save_obj_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace save_obj 9 | { 10 | typedef float real_t; 11 | void save_mesh( const std::string& outpath, const std::vector< std::vector< int > >& faces, const std::vector< std::vector< float > >& vertices, const std::string& header_message = "" ); 12 | void save_mesh( std::ostream& out, const std::vector< std::vector< int > >& faces, const std::vector< std::vector< float > >& vertices, const std::string& header_message = "" ); 13 | } 14 | 15 | #endif /* __save_obj_h__ */ 16 | -------------------------------------------------------------------------------- /subdivgui/Mesh.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | class Mesh 5 | { 6 | public: 7 | // Rest and deformed positions 8 | Eigen::Matrix V,U,N,C; 9 | Eigen::MatrixXd W; 10 | Eigen::Matrix UCT,NCT; 11 | // Quad and triangle face lists 12 | Eigen::Matrix Q; 13 | Eigen::Matrix F; 14 | Eigen::Matrix T,T1; 15 | // Display list, vertex/normal/index buffer 16 | GLuint dl=0,vbo=0,nbo=0,ibo=0; 17 | // Display list, vbo, and nbo are stale 18 | bool wireframe = false; 19 | bool stale = true; 20 | bool must_update = true; 21 | bool force_visible; 22 | bool show_weights = false; 23 | std::vector selected_weights; 24 | Mesh(bool _force_visible):force_visible(_force_visible){}; 25 | void draw_and_cache(); 26 | public: 27 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /lib/subdivision_matrix_types.h: -------------------------------------------------------------------------------- 1 | #ifndef __subdivision_matrix_types_h__ 2 | #define __subdivision_matrix_types_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include // pair 8 | 9 | namespace subdivision_matrix 10 | { 11 | 12 | typedef float real_t; 13 | // typedef double long_real_t; 14 | typedef int index_t; 15 | typedef std::vector< std::pair< index_t, real_t > > sparse_vector_t; 16 | 17 | // typedef Eigen::Matrix< real_t, Eigen::Dynamic, Eigen::Dynamic > Matrix_t; 18 | typedef Eigen::SparseMatrix< real_t, Eigen::RowMajor > SparseMatrix_t; 19 | 20 | typedef Eigen::Matrix< real_t, 1, 3> vertex_t; 21 | 22 | typedef Eigen::Matrix< real_t, Eigen::Dynamic, Eigen::Dynamic > MatrixXX_t; 23 | typedef Eigen::Matrix< real_t, Eigen::Dynamic, 4 > MatrixX4_t; 24 | typedef Eigen::Matrix< real_t, Eigen::Dynamic, 3 > MatrixX3_t; 25 | typedef Eigen::Matrix< real_t, Eigen::Dynamic, 1 > VectorX_t; 26 | 27 | typedef std::pair< std::vector< int >, std::vector< int > > faces_t; 28 | typedef Eigen::Matrix< real_t, 4, 4 > transform_t; 29 | 30 | } 31 | 32 | #endif /* __subdivision_matrix_types_h__ */ 33 | -------------------------------------------------------------------------------- /lib/save_obj.cpp: -------------------------------------------------------------------------------- 1 | #include "save_obj.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace save_obj 7 | { 8 | void save_mesh( const std::string& out_path, const std::vector< std::vector< int > >& faces, const std::vector< std::vector< real_t > >& vertices, const std::string& header_message ) 9 | { 10 | std::ofstream out( out_path ); 11 | save_mesh( out, faces, vertices, header_message ); 12 | std::cout << "Saved a mesh to: " << out_path << '\n'; 13 | } 14 | void save_mesh( std::ostream& out, const std::vector< std::vector< int > >& faces, const std::vector< std::vector< real_t > >& vertices, const std::string& header_message ) 15 | { 16 | // Save an optional header message. 17 | out << header_message; 18 | 19 | // Save vertices. 20 | for( const auto& vert: vertices ) 21 | { 22 | out << "v"; 23 | for( const auto& coord: vert ) 24 | { 25 | out << ' ' << coord; 26 | } 27 | out << '\n'; 28 | } 29 | 30 | out << '\n'; 31 | 32 | for( const auto& f: faces ) 33 | { 34 | out << 'f'; 35 | for( const auto& vi : f ) 36 | { 37 | // Vertices are 1-indexed. 38 | out << ' ' << (vi+1); 39 | } 40 | out << '\n'; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/subdivision_limit_mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef __subdivision_limit_mesh_h__ 2 | #define __subdivision_limit_mesh_h__ 3 | 4 | #include "subdivision_matrices.h" 5 | #include "subdivision_matrices_eigen.h" 6 | 7 | namespace subdivision_limit_mesh 8 | { 9 | 10 | typedef subdivision_matrix::real_t real_t; 11 | /* 12 | Given the vertices of the mesh 'vertices', 13 | an HbrMesh pointer 'mesh', 14 | and the desired number of "u" and "v" parameters sampling the range [0,1]x[0,1], 15 | returns a quad mesh for the limit surface sampled at least according 16 | to 'num_u' and 'num_v'; 17 | the resulting vertices are placed in 'vertices_out' and 18 | the resulting faces (vectors of CCW faces 0-indexed into vertices_out) in 'faces_out'. 19 | 20 | NOTE: If the same 'num_u' and 'num_v' are passed to createUVs() followed by compute_subdivision_coefficients*(), 21 | then the mesh returned by this function will include all of the same limit positions (and more). 22 | */ 23 | void compute_quad_limit_mesh_for_mesh( 24 | const subdivision_matrix::MatrixX3_t& vertices, 25 | OpenSubdiv::HbrMesh* mesh, 26 | int num_u, int num_v, 27 | std::vector< std::vector< real_t > >& vertices_out, 28 | std::vector< std::vector< int > >& faces_out 29 | ); 30 | } 31 | 32 | #ifdef SUBDIVISION_LIMIT_MESH_HEADER_ONLY 33 | #include "subdivision_limit_mesh.cpp" 34 | #endif 35 | 36 | #endif /* __subdivision_limit_mesh_h__ */ 37 | -------------------------------------------------------------------------------- /lib/subdivision_matrices_osd_eigen.h: -------------------------------------------------------------------------------- 1 | #ifndef __subdivision_matrices_osd_eigen_h__ 2 | #define __subdivision_matrices_osd_eigen_h__ 3 | 4 | #include "subdivision_matrices_osd.h" 5 | #include 6 | #include 7 | 8 | namespace subdivision_matrix 9 | { 10 | 11 | /* 12 | Given the number of vertices in the mesh 'num_vertices' 13 | and a vector of faces 'faces', each element of which is a vector of vertex indices, and 14 | a positive integer 'level' indicating the level of refinement, 15 | fills 'positions_out' with a matrix such that 16 | the matrix multiplication of 'positions_out' times a num_vertices-by-K matrix of control points 17 | yields the positions. 18 | 19 | The optional parameter 'faces_out', if specified, is a sequence of quad faces 20 | obtained by subdivision, where each face is four indices into 'positions_out': 21 | face0_vertex0 face0_vertex1 face0_vertex2 face0_vertex3 face1_vertex0 face1_vertex1 face1_vertex2 face1_vertex3 ... 22 | */ 23 | void compute_subdivision_coefficients_for_mesh( 24 | int num_vertices, 25 | const faces_t& faces, 26 | const int level, 27 | SparseMatrix_t& positions_out, 28 | std::vector< index_t >* quad_faces_out = nullptr 29 | ); 30 | void compute_subdivision_coefficients_for_mesh( 31 | int num_vertices, 32 | OpenSubdiv::HbrMesh* mesh, 33 | const int level, 34 | SparseMatrix_t& positions_out, 35 | std::vector< index_t >* quad_faces_out = nullptr 36 | ); 37 | 38 | } 39 | 40 | #ifdef SUBDIVISION_MATRICES_HEADER_ONLY 41 | #include "subdivision_matrices_osd_eigen.cpp" 42 | #endif 43 | 44 | #endif /* __subdivision_matrices_osd_eigen_h__ */ 45 | -------------------------------------------------------------------------------- /subdivgui/torus.obj: -------------------------------------------------------------------------------- 1 | # OBJ: 2 | v 1.25052 0.517982 0.353553 3 | v 0.597239 0.247384 0.353553 4 | v 0.597239 0.247384 -0.353553 5 | v 1.25052 0.517982 -0.353553 6 | v 0.517982 1.25052 0.353553 7 | v 0.247384 0.597239 0.353553 8 | v 0.247384 0.597239 -0.353553 9 | v 0.517982 1.25052 -0.353553 10 | v -0.517982 1.25052 0.353553 11 | v -0.247384 0.597239 0.353553 12 | v -0.247384 0.597239 -0.353553 13 | v -0.517982 1.25052 -0.353553 14 | v -1.25052 0.517982 0.353553 15 | v -0.597239 0.247384 0.353553 16 | v -0.597239 0.247384 -0.353553 17 | v -1.25052 0.517982 -0.353553 18 | v -1.25052 -0.517982 0.353553 19 | v -0.597239 -0.247384 0.353553 20 | v -0.597239 -0.247384 -0.353553 21 | v -1.25052 -0.517982 -0.353553 22 | v -0.517982 -1.25052 0.353553 23 | v -0.247384 -0.597239 0.353553 24 | v -0.247384 -0.597239 -0.353553 25 | v -0.517982 -1.25052 -0.353553 26 | v 0.517982 -1.25052 0.353553 27 | v 0.247384 -0.597239 0.353553 28 | v 0.247384 -0.597239 -0.353553 29 | v 0.517982 -1.25052 -0.353553 30 | v 1.25052 -0.517982 0.353553 31 | v 0.597239 -0.247384 0.353553 32 | v 0.597239 -0.247384 -0.353553 33 | v 1.25052 -0.517982 -0.353553 34 | 35 | f 5 6 2 1 36 | f 6 7 3 2 37 | f 7 8 4 3 38 | f 8 5 1 4 39 | f 9 10 6 5 40 | f 10 11 7 6 41 | f 11 12 8 7 42 | f 12 9 5 8 43 | f 13 14 10 9 44 | f 14 15 11 10 45 | f 15 16 12 11 46 | f 16 13 9 12 47 | f 17 18 14 13 48 | f 18 19 15 14 49 | f 19 20 16 15 50 | f 20 17 13 16 51 | f 21 22 18 17 52 | f 22 23 19 18 53 | f 23 24 20 19 54 | f 24 21 17 20 55 | f 25 26 22 21 56 | f 26 27 23 22 57 | f 27 28 24 23 58 | f 28 25 21 24 59 | f 29 30 26 25 60 | f 30 31 27 26 61 | f 31 32 28 27 62 | f 32 29 25 28 63 | f 1 2 30 29 64 | f 2 3 31 30 65 | f 3 4 32 31 66 | f 4 1 29 32 67 | -------------------------------------------------------------------------------- /lib/Timing.cpp: -------------------------------------------------------------------------------- 1 | #include "Timing.hpp" 2 | 3 | #include 4 | #include // gettimeofday(), getrusage() 5 | #include // getrusage() 6 | 7 | #define TICTOC 1 8 | 9 | static double timeofday() 10 | { 11 | timeval tp ; 12 | gettimeofday( &tp, NULL ) ; 13 | double result = (double)tp.tv_sec + 1e-6 * (double)tp.tv_usec ; 14 | return result ; 15 | } 16 | static double cpuusage() 17 | { 18 | struct rusage rus ; 19 | getrusage( RUSAGE_SELF, &rus ) ; 20 | double user = (double)rus.ru_utime.tv_sec + 1e-6 * (double)rus.ru_utime.tv_usec ; 21 | double sys = (double)rus.ru_stime.tv_sec + 1e-6 * (double)rus.ru_stime.tv_usec ; 22 | 23 | return user + sys ; 24 | } 25 | 26 | static const int TICTOC_STACK_SIZE = 25 ; 27 | static double sTicStart[ TICTOC_STACK_SIZE ] = { 0.0 } ; 28 | static int sTicStartIndex = -1 ; 29 | void tic( const char* txt ) 30 | { 31 | #if TICTOC 32 | sTicStartIndex += 1 ; 33 | if( sTicStartIndex >= TICTOC_STACK_SIZE ) 34 | { 35 | sTicStartIndex = TICTOC_STACK_SIZE - 1 ; 36 | std::cerr << "tic()/toc() max depth reached. Re-using top of stack.\n"; 37 | } 38 | 39 | if( txt ) std::cout << '[' << txt << "] begins\n"; 40 | 41 | // sTicStart[ sTicStartIndex ] = timeofday() ; 42 | sTicStart[ sTicStartIndex ] = cpuusage() ; 43 | #endif 44 | } 45 | void toc( const char* txt ) 46 | { 47 | #if TICTOC 48 | if( sTicStartIndex >= 0 ) 49 | { 50 | // double curtime = timeofday() ; 51 | double curtime = cpuusage() ; 52 | if( txt ) std::cout << '[' << txt << "] "; 53 | std::cout << "elapsed cpu time: " << (curtime - sTicStart[ sTicStartIndex ]) << std::endl ; 54 | sTicStartIndex -= 1 ; 55 | } 56 | else 57 | { 58 | std::cerr << "Called toc() with no matching tic().\n"; 59 | } 60 | #endif 61 | } 62 | -------------------------------------------------------------------------------- /lib/subdivision_matrices_eigen.h: -------------------------------------------------------------------------------- 1 | #ifndef __subdivision_matrices_eigen_h__ 2 | #define __subdivision_matrices_eigen_h__ 3 | 4 | #include "subdivision_matrices.h" 5 | #include 6 | #include 7 | 8 | namespace subdivision_matrix 9 | { 10 | 11 | /* 12 | Given the number of vertices in the mesh 'num_vertices' 13 | and a vector of faces 'faces', each element of which is a vector of vertex indices, and 14 | two same-length vectors 'us' and 'vs' where the ( us[i], vs[i] ) are the uv locations 15 | with which to sample every face, 16 | fills 'positions_out' with a matrix such that 17 | the matrix multiplication of 'positions_out' times a num_vertices-by-K matrix of control points 18 | yields the positions. 19 | 20 | The optional parameters 'du_out' and 'dv_out', if specified, are similar to 'positions_out' 21 | except that the matrix multiplication results in du and dv vectors. 22 | */ 23 | void compute_subdivision_coefficients_for_mesh( 24 | int num_vertices, 25 | const faces_t& faces, 26 | const std::vector< real_t >& us, 27 | const std::vector< real_t >& vs, 28 | SparseMatrix_t& positions_out, 29 | SparseMatrix_t* du_out = nullptr, 30 | SparseMatrix_t* dv_out = nullptr 31 | ); 32 | void compute_subdivision_coefficients_for_mesh( 33 | int num_vertices, 34 | OpenSubdiv::HbrMesh* mesh, 35 | const std::vector< real_t >& us, 36 | const std::vector< real_t >& vs, 37 | SparseMatrix_t& positions_out, 38 | SparseMatrix_t* du_out = nullptr, 39 | SparseMatrix_t* dv_out = nullptr 40 | ); 41 | 42 | } 43 | 44 | #ifdef SUBDIVISION_MATRICES_HEADER_ONLY 45 | #include "subdivision_matrices_eigen.cpp" 46 | #endif 47 | 48 | #endif /* __subdivision_matrices_eigen_h__ */ 49 | -------------------------------------------------------------------------------- /lib/subdivision_matrices_osd.h: -------------------------------------------------------------------------------- 1 | #ifndef __subdivision_matrices_osd_h__ 2 | #define __subdivision_matrices_osd_h__ 3 | 4 | #include "subdivision_matrix_types.h" 5 | #include 6 | 7 | // -I/path/to/OpenSubDiv 8 | #include 9 | #include 10 | 11 | namespace subdivision_matrix 12 | { 13 | 14 | /* 15 | Given the number of vertices in the mesh 'num_vertices' 16 | and a vector of faces 'faces', each element of which is a vector of vertex indices, and 17 | a positive integer 'level' indicating the level of refinement, 18 | fills 'positions_out' with sparse vectors such that the position 19 | for the i-th uv value in the original call to precomputeStencils() can be obtained by: 20 | \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second 21 | 22 | The optional parameter 'faces_out', if specified, is a sequence of quad faces 23 | obtained by subdivision, where each face is four indices into 'positions_out': 24 | face0_vertex0 face0_vertex1 face0_vertex2 face0_vertex3 face1_vertex0 face1_vertex1 face1_vertex2 face1_vertex3 ... 25 | */ 26 | void compute_subdivision_coefficients_for_mesh( 27 | int num_vertices, 28 | const faces_t& faces, 29 | const int level, 30 | std::vector< sparse_vector_t >& positions_out, 31 | std::vector< index_t >* quad_faces_out = nullptr 32 | ); 33 | // The same as above, except with an HbrMesh pointer 34 | void compute_subdivision_coefficients_for_mesh( 35 | int num_vertices, 36 | OpenSubdiv::HbrMesh* mesh, 37 | const int level, 38 | std::vector< sparse_vector_t >& positions_out, 39 | std::vector< index_t >* quad_faces_out = nullptr 40 | ); 41 | 42 | } 43 | 44 | #ifdef SUBDIVISION_MATRICES_HEADER_ONLY 45 | #include "subdivision_matrices_osd.cpp" 46 | #endif 47 | 48 | #endif /* __subdivision_matrices_osd_h__ */ 49 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.6) 2 | 3 | ## We need C++11 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | ## We need Eigen 7 | find_package(Eigen3 REQUIRED) 8 | include_directories( "${EIGEN3_INCLUDE_DIR}" ) 9 | 10 | ## We need OpenSubdiv 11 | set(OpenSubdiv_DIR "${CMAKE_SOURCE_DIR}/OpenSubdiv") 12 | 13 | find_library(OPENSUBDIV_LIBRARY libosdCPU.a "${CMAKE_SOURCE_DIR}/OpenSubdiv/build/lib") 14 | include_directories("${OpenSubdiv_DIR}/opensubdiv") 15 | 16 | ## To prevent duplicate compilation for the static and dynamic libraries 17 | # http://www.cmake.org/Wiki/CMake/Tutorials/Object_Library 18 | add_library(subdivision_skinning_common_objects 19 | OBJECT 20 | subdivision_matrices.cpp 21 | subdivision_matrices_eigen.cpp 22 | subdivision_matrices_osd.cpp 23 | subdivision_matrices_osd_eigen.cpp 24 | subdivision_skinning.cpp 25 | subdivision_engine.cpp 26 | subdivision_limit_mesh.cpp 27 | save_obj.cpp 28 | Timing.cpp 29 | ) 30 | 31 | ## To prevent duplicate compilation for the static and dynamic libraries 32 | # http://www.cmake.org/Wiki/CMake/Tutorials/Object_Library 33 | add_library(subdivision_skinning_library_sources 34 | OBJECT 35 | subdivision_skinning_wrapper.cpp 36 | ) 37 | add_library(subdivision_skinning_library_static STATIC $ $) 38 | add_library(subdivision_skinning_library_shared SHARED $ $) 39 | 40 | set_target_properties(subdivision_skinning_library_static PROPERTIES OUTPUT_NAME subdivision_skinning CLEAN_DIRECT_OUTPUT 1) 41 | set_target_properties(subdivision_skinning_library_shared PROPERTIES OUTPUT_NAME subdivision_skinning CLEAN_DIRECT_OUTPUT 1) 42 | target_link_libraries( 43 | subdivision_skinning_library_shared 44 | "${OPENSUBDIV_LIBRARY}" 45 | ) 46 | -------------------------------------------------------------------------------- /lib/subdivision_matrices.h: -------------------------------------------------------------------------------- 1 | #ifndef __subdivision_matrices_h__ 2 | #define __subdivision_matrices_h__ 3 | 4 | #include "subdivision_matrix_types.h" 5 | #include 6 | 7 | // -I/path/to/OpenSubDiv 8 | #include 9 | #include 10 | 11 | namespace subdivision_matrix 12 | { 13 | 14 | /* 15 | Given the number of vertices in the mesh 'num_vertices' 16 | and a vector of faces 'faces', each element of which is a vector of vertex indices, and 17 | two same-length vectors 'us' and 'vs' where the ( us[i], vs[i] ) are the uv locations, 18 | fills 'positions_out' with sparse vectors such that the position 19 | for the i-th uv value in the original call to precomputeStencils() can be obtained by: 20 | \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second 21 | 22 | The optional parameters 'du_out' and 'dv_out', if specified, are similar to 'positions_out' 23 | except that the above summation results in du and dv. 24 | */ 25 | void compute_subdivision_coefficients_for_mesh( 26 | int num_vertices, 27 | const faces_t& faces, 28 | const std::vector< real_t >& us, 29 | const std::vector< real_t >& vs, 30 | std::vector< sparse_vector_t >& positions_out, 31 | std::vector< sparse_vector_t >* du_out = nullptr, 32 | std::vector< sparse_vector_t >* dv_out = nullptr 33 | ); 34 | // The same as above, except with an HbrMesh pointer 35 | void compute_subdivision_coefficients_for_mesh( 36 | int num_vertices, 37 | OpenSubdiv::HbrMesh* mesh, 38 | const std::vector< real_t >& us, 39 | const std::vector< real_t >& vs, 40 | std::vector< sparse_vector_t >& positions_out, 41 | std::vector< sparse_vector_t >* du_out = nullptr, 42 | std::vector< sparse_vector_t >* dv_out = nullptr 43 | ); 44 | 45 | /* 46 | Given a desired number of "u" and "v" parameters sampling the range [0,1]x[0,1], 47 | returns in the output vectors 'us' and 'vs' coordinates sampling the range 48 | in equal intervals. 49 | */ 50 | void createUVs( int num_u, int num_v, std::vector< real_t >& us, std::vector< real_t >& vs ); 51 | 52 | } 53 | 54 | #ifdef SUBDIVISION_MATRICES_HEADER_ONLY 55 | #include "subdivision_matrices.cpp" 56 | #endif 57 | 58 | #endif /* __subdivision_matrices_h__ */ 59 | -------------------------------------------------------------------------------- /lib/subdivision_engine.h: -------------------------------------------------------------------------------- 1 | #ifndef __subdivision_engine_h__ 2 | #define __subdivision_engine_h__ 3 | 4 | #include "subdivision_matrices_eigen.h" 5 | #include "subdivision_matrices_osd_eigen.h" 6 | #include 7 | #include 8 | #include "Timing.hpp" 9 | 10 | namespace subdivision_matrix 11 | { 12 | // Declare the mesh structure. 13 | struct subdivision_control_mesh 14 | { 15 | // This should be an N-by-3 dense matrix. 16 | MatrixX3_t vs; 17 | // This is a vector of faces. 18 | faces_t faces; 19 | }; 20 | 21 | typedef Eigen::DiagonalMatrix< real_t, Eigen::Dynamic > DiagonalMatrix_t; 22 | 23 | void prepare( const subdivision_control_mesh& mesh, const MatrixX3_t& handle_positions, const char * weight_function, 24 | std::vector< MatrixX4_t > & W_Array ); 25 | 26 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const SparseMatrix_t& Du_matrices, const SparseMatrix_t& Dv_matrices, 27 | const MatrixX3_t& handle_positions, const char* weight_function, 28 | std::vector< MatrixX4_t > & W_Array ); 29 | 30 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, 31 | const MatrixX3_t& handle_positions, const char* weight_function, 32 | std::vector< MatrixX4_t > & W_Array ); 33 | 34 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const DiagonalMatrix_t& areas, 35 | const MatrixXX_t& weights, std::vector< MatrixX4_t > & W_Array ); 36 | 37 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, 38 | const MatrixXX_t& weights, std::vector< MatrixX4_t > & W_Array ); 39 | 40 | void solve( const std::vector< MatrixX4_t >& W_Array, const std::vector< transform_t >& transforms, MatrixX3_t & result ); 41 | 42 | // naive approach 43 | void naive_prepare( const MatrixX3_t& vs, const MatrixX3_t& handle_positions, 44 | std::vector< MatrixX4_t > & W_Array ); 45 | 46 | void compute_target_surface( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, 47 | const MatrixX3_t& handle_positions, const std::vector< transform_t >& transforms, 48 | MatrixX3_t& result ); 49 | 50 | VectorX_t piecewise_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ); 51 | 52 | real_t norm_of_piecewise_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ); 53 | 54 | real_t hausdorff_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ); 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /subdivgui/robust_bbw.cpp: -------------------------------------------------------------------------------- 1 | #include "robust_bbw.h" 2 | #include "clean.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | bool robust_bbw( 13 | const Eigen::MatrixXd & V, 14 | const Eigen::MatrixXi & F, 15 | const Eigen::MatrixXd & C, 16 | const Eigen::MatrixXi & BE, 17 | Eigen::MatrixXd & W) 18 | { 19 | using namespace igl; 20 | using namespace Eigen; 21 | using namespace std; 22 | // clean mesh 23 | MatrixXd CV; 24 | MatrixXi CF; 25 | VectorXi IM; 26 | if(!clean(V,F,CV,CF,IM)) 27 | { 28 | return false; 29 | } 30 | MatrixXd TV; 31 | MatrixXi TT; 32 | // compute tet-mesh 33 | { 34 | MatrixXi _1; 35 | #ifdef VERBOSE 36 | cerr<<"mesh_with_skeleton"<thresh).cast().sum(); 55 | TT.resize(count,oldTT.cols()); 56 | int c = 0; 57 | for(int t = 0;tthresh) 60 | { 61 | TT.row(c++) = oldTT.row(t); 62 | } 63 | } 64 | } 65 | 66 | // compute weights 67 | VectorXi b; 68 | MatrixXd bc; 69 | if(!boundary_conditions(TV,TT,C,{},BE,{},b,bc)) 70 | { 71 | cout< 14 | # Copyright (c) 2008, 2009 Gael Guennebaud, 15 | # Copyright (c) 2009 Benoit Jacob 16 | # Redistribution and use is allowed according to the terms of the 2-clause BSD license. 17 | 18 | if(NOT Eigen3_FIND_VERSION) 19 | if(NOT Eigen3_FIND_VERSION_MAJOR) 20 | set(Eigen3_FIND_VERSION_MAJOR 2) 21 | endif(NOT Eigen3_FIND_VERSION_MAJOR) 22 | if(NOT Eigen3_FIND_VERSION_MINOR) 23 | set(Eigen3_FIND_VERSION_MINOR 91) 24 | endif(NOT Eigen3_FIND_VERSION_MINOR) 25 | if(NOT Eigen3_FIND_VERSION_PATCH) 26 | set(Eigen3_FIND_VERSION_PATCH 0) 27 | endif(NOT Eigen3_FIND_VERSION_PATCH) 28 | 29 | set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") 30 | endif(NOT Eigen3_FIND_VERSION) 31 | 32 | macro(_eigen3_check_version) 33 | file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) 34 | 35 | string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") 36 | set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") 37 | string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") 38 | set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") 39 | string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") 40 | set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") 41 | 42 | set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) 43 | if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 44 | set(EIGEN3_VERSION_OK FALSE) 45 | else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 46 | set(EIGEN3_VERSION_OK TRUE) 47 | endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 48 | 49 | if(NOT EIGEN3_VERSION_OK) 50 | 51 | message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " 52 | "but at least version ${Eigen3_FIND_VERSION} is required") 53 | endif(NOT EIGEN3_VERSION_OK) 54 | endmacro(_eigen3_check_version) 55 | 56 | if (EIGEN3_INCLUDE_DIR) 57 | 58 | # in cache already 59 | _eigen3_check_version() 60 | set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) 61 | 62 | else (EIGEN3_INCLUDE_DIR) 63 | 64 | find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library 65 | PATHS 66 | ${CMAKE_INSTALL_PREFIX}/include 67 | ${KDE4_INCLUDE_DIR} 68 | PATH_SUFFIXES eigen3 eigen 69 | ) 70 | 71 | if(EIGEN3_INCLUDE_DIR) 72 | _eigen3_check_version() 73 | endif(EIGEN3_INCLUDE_DIR) 74 | 75 | include(FindPackageHandleStandardArgs) 76 | find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) 77 | 78 | mark_as_advanced(EIGEN3_INCLUDE_DIR) 79 | 80 | endif(EIGEN3_INCLUDE_DIR) 81 | 82 | -------------------------------------------------------------------------------- /lib/ColorMap.h: -------------------------------------------------------------------------------- 1 | #ifndef __ColorMap_h__ 2 | #define __ColorMap_h__ 3 | 4 | template < typename value_t > struct ColorMap 5 | { 6 | typedef float color_component_t; 7 | struct Color 8 | { 9 | color_component_t r; 10 | color_component_t g; 11 | color_component_t b; 12 | 13 | Color() : r(0), g(0), b(0) {} 14 | Color( color_component_t r0, color_component_t g0, color_component_t b0 ) : r(r0), g(g0), b(b0) {} 15 | 16 | static Color lerp( const Color& a, const Color& b, value_t t ) 17 | { 18 | return Color( 19 | a.r + t*(b.r-a.r), 20 | a.g + t*(b.g-a.g), 21 | a.b + t*(b.b-a.b) 22 | ); 23 | } 24 | }; 25 | 26 | // The minimum value. 27 | value_t min_value; 28 | // The maximum value. 29 | value_t max_value; 30 | // The 0-th entry corresponds to values <= min_value. 31 | // The last entry corresponds to values >= max_value. 32 | std::vector< Color > colormap; 33 | 34 | ColorMap( value_t min, value_t max ) 35 | : min_value( min ), max_value( max ) 36 | { 37 | SetWhiteToRed(); 38 | } 39 | 40 | ColorMap() 41 | : min_value( 0 ), max_value( 1 ) 42 | { 43 | SetWhiteToRed(); 44 | } 45 | 46 | void SetWhiteToRed() 47 | { 48 | colormap.resize(2); 49 | colormap[0] = Color( 1,1,1 ); 50 | colormap[1] = Color( 1,0,0 ); 51 | } 52 | 53 | // Returns the computed color. 54 | Color Value2Color( const value_t val ) 55 | { 56 | assert( colormap.size() >= 2 ); 57 | assert( max_value >= min_value ); 58 | 59 | /// 1 Find 'val' as a fraction from min_value to max_value. 60 | /// 2 Find the percent's index into colormap. 61 | /// 3 Find the percent between the two surrounding colors. 62 | /// 4 Lerp the two surrounding colors in the color map. 63 | 64 | static const double eps = 1e-7; 65 | 66 | if( fabs( max_value - min_value ) < eps ) 67 | { 68 | return Color::lerp( colormap.at(0), colormap.at(1), .5 ); 69 | } 70 | 71 | /// 1 72 | const value_t fraction = std::max( value_t(0), std::min( value_t(1), ( val - min_value )/(max_value - min_value) ) ); 73 | /// 2 74 | const value_t real_index = fraction*( colormap.size()-1 ); 75 | const int lower_index = std::max( 0, std::min( int(colormap.size())-2, int( real_index ) ) ); 76 | /// 3 77 | value_t remaining_fraction = real_index - lower_index; 78 | assert( remaining_fraction > -eps ); 79 | assert( remaining_fraction < 1+eps ); 80 | remaining_fraction = std::max( value_t(0), std::min( value_t(1), remaining_fraction ) ); 81 | /// 4 82 | return Color::lerp( colormap.at( lower_index ), colormap.at( lower_index+1 ), remaining_fraction ); 83 | } 84 | // Directly puts the output into a pointer to three color_component_t's. 85 | void Value2Color( const value_t val, color_component_t* rgb_out ) 86 | { 87 | const Color result = Value2Color( val ); 88 | rgb_out[0] = result.r; 89 | rgb_out[1] = result.g; 90 | rgb_out[2] = result.b; 91 | } 92 | }; 93 | 94 | #endif /* __ColorMap_h__ */ 95 | -------------------------------------------------------------------------------- /lib/subdivision_skinning.h: -------------------------------------------------------------------------------- 1 | #ifndef __subdivision_skinning_h__ 2 | #define __subdivision_skinning_h__ 3 | 4 | #include 5 | #include 6 | 7 | // -I/path/to/OpenSubDiv 8 | #include 9 | #include 10 | 11 | #include "subdivision_engine.h" 12 | 13 | namespace subdivision_skinning 14 | { 15 | typedef float real_t; 16 | // A sequence of vertex values: x0 y0 z0 x1 y1 z1 ... 17 | typedef std::vector< real_t > vertices_t; 18 | // Each face in a faces_t is a sequence of vertex indices. 19 | using subdivision_matrix::faces_t; 20 | // An H-by-3 matrix with an x,y,z position for each handle. 21 | typedef Eigen::MatrixXf handles_t; 22 | // A transform is a 4x4 matrix. 23 | typedef Eigen::Matrix4f transform_t; 24 | 25 | // Forward declare the engine structure. 26 | struct engine_t; 27 | 28 | // Precomputes and returns a new engine_t for the given subdivision surface. 29 | // The resulting pointer can be passed to update_control_vertices_given_handle_transforms(). 30 | // To destroy the engine_t and free the memory associated with it, call delete_engine_t(). 31 | // The optional HbrMesh* provides tagging information. 32 | engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const handles_t& handle_positions, const char* weight_function, OpenSubdiv::HbrMesh* mesh = 0 ); 33 | engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const handles_t& handle_positions, const char* weight_function, OpenSubdiv::HbrMesh* mesh, std::vector< subdivision_matrix::index_t >* quad_faces_out = 0 ); 34 | engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const subdivision_matrix::SparseMatrix_t& M_matrices, const subdivision_matrix::MatrixXX_t& weights ); 35 | // Given an existing 'engine_t', modifies it to use the new 'handle_positions' and 'weight_function'. 36 | // Subsequent calls to update_control_vertices_given_handle_transforms() must pass 37 | // 'handle_positions.rows()' transforms. 38 | void set_new_handle_positions( engine_t* engine, const handles_t& handle_positions, const char* weight_function ); 39 | // Given a set of transform_t's, one for each handle_position, 40 | // returns in 'vertices_out' updated positions for the control points of the subdivision surface. 41 | void update_control_vertices_given_handle_transforms( const engine_t* engine, const std::vector< transform_t >& transforms, vertices_t& vertices_out ); 42 | // update with test and all kinds of comparisons. 43 | void update_control_vertices_given_handle_transforms( const engine_t* engine, const std::vector< transform_t >& transforms, vertices_t& vertices_out, const char* approach, vertices_t& g_targetPositions, vertices_t& g_targetColors ); 44 | // Frees the memory associated with an engine_t. 45 | void delete_engine_t( engine_t* engine ); 46 | // naive approach wrapper 47 | 48 | // Upon return, replaces 'limit_vertices_out' with limit vertex positions 49 | // for all of our sample locations, based on the initial control mesh. 50 | // This is useful for an experiment. 51 | void limit_vertices_for_initial_sample_locations( const engine_t* engine, vertices_t& limit_vertices_out ); 52 | } 53 | 54 | #endif /* __subdivision_skinning_h__ */ 55 | -------------------------------------------------------------------------------- /subdivgui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.6) 2 | 3 | ## CGAL 4 | find_package(CGAL REQUIRED) 5 | ## This conflicts with many of my directives if it isn't first. 6 | include(${CGAL_USE_FILE}) 7 | # CGAL's monkeying with all of the flags. Rather than change the CGAL_USE_FILE 8 | # just get ride of this flag. 9 | # http://stackoverflow.com/a/18234926/148668 10 | macro(remove_cxx_flag flag) 11 | string(REPLACE "${flag}" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 12 | endmacro() 13 | remove_cxx_flag("-stdlib=libc++") 14 | 15 | # This is absolutely necessary for Exact Construction 16 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -frounding-math -fsignaling-nans") 17 | #set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${CGAL_LIBRARIES} ${CGAL_3RD_PARTY_LIBRARIES}) 18 | 19 | ## We need Eigen 20 | find_package(Eigen3 REQUIRED) 21 | include_directories( "${EIGEN3_INCLUDE_DIR}" ) 22 | 23 | ## We need libigl 24 | 25 | if(DEFINED ENV{LIBIGL}) 26 | set(LIBIGL $ENV{LIBIGL}) 27 | else(DEFINED ENV{LIBIGL}) 28 | set(LIBIGL "${CMAKE_SOURCE_DIR}/libigl") 29 | endif(DEFINED ENV{LIBIGL}) 30 | include_directories("${LIBIGL}/include") 31 | 32 | ## We don't have/want MOSEK 33 | add_definitions(-DIGL_NO_MOSEK) 34 | 35 | ## AntTweakBar 36 | include_directories("${LIBIGL}/external/AntTweakBar/include") 37 | find_library(ANTTWEAKBAR_LIB AntTweakBar "${LIBIGL}/external/AntTweakBar/lib") 38 | set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${ANTTWEAKBAR_LIB}) 39 | 40 | ## Embree 41 | include_directories("${LIBIGL}/external/embree") 42 | include_directories("${LIBIGL}/external/embree/include") 43 | find_library(EMBREE_MAIN_LIB NAMES embree PATHS "${LIBIGL}/external/embree/build") 44 | find_library(EMBREE_SYS_LIB NAMES sys PATHS "${LIBIGL}/external/embree/build") 45 | set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${EMBREE_MAIN_LIB} ${EMBREE_SYS_LIB}) 46 | 47 | ## Tetgen 48 | include_directories("${LIBIGL}/external/tetgen") 49 | find_library(TETGEN_LIB tet "${LIBIGL}/external/tetgen") 50 | set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${TETGEN_LIB}) 51 | 52 | ## YImage 53 | include_directories("${LIBIGL}/external/yimg") 54 | find_library(YIMG_LIB yimg "${LIBIGL}/external/yimg") 55 | find_package(PNG REQUIRED) 56 | set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${YIMG_LIB} ${PNG_LIBRARIES}) 57 | 58 | ## OpenGL/GLU/GLUT 59 | find_package(OpenGL REQUIRED) 60 | find_package(GLUT REQUIRED) 61 | #include_directories("${OPENGL_INCLUDE_DIRS}") 62 | #include_directories("${GLUT_INCLUDE_DIRS}") 63 | ## I don't have Alec's patched GLUT with GLUT_ACTIVE_COMMAND 64 | add_definitions(-DGLUT_ACTIVE_COMMAND=8) 65 | set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES}) 66 | 67 | ## Our library 68 | include_directories("${CMAKE_SOURCE_DIR}/lib") 69 | ## I'm not good enough at CMake to use find_library() on a previous subdirectories output. 70 | # find_library(SUBDIVISION_SKINNING_LIB subdivision_skinning "${CMAKE_BINARY_DIR}/lib") 71 | set(SUBDIVISION_SKINNING_LIB "${CMAKE_BINARY_DIR}/lib/libsubdivision_skinning.a") 72 | ## OpenSubdiv library is defined by the CMakeList from our lib directory, which 73 | ## is processed first. 74 | set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${SUBDIVISION_SKINNING_LIB} ${OPENSUBDIV_LIBRARY}) 75 | 76 | ## We need C++11. Put this directive after CGAL's include. 77 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 78 | 79 | add_executable(subdivgui 80 | subdivgui.cpp 81 | clean.cpp 82 | robust_bbw.cpp 83 | subdiv_weights.cpp 84 | Mesh.cpp 85 | ) 86 | target_link_libraries(subdivgui 87 | ${SUBDIVGUI_LIBS} 88 | ) 89 | -------------------------------------------------------------------------------- /subdivgui/clean.cpp: -------------------------------------------------------------------------------- 1 | #include "clean.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | bool clean( 14 | const Eigen::MatrixXd & V, 15 | const Eigen::MatrixXi & F, 16 | Eigen::MatrixXd & CV, 17 | Eigen::MatrixXi & CF, 18 | Eigen::VectorXi & IM) 19 | { 20 | using namespace igl; 21 | using namespace Eigen; 22 | using namespace std; 23 | //writeOBJ("VF.obj",V,F); 24 | const auto & validate_IM = []( 25 | const Eigen::MatrixXd & V, 26 | const Eigen::MatrixXd & CV, 27 | const Eigen::VectorXi & IM) 28 | { 29 | for(int i = 0;i1e-6) 35 | { 36 | cout<thresh).cast().sum(); 94 | MatrixXi CT(count,TT.cols()); 95 | int c = 0; 96 | for(int t = 0;tthresh) 99 | { 100 | CT.row(c++) = TT.row(t); 101 | } 102 | } 103 | assert(c==count); 104 | boundary_facets(CT,CF); 105 | //writeMESH("CVCTCF.mesh",TV,CT,CF); 106 | cout<<"remove_unreferenced"< 4 | #include 5 | 6 | extern "C" 7 | { 8 | #include "subdivision_skinning_wrapper.h" 9 | } 10 | 11 | bool subdiv_weights( 12 | const Eigen::MatrixXd & CV, 13 | const Eigen::MatrixXi & CQ, 14 | const Eigen::MatrixXd & C, 15 | const Eigen::MatrixXi & BE, 16 | const int computation_level, 17 | const int evaluation_level, 18 | const WeightsType type, 19 | Eigen::MatrixXd & W) 20 | { 21 | using namespace std; 22 | using namespace Eigen; 23 | using namespace igl; 24 | 25 | typedef subdivision_evaluator_real_t Scalar; 26 | typedef Eigen::Matrix MatrixXS; 27 | 28 | void* computation_eval; 29 | void* evaluation_eval; 30 | 31 | // 1. Create evaluator from control mesh with level L1 appropriate for 32 | // computing weights. 33 | 34 | { 35 | const MatrixXS CVT = CV.cast().transpose(); 36 | const Scalar * vs = CVT.data(); 37 | const int num_vs = CV.rows(); 38 | const MatrixXi CQT = CQ.cast().transpose(); 39 | const int * faces = CQT.data(); 40 | const int num_faces = CQ.rows(); 41 | computation_eval = new_subdivision_evaluator( 42 | num_vs, (Scalar*)vs, num_faces, (int*)faces, computation_level); 43 | } 44 | 45 | // 2. Compute refined mesh R1 (V,Q) 46 | MatrixXd V; 47 | MatrixXi Q,F; 48 | int * refined_faces; 49 | int num_refined_faces; 50 | Scalar * refined_vs; 51 | int num_refined_vs; 52 | { 53 | num_refined_faces = 54 | num_refined_quad_faces_of_subdivision_evaluator( computation_eval ); 55 | refined_faces = new int[ num_refined_faces*4 ]; 56 | get_refined_quad_faces_of_subdivision_evaluator( 57 | computation_eval, refined_faces ); 58 | const auto & QT = Map(refined_faces,4,num_refined_faces); 59 | Q = QT.transpose(); 60 | polygon_mesh_to_triangle_mesh(Q,F); 61 | num_refined_vs = 62 | num_refined_vertices_of_subdivision_evaluator( computation_eval ); 63 | refined_vs = new Scalar[ num_refined_vs*3 ]; 64 | get_refined_vertices_of_subdivision_evaluator(computation_eval,refined_vs); 65 | const auto & VT = Map 66 | (refined_vs,3,num_refined_vs); 67 | V = VT.cast().transpose(); 68 | } 69 | 70 | // 3. Compute weights W1 on R1 (V,F) 71 | MatrixXd W1; 72 | switch(type) 73 | { 74 | default: 75 | assert(false && "Unknown weighting type."); 76 | case WEIGHTS_TYPE_BBW: 77 | if(!robust_bbw(V,F,C,BE,W1)) 78 | { 79 | return false; 80 | } 81 | break; 82 | case WEIGHTS_TYPE_BONE_HEAT: 83 | if(!bone_heat(V,F,C,{},BE,{},W1)) 84 | { 85 | return false; 86 | } 87 | break; 88 | } 89 | 90 | // Let L2 be the level appropriate for our skinning engine. 91 | if(computation_level == evaluation_level) 92 | { 93 | W = W1; 94 | return true; 95 | } 96 | // 4. Create evaluator from R1 with L2-L1. 97 | { 98 | assert(num_refined_vs == W1.rows()); 99 | evaluation_eval = new_subdivision_evaluator( 100 | num_refined_vs, (Scalar*)refined_vs, 101 | num_refined_faces, (int*)refined_faces, 102 | evaluation_level-computation_level); 103 | } 104 | 105 | // 5. Evaluate R1 using the weights as the control mesh positions to get weights W2 for L2. 106 | { 107 | const int num_weights = W1.cols(); 108 | const int num_refined_vs = 109 | num_refined_vertices_of_subdivision_evaluator( evaluation_eval ); 110 | Scalar* refined_W = new Scalar[ num_refined_vs*num_weights ]; 111 | const MatrixXS W1T = W1.cast().transpose(); 112 | const Scalar * coarse_W = W1T.data(); 113 | get_refined_vertices_of_subdivision_evaluator_with_control_vertices( 114 | evaluation_eval, 115 | num_weights, 116 | coarse_W, 117 | refined_W); 118 | const auto & WT = Map(refined_W,num_weights,num_refined_vs); 119 | W = WT.cast().transpose(); 120 | delete[] refined_W; 121 | } 122 | 123 | 124 | delete[] refined_faces; 125 | delete[] refined_vs; 126 | delete_subdivision_evaluator( computation_eval ); 127 | delete_subdivision_evaluator( evaluation_eval ); 128 | return true; 129 | } 130 | -------------------------------------------------------------------------------- /subdivgui/Mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "Mesh.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void Mesh::draw_and_cache() 9 | { 10 | using namespace Eigen; 11 | using namespace igl; 12 | if(wireframe || show_weights) 13 | { 14 | glPushAttrib(GL_POLYGON_BIT); 15 | glPushAttrib(GL_ENABLE_BIT); 16 | glPushAttrib(GL_LIGHTING_BIT); 17 | glPushAttrib(GL_LINE_BIT); 18 | if(wireframe) 19 | { 20 | glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 21 | } 22 | glEnable(GL_COLOR_MATERIAL); 23 | if(show_weights) 24 | { 25 | glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT); 26 | glColor3f(0.1,0.1,0.1); 27 | glColorMaterial(GL_FRONT_AND_BACK,GL_SPECULAR); 28 | glColor3f(0.4,0.4,0.4); 29 | }else 30 | { 31 | glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT); 32 | glColor3f(0.,0,0); 33 | glColorMaterial(GL_FRONT_AND_BACK,GL_SPECULAR); 34 | glColor3f(0.,0,0); 35 | } 36 | glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE); 37 | if(wireframe) 38 | { 39 | glColor3f(0,0,0); 40 | } 41 | if(!glIsList(dl) || stale) 42 | { 43 | assert(V.rows() == U.rows()); 44 | if(show_weights && W.cols()>0) 45 | { 46 | per_vertex_normals(U,F,N); 47 | C.resize(V.rows(),3); 48 | for(size_t v = 0;v TCT(T.rows(),3); 89 | for(int q=0;q10000) 118 | for(int f=0;f(); 123 | UCT.row((f+1)*3+c) = U.row(T(f+1,c)).cast(); 124 | } 125 | Matrix v1 = UCT.row(f*3+1)-UCT.row(f*3+0); 126 | Matrix v2 = UCT.row(f*3+2)-UCT.row(f*3+0); 127 | Matrix n = v1.cross(v2); 128 | for(int c = 0;c<3;c++) 129 | { 130 | for(int d = 0;d<3;d++) 131 | { 132 | const float nd = n(d); 133 | NCT(f*3+c,d) = nd; 134 | NCT((f+1)*3+c,d) = nd; 135 | } 136 | } 137 | } 138 | 139 | glEnableClientState(GL_VERTEX_ARRAY); 140 | glBindBuffer(GL_ARRAY_BUFFER,vbo); 141 | glBufferData(GL_ARRAY_BUFFER,sizeof(float)*UCT.size(),UCT.data(),GL_DYNAMIC_DRAW); 142 | glVertexPointer(3,GL_FLOAT,0,0); 143 | glEnableClientState(GL_NORMAL_ARRAY); 144 | glBindBuffer(GL_ARRAY_BUFFER,nbo); 145 | glBufferData(GL_ARRAY_BUFFER,sizeof(float)*NCT.size(),NCT.data(),GL_DYNAMIC_DRAW); 146 | glNormalPointer(GL_FLOAT,0,0); 147 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ibo); 148 | glDrawElements(GL_TRIANGLES,T.size(),GL_UNSIGNED_INT,0); 149 | glBindBuffer(GL_ARRAY_BUFFER,0); 150 | } 151 | 152 | stale = false; 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Skinning Catmull-Clark Subdivision Surfaces 2 | 3 | This code implements the subdivision surface portion of our SIGGRAPH Asia 2014 paper 4 | [Skinning Cubic Bézier Splines and Catmull-Clark Subdivision Surfaces](http://cs.gmu.edu/~ygingold/splineskin/) 5 | by [Songrun Liu](http://cs.gmu.edu/~sliu11/), [Alec Jacobson](http://www.cs.columbia.edu/~jacobson/), and [Yotam Gingold](http://cs.gmu.edu/~ygingold/). 6 | The implementation consists of a library and a GUI for posing subdivision surfaces with skeletons. 7 | 8 | ## Compiling 9 | 10 | This code depends on: 11 | 12 | - [OpenSubDiv](http://graphics.pixar.com/opensubdiv) 2.x 13 | - [libigl](https://github.com/libigl/libigl) 14 | - included sub-dependencies: 15 | - AntTweakBar 16 | - embree 17 | - tetgen 18 | - yimg 19 | - tetgen 20 | - not included sub-dependencies: 21 | - [CGAL](http://www.cgal.org) (e.g. `brew install cgal`) 22 | - [MOSEK](https://www.mosek.com) (optional) 23 | - [eigen](http://eigen.tuxfamily.org/) (e.g. `brew install eigen`) 24 | 25 | Academics can install MOSEK for free, but this is optional. 26 | The CMakeFiles define the flag `-DIGL_NO_MOSEK` to disable Mosek support when building this 27 | project. 28 | 29 | ### Download and compile the OpenSubdiv 2.x dependency 30 | git clone https://github.com/PixarAnimationStudios/OpenSubdiv.git 31 | ( 32 | cd OpenSubdiv 33 | && 34 | git reset --hard 25cee425f32758a7e0e8812da628007a8eeecce6 35 | && 36 | mkdir build 37 | && 38 | cd build 39 | && 40 | cmake -DCMAKE_BUILD_TYPE=Release .. 41 | && 42 | make 43 | ) 44 | 45 | To compile using gcc on Mac OS X, consider replacing the last two commands 46 | with: 47 | 48 | cmake -DCMAKE_BUILD_TYPE=Release -DNO_EXAMPLES=1 -DNO_GCD=1 -DNO_CUDA=1 .. 49 | && 50 | make osd_static_cpu 51 | 52 | If you installed opensubdiv somewhere other than _this_ directory, then 53 | create a symbolic link to it. For example, 54 | 55 | ln -s ~/Documents/OpenSubdiv OpenSubdiv 56 | 57 | ### Download libigl and compile the third-party dependencies 58 | git clone https://github.com/libigl/libigl.git 59 | ( cd libigl/external/AntTweakBar/src && make ) 60 | ( cd libigl/external/embree && mkdir build && cd build && cmake .. && make ) 61 | ( cd libigl/external/tetgen && make ) 62 | ( cd libigl/external/yimg && make ) 63 | 64 | 65 | ### Compile this project (libsubdivision_skinning and subdivgui) 66 | mkdir build 67 | cd build 68 | cmake -DCMAKE_BUILD_TYPE=Release .. 69 | make 70 | cd .. 71 | 72 | 73 | ## Using the library 74 | 75 | The library is compiled into 76 | 77 | build/lib/libsubdivision_skinning.{a,dylib} 78 | 79 | The library interface is exposed through a simple pure-C interface. The header file describing the functions is in 80 | 81 | lib/subdivision_skinning_wrapper.h 82 | 83 | A simple usage example can be found in 84 | 85 | lib/subdivision_skinning_wrapper_test.cpp 86 | 87 | 88 | ## Running the included GUI 89 | 90 | Prepare a coarse subdivision surface cage as a .obj file `cage.obj` and a skeleton using 91 | the [.tgf file 92 | format](http://igl.ethz.ch/projects/libigl/file-formats/tgf.html) of libigl 93 | `skeleton.tgf`. 94 | You could use the `libigl/examples/skeleton-builder/` example to build your 95 | skeleton with a GUI. 96 | 97 | Run this program for the first time to compute bounded biharmonic weights: 98 | 99 | ./build/subdivgui/subdivgui cage.obj skeleton.tgf [computation_level evaluation_level [weights.dmat]] 100 | 101 | The computation and evaluation level parameters specify the (integer) level of subdivision to 102 | use when computing skinning weights and when evaluating the energy described in the paper. 103 | Skinning weight computation can be lengthy, so computation level is allowed to be a 104 | smaller number than evaluation level. The defaults are 1 and 3, respectively. 105 | For very coarse subdivision control meshes, 2 and 4 are more sensible. 106 | 107 | You can run the included torus example: 108 | 109 | ./build/subdivgui/subdivgui subdivgui/torus{.obj,.tgf} 2 4 subdivgui/torus-weights.dmat 110 | 111 | This will attempt to _clean_ the model by meshing self-intersections, and 112 | filling holes. Then it will compute a tetrahedral mesh of the surface's solid 113 | volume. Finally it will compute bounded biharmonic weights for each skeleton 114 | bone. When finished the weights will be saved to the provided path 115 | `weights.dmat` in the [.dmat file 116 | format](http://igl.ethz.ch/projects/libigl/file-formats/dmat.html) of libigl. 117 | 118 | The GUI will now start and you may follow the standard output instructions to 119 | interact and change visualization settings. 120 | 121 | Subsequent runs of 122 | 123 | ./build/subdivgui/subdivgui cage.obj skeleton.tgf computation_level evaluation_level weights.dmat 124 | 125 | will load weights from `weights.dmat` rather than recompute them. 126 | (Computation and evaluation level must be the same between runs.) 127 | -------------------------------------------------------------------------------- /subdivgui/refine.cpp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | /*/../bin/ls > /dev/null 3 | # BEGIN BASH SCRIPT 4 | export PS4="" 5 | set -o xtrace 6 | TEMP="$0.cpp" 7 | #clang++ -O3 -std=c++11 -o .main $TEMP \ 8 | #g++ -O3 -std=c++11 -o .main $TEMP \ 9 | # -fopenmp \ 10 | printf "//" | cat - $0 >$TEMP 11 | clang++ -g -O3 -DNDEBUG -std=c++11 -o .main $TEMP \ 12 | -I. libosdCPU.a libsubdivision_skinning.a \ 13 | -I/opt/local/include/eigen3/ -I/usr/local/igl/libigl/include \ 14 | -I/usr/local/igl/libigl/external/AntTweakBar/include \ 15 | -I/Applications/MATLAB_R2014a.app/extern/include/ \ 16 | -I/usr/local/igl/libigl/external/AntTweakBar/src \ 17 | -I/usr/local/igl/libigl/external/glfw/include \ 18 | -L/usr/local/igl/libigl/external/glfw/lib -lglfw3 -framework QuartzCore -framework IOKit \ 19 | -I/usr/local/igl/libigl/external/Singular_Value_Decomposition/ \ 20 | -I/opt/local/include/ -I/usr/include/ \ 21 | -L/opt/local/lib -lCGAL -lCGAL_Core -lgmp -lmpfr -lboost_thread-mt -lboost_system-mt \ 22 | -L/Applications/MATLAB_R2014a.app/extern/lib/ \ 23 | -framework OpenGL \ 24 | -framework GLUT \ 25 | -framework AppKit \ 26 | -L/opt/local/lib -lboost_thread-mt -lboost_system-mt \ 27 | -L/Applications/MATLAB_R2014a.app/bin/maci64/ -lmx -lmat -lmex -leng \ 28 | -L/usr/local/igl/libigl/external/AntTweakBar/lib -lAntTweakBar \ 29 | -L/opt/local/lib -lboost_program_options-mt && ./.main "$@" 30 | #-DIGL_STATIC_LIBRARY -L/usr/local/igl/libigl/lib -liglviewer -ligl -liglmatlab -liglsvd3x3 -msse4.2 -fopenmp \ 31 | #rm -f .main 32 | rm -f $TEMP 33 | # END BASH SCRIPT 34 | exit 35 | */ 36 | 37 | // This is a tiny program to read in a coarse mesh and write out a refined 38 | // mesh. 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | 50 | extern "C" 51 | { 52 | #include "subdivision_skinning_wrapper.h" 53 | } 54 | 55 | int main(int argc, char * argv[]) 56 | { 57 | using namespace std; 58 | using namespace Eigen; 59 | using namespace igl; 60 | MatrixXd CV,V; 61 | MatrixXi CQ,Q; 62 | 63 | int level = 3; 64 | 65 | string filename = "torus.obj", output_filename = ""; 66 | switch(argc) 67 | { 68 | case 4: 69 | output_filename = argv[3]; 70 | case 3: 71 | level = atoi(argv[2]); 72 | case 2: 73 | // Read and prepare mesh 74 | filename = argv[1]; 75 | break; 76 | default: 77 | cerr<<"Usage:"< > vCQ; 86 | vector > vCV; 87 | if(!readOBJ(filename,vCV,vCQ)) 88 | { 89 | return EXIT_FAILURE; 90 | } 91 | list_to_matrix(vCV,CV); 92 | if(!list_to_matrix(vCQ,4,-1,CQ)) 93 | { 94 | cerr<<"Error: "< CVT = 113 | CV.cast().transpose(); 114 | const subdivision_evaluator_real_t * vs = CVT.data(); 115 | const int num_vs = CV.rows(); 116 | const MatrixXi CQT = CQ.cast().transpose(); 117 | const int * faces = CQT.data(); 118 | const int num_faces = CQ.rows(); 119 | eval = new_subdivision_evaluator( 120 | num_vs, (subdivision_evaluator_real_t*)vs, num_faces, (int*)faces,level); 121 | 122 | ///////////////////////////////////////////////////////////////////////// 123 | // Ask subdiv lib for refined mesh 124 | ///////////////////////////////////////////////////////////////////////// 125 | const int num_refined_faces = num_refined_quad_faces_of_subdivision_evaluator( eval ); 126 | int* refined_faces = new int[ num_refined_faces*4 ]; 127 | get_refined_quad_faces_of_subdivision_evaluator( eval, refined_faces ); 128 | const auto & QT = Map(refined_faces,4,num_refined_faces); 129 | Q = QT.transpose(); 130 | delete[] refined_faces; 131 | 132 | const int num_refined_vs = num_refined_vertices_of_subdivision_evaluator( eval ); 133 | subdivision_evaluator_real_t* refined_vs = new subdivision_evaluator_real_t[ num_refined_vs*3 ]; 134 | get_refined_vertices_of_subdivision_evaluator( eval, refined_vs ); 135 | const auto & VT = Map > 136 | (refined_vs,3,num_refined_vs); 137 | V = VT.cast().transpose(); 138 | delete[] refined_vs; 139 | 140 | if(!writeOBJ(output_filename,V,Q)) 141 | { 142 | cerr<<"Failed to write to "< 6 | 7 | namespace 8 | { 9 | /* 10 | Given the number of vertices 'num_vertices', 11 | a vector of sparse_vector_t 'sparse_vectors' containing a sequence of ( index, coefficient ) pairs, 12 | and an output Eigen::SparseMatrix 'matrix', 13 | fills 'matrix' such that the matrix multiplication of 'matrix' times a num_vertices-by-K matrix of control points 14 | yields the positions specified by 'sparse_vectors'. 15 | */ 16 | void convert_vector_of_sparse_vectors_to_matrix( int num_vertices, const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, subdivision_matrix::SparseMatrix_t& matrix ) 17 | { 18 | assert( num_vertices > 0 ); 19 | 20 | // Clear the output matrix. 21 | matrix.resize( sparse_vectors.size(), num_vertices ); 22 | matrix.setZero(); 23 | 24 | // We will fill the matrix with a vector of triplets. 25 | std::vector< Eigen::Triplet< subdivision_matrix::real_t > > triplets; 26 | 27 | // Count the number of triplets we will need. 28 | int nnz = 0; 29 | for( const auto& sp : sparse_vectors ) nnz += sp.size(); 30 | triplets.reserve( nnz ); 31 | 32 | // Convert 'sparse_vectors' to triplets. 33 | for( int row = 0; row < sparse_vectors.size(); ++row ) 34 | { 35 | for( const auto p : sparse_vectors[row] ) 36 | { 37 | triplets.push_back( { row, p.first, p.second } ); 38 | } 39 | } 40 | 41 | matrix.setFromTriplets( triplets.begin(), triplets.end() ); 42 | } 43 | void convert_vector_of_quad_indices_to_matrix( const std::vector< subdivision_matrix::index_t >& quad_faces, Eigen::Matrix< subdivision_matrix::index_t, Eigen::Dynamic, 4 >& quad_faces_out ) 44 | { 45 | assert( quad_faces.size() % 4 == 0 ); 46 | 47 | quad_faces_out.setZero( quad_faces.size()/4, 4 ); 48 | 49 | for( int i = 0; i+3 < quad_faces.size(); i += 4 ) 50 | { 51 | quad_faces_out( i/4, i%4 ) = quad_faces.at(i); 52 | } 53 | } 54 | 55 | } 56 | 57 | namespace subdivision_matrix 58 | { 59 | 60 | void compute_subdivision_coefficients_for_mesh( 61 | int num_vertices, 62 | const faces_t& faces, 63 | const int level, 64 | SparseMatrix_t& positions_out, 65 | std::vector< index_t >* quad_faces_out 66 | ) 67 | { 68 | // Intermediary output vectors. 69 | std::vector< sparse_vector_t > position_coeffs; 70 | std::vector< index_t > quad_faces; 71 | 72 | // Call the function we're wrapping. 73 | compute_subdivision_coefficients_for_mesh( 74 | num_vertices, 75 | faces, 76 | level, 77 | position_coeffs, 78 | quad_faces_out 79 | ); 80 | 81 | convert_vector_of_sparse_vectors_to_matrix( num_vertices, position_coeffs, positions_out ); 82 | } 83 | 84 | void compute_subdivision_coefficients_for_mesh( 85 | int num_vertices, 86 | OpenSubdiv::HbrMesh* mesh, 87 | const int level, 88 | SparseMatrix_t& positions_out, 89 | std::vector< index_t >* quad_faces_out 90 | ) 91 | { 92 | // Intermediary output vectors. 93 | std::vector< sparse_vector_t > position_coeffs; 94 | std::vector< index_t > quad_faces; 95 | 96 | // Call the function we're wrapping. 97 | compute_subdivision_coefficients_for_mesh( 98 | num_vertices, 99 | mesh, 100 | level, 101 | position_coeffs, 102 | quad_faces_out 103 | ); 104 | 105 | // NOTE: We don't call mesh->GetNumVertices() because that may not be the number of coarse vertices. 106 | convert_vector_of_sparse_vectors_to_matrix( num_vertices, position_coeffs, positions_out ); 107 | } 108 | 109 | } 110 | 111 | #ifdef SUBDIVISION_MATRICES_OSD_EIGEN_MAIN 112 | #include 113 | 114 | #define TORUS 32, { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } 115 | 116 | template< typename T > 117 | void print_dims( const std::string& prefix, const T& m ) 118 | { 119 | std::cerr << prefix << m.rows() << "x" << m.cols() << '\n'; 120 | } 121 | 122 | #include 123 | void save_compute_coefficients( int level, std::ostream& out ) 124 | { 125 | using namespace subdivision_matrix; 126 | 127 | SparseMatrix_t position_coeffs; 128 | compute_subdivision_coefficients_for_mesh( 129 | // Cube 130 | // CUBE, 131 | // Torus 132 | TORUS, 133 | level, 134 | position_coeffs 135 | ); 136 | 137 | std::vector< std::vector< real_t > > positions_orig( { { 1.25052, 0.517982, 0.353553 }, { 0.597239, 0.247384, 0.353553 }, { 0.597239, 0.247384, -0.353553 }, { 1.25052, 0.517982, -0.353553 }, { 0.517982, 1.25052, 0.353553 }, { 0.247384, 0.597239, 0.353553 }, { 0.247384, 0.597239, -0.353553 }, { 0.517982, 1.25052, -0.353553 }, { -0.517982, 1.25052, 0.353553 }, { -0.247384, 0.597239, 0.353553 }, { -0.247384, 0.597239, -0.353553 }, { -0.517982, 1.25052, -0.353553 }, { -1.25052, 0.517982, 0.353553 }, { -0.597239, 0.247384, 0.353553 }, { -0.597239, 0.247384, -0.353553 }, { -1.25052, 0.517982, -0.353553 }, { -1.25052, -0.517982, 0.353553 }, { -0.597239, -0.247384, 0.353553 }, { -0.597239, -0.247384, -0.353553 }, { -1.25052, -0.517982, -0.353553 }, { -0.517982, -1.25052, 0.353553 }, { -0.247384, -0.597239, 0.353553 }, { -0.247384, -0.597239, -0.353553 }, { -0.517982, -1.25052, -0.353553 }, { 0.517982, -1.25052, 0.353553 }, { 0.247384, -0.597239, 0.353553 }, { 0.247384, -0.597239, -0.353553 }, { 0.517982, -1.25052, -0.353553 }, { 1.25052, -0.517982, 0.353553 }, { 0.597239, -0.247384, 0.353553 }, { 0.597239, -0.247384, -0.353553 }, { 1.25052, -0.517982, -0.353553 } } ); 138 | Eigen::Matrix< real_t, 3, Eigen::Dynamic > positions; 139 | positions.resize( 3, positions_orig.size() ); 140 | for( int row = 0; row < positions.rows(); ++row ) 141 | for( int col = 0; col < positions.cols(); ++col ) 142 | { 143 | positions( row, col ) = positions_orig.at(col).at(row); 144 | } 145 | 146 | print_dims( "position_coeffs is ", position_coeffs ); 147 | print_dims( "positions is ", positions ); 148 | 149 | Eigen::MatrixXf all_pos = positions * position_coeffs.transpose(); 150 | print_dims( "all_pos is ", all_pos ); 151 | // out << all_pos << '\n'; 152 | 153 | for( int i = 0; i < position_coeffs.rows(); ++i ) 154 | { 155 | // std::cerr << position_coeffs.row(i) << '\n'; 156 | 157 | Eigen::VectorXf pos = positions * position_coeffs.row(i).transpose(); 158 | out << "v " << pos(0) << ' ' << pos(1) << ' ' << pos(2) << '\n'; 159 | } 160 | } 161 | 162 | #include 163 | #include 164 | void usage( const char* argv0 ) 165 | { 166 | std::cerr << "Usage: " << argv0 << " save_compute_coefficients level\n"; 167 | exit( -1 ); 168 | } 169 | int main( int argc, char* argv[] ) 170 | { 171 | using std::string; 172 | 173 | if( argc != 3 ) usage( argv[0] ); 174 | 175 | const int level = atoi( argv[2] ); 176 | if( level < 1 ) usage( argv[0] ); 177 | 178 | const string test_name = string(argv[1]); 179 | 180 | if( string("save_compute_coefficients") == test_name ) 181 | { 182 | std::ofstream out( "torus-eigen.obj" ); 183 | save_compute_coefficients( level, out ); 184 | } 185 | else 186 | { 187 | usage( argv[0] ); 188 | } 189 | 190 | return 0; 191 | } 192 | #endif 193 | -------------------------------------------------------------------------------- /lib/subdivision_matrices_eigen.cpp: -------------------------------------------------------------------------------- 1 | // c++ -g -std=c++11 -I../OpenSubdiv/opensubdiv -I../OpenSubdiv/regression subdivision_matrices_eigen.cpp subdivision_matrices.cpp -o subdivision_matrices_eigen -DSUBDIVISION_MATRICES_EIGEN_MAIN -I/usr/local/include/eigen3 2 | 3 | #include "subdivision_matrices_eigen.h" 4 | 5 | #include 6 | 7 | namespace 8 | { 9 | /* 10 | Given the number of vertices 'num_vertices', 11 | a vector of sparse_vector_t 'sparse_vectors' containing a sequence of ( index, coefficient ) pairs, 12 | and an output Eigen::SparseMatrix 'matrix', 13 | fills 'matrix' such that the matrix multiplication of 'matrix' times a num_vertices-by-K matrix of control points 14 | yields the positions specified by 'sparse_vectors'. 15 | */ 16 | void convert_vector_of_sparse_vectors_to_matrix( int num_vertices, const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, subdivision_matrix::SparseMatrix_t& matrix ) 17 | { 18 | assert( num_vertices > 0 ); 19 | 20 | // Clear the output matrix. 21 | matrix.resize( sparse_vectors.size(), num_vertices ); 22 | matrix.setZero(); 23 | 24 | // We will fill the matrix with a vector of triplets. 25 | std::vector< Eigen::Triplet< subdivision_matrix::real_t > > triplets; 26 | 27 | // Count the number of triplets we will need. 28 | int nnz = 0; 29 | for( const auto& sp : sparse_vectors ) nnz += sp.size(); 30 | triplets.reserve( nnz ); 31 | 32 | // Convert 'sparse_vectors' to triplets. 33 | for( int row = 0; row < sparse_vectors.size(); ++row ) 34 | { 35 | for( const auto p : sparse_vectors[row] ) 36 | { 37 | triplets.push_back( { row, p.first, p.second } ); 38 | } 39 | } 40 | 41 | matrix.setFromTriplets( triplets.begin(), triplets.end() ); 42 | } 43 | } 44 | 45 | namespace subdivision_matrix 46 | { 47 | 48 | void compute_subdivision_coefficients_for_mesh( 49 | int num_vertices, 50 | const faces_t& faces, 51 | const std::vector< real_t >& us, 52 | const std::vector< real_t >& vs, 53 | SparseMatrix_t& positions_out, 54 | SparseMatrix_t* du_out, 55 | SparseMatrix_t* dv_out 56 | ) 57 | { 58 | // Intermediary output vectors. 59 | std::vector< sparse_vector_t > position_coeffs; 60 | std::vector< sparse_vector_t > du_coeffs; 61 | std::vector< sparse_vector_t > dv_coeffs; 62 | 63 | // Call the function we're wrapping. 64 | compute_subdivision_coefficients_for_mesh( 65 | num_vertices, 66 | faces, 67 | us, vs, 68 | position_coeffs, 69 | du_out ? &du_coeffs : nullptr, 70 | dv_out ? &dv_coeffs : nullptr 71 | ); 72 | 73 | convert_vector_of_sparse_vectors_to_matrix( num_vertices, position_coeffs, positions_out ); 74 | if( du_out ) 75 | { 76 | assert( dv_out ); 77 | 78 | convert_vector_of_sparse_vectors_to_matrix( num_vertices, du_coeffs, *du_out ); 79 | convert_vector_of_sparse_vectors_to_matrix( num_vertices, dv_coeffs, *dv_out ); 80 | } 81 | } 82 | 83 | void compute_subdivision_coefficients_for_mesh( 84 | int num_vertices, 85 | OpenSubdiv::HbrMesh* mesh, 86 | const std::vector< real_t >& us, 87 | const std::vector< real_t >& vs, 88 | SparseMatrix_t& positions_out, 89 | SparseMatrix_t* du_out, 90 | SparseMatrix_t* dv_out 91 | ) 92 | { 93 | // Intermediary output vectors. 94 | std::vector< sparse_vector_t > position_coeffs; 95 | std::vector< sparse_vector_t > du_coeffs; 96 | std::vector< sparse_vector_t > dv_coeffs; 97 | 98 | // Call the function we're wrapping. 99 | compute_subdivision_coefficients_for_mesh( 100 | num_vertices, 101 | mesh, 102 | us, vs, 103 | position_coeffs, 104 | du_out ? &du_coeffs : nullptr, 105 | dv_out ? &dv_coeffs : nullptr 106 | ); 107 | 108 | // NOTE: We don't call mesh->GetNumVertices() because that may not be the number of coarse vertices. 109 | convert_vector_of_sparse_vectors_to_matrix( num_vertices, position_coeffs, positions_out ); 110 | if( du_out ) 111 | { 112 | assert( dv_out ); 113 | 114 | convert_vector_of_sparse_vectors_to_matrix( num_vertices, du_coeffs, *du_out ); 115 | convert_vector_of_sparse_vectors_to_matrix( num_vertices, dv_coeffs, *dv_out ); 116 | } 117 | } 118 | 119 | } 120 | 121 | #ifdef SUBDIVISION_MATRICES_EIGEN_MAIN 122 | #include 123 | 124 | #define TORUS 32, { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } 125 | 126 | template< typename T > 127 | void print_dims( const std::string& prefix, const T& m ) 128 | { 129 | std::cerr << prefix << m.rows() << "x" << m.cols() << '\n'; 130 | } 131 | 132 | #include 133 | void save_compute_coefficients( int resolution, std::ostream& out ) 134 | { 135 | using namespace subdivision_matrix; 136 | 137 | std::vector< real_t > us, vs; 138 | createUVs( resolution, resolution, us, vs ); 139 | 140 | SparseMatrix_t position_coeffs; 141 | compute_subdivision_coefficients_for_mesh( 142 | // Cube 143 | // CUBE, 144 | // Torus 145 | TORUS, 146 | us, vs, 147 | position_coeffs 148 | ); 149 | 150 | std::vector< std::vector< real_t > > positions_orig( { { 1.25052, 0.517982, 0.353553 }, { 0.597239, 0.247384, 0.353553 }, { 0.597239, 0.247384, -0.353553 }, { 1.25052, 0.517982, -0.353553 }, { 0.517982, 1.25052, 0.353553 }, { 0.247384, 0.597239, 0.353553 }, { 0.247384, 0.597239, -0.353553 }, { 0.517982, 1.25052, -0.353553 }, { -0.517982, 1.25052, 0.353553 }, { -0.247384, 0.597239, 0.353553 }, { -0.247384, 0.597239, -0.353553 }, { -0.517982, 1.25052, -0.353553 }, { -1.25052, 0.517982, 0.353553 }, { -0.597239, 0.247384, 0.353553 }, { -0.597239, 0.247384, -0.353553 }, { -1.25052, 0.517982, -0.353553 }, { -1.25052, -0.517982, 0.353553 }, { -0.597239, -0.247384, 0.353553 }, { -0.597239, -0.247384, -0.353553 }, { -1.25052, -0.517982, -0.353553 }, { -0.517982, -1.25052, 0.353553 }, { -0.247384, -0.597239, 0.353553 }, { -0.247384, -0.597239, -0.353553 }, { -0.517982, -1.25052, -0.353553 }, { 0.517982, -1.25052, 0.353553 }, { 0.247384, -0.597239, 0.353553 }, { 0.247384, -0.597239, -0.353553 }, { 0.517982, -1.25052, -0.353553 }, { 1.25052, -0.517982, 0.353553 }, { 0.597239, -0.247384, 0.353553 }, { 0.597239, -0.247384, -0.353553 }, { 1.25052, -0.517982, -0.353553 } } ); 151 | Eigen::Matrix< real_t, 3, Eigen::Dynamic > positions; 152 | positions.resize( 3, positions_orig.size() ); 153 | for( int row = 0; row < positions.rows(); ++row ) 154 | for( int col = 0; col < positions.cols(); ++col ) 155 | { 156 | positions( row, col ) = positions_orig.at(col).at(row); 157 | } 158 | 159 | print_dims( "position_coeffs is ", position_coeffs ); 160 | print_dims( "positions is ", positions ); 161 | 162 | Eigen::MatrixXf all_pos = positions * position_coeffs.transpose(); 163 | print_dims( "all_pos is ", all_pos ); 164 | // out << all_pos << '\n'; 165 | 166 | for( int i = 0; i < position_coeffs.rows(); ++i ) 167 | { 168 | // std::cerr << position_coeffs.row(i) << '\n'; 169 | 170 | Eigen::VectorXf pos = positions * position_coeffs.row(i).transpose(); 171 | out << "v " << pos(0) << ' ' << pos(1) << ' ' << pos(2) << '\n'; 172 | } 173 | } 174 | 175 | #include 176 | #include 177 | void usage( const char* argv0 ) 178 | { 179 | std::cerr << "Usage: " << argv0 << " save_compute_coefficients resolution\n"; 180 | exit( -1 ); 181 | } 182 | int main( int argc, char* argv[] ) 183 | { 184 | using std::string; 185 | 186 | if( argc != 3 ) usage( argv[0] ); 187 | 188 | const int resolution = atoi( argv[2] ); 189 | if( resolution < 0 ) usage( argv[0] ); 190 | 191 | const string test_name = string(argv[1]); 192 | 193 | if( string("save_compute_coefficients") == test_name ) 194 | { 195 | std::ofstream out( "torus-eigen.obj" ); 196 | save_compute_coefficients( resolution, out ); 197 | } 198 | else 199 | { 200 | usage( argv[0] ); 201 | } 202 | 203 | return 0; 204 | } 205 | #endif 206 | -------------------------------------------------------------------------------- /lib/subdivision_skinning_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "subdivision_skinning.h" 2 | #include "subdivision_matrices_osd_eigen.h" 3 | 4 | #include // std::copy 5 | 6 | // Static definitions. 7 | namespace 8 | { 9 | const int kDefaultLevel = 4; 10 | 11 | struct subdivision_evaluator_t 12 | { 13 | subdivision_skinning::vertices_t vertices; 14 | subdivision_skinning::faces_t faces; 15 | subdivision_matrix::SparseMatrix_t M_matrices; 16 | std::vector< subdivision_matrix::index_t > quad_faces; 17 | }; 18 | 19 | struct subdivision_skinning_engine_t 20 | { 21 | subdivision_matrix::SparseMatrix_t M_matrices; 22 | int num_transforms; 23 | subdivision_skinning::engine_t* engine; 24 | 25 | subdivision_skinning_engine_t() : engine( 0 ) {} 26 | ~subdivision_skinning_engine_t() 27 | { 28 | if( engine ) subdivision_skinning::delete_engine_t( engine ); 29 | engine = 0; 30 | } 31 | }; 32 | } 33 | 34 | 35 | extern "C" { 36 | 37 | #include "subdivision_skinning_wrapper.h" 38 | 39 | void* new_subdivision_evaluator( int num_vertices, const subdivision_evaluator_real_t* vertices, int num_faces, const int* faces, int level ) 40 | { 41 | assert( num_vertices > 0 ); 42 | assert( num_faces > 0 ); 43 | assert( vertices ); 44 | assert( faces ); 45 | assert( level > 0 ); 46 | 47 | // Create the result object. 48 | subdivision_evaluator_t* result = new subdivision_evaluator_t; 49 | 50 | // Copy the vertices. 51 | result->vertices.resize( num_vertices*3 ); 52 | std::copy( vertices, vertices + num_vertices*3, result->vertices.begin() ); 53 | 54 | // Copy the faces. 55 | result->faces.first.reserve( num_faces ); 56 | result->faces.second.reserve( num_faces*4 ); 57 | for( int fi = 0; fi < num_faces; ++fi ) 58 | { 59 | // Number of vertices in the face: 60 | result->faces.first.push_back( 0 ); 61 | for( int vi = 0; vi < 4; ++vi ) 62 | { 63 | const int vertex_index = faces[ 4*fi + vi ]; 64 | if( -1 != vertex_index ) 65 | { 66 | result->faces.second.push_back( vertex_index ); 67 | result->faces.first.back() += 1; 68 | } 69 | } 70 | assert( result->faces.first.back() == 3 || result->faces.first.back() == 4 ); 71 | } 72 | 73 | // Create M_matrices and quads. 74 | subdivision_matrix::compute_subdivision_coefficients_for_mesh( 75 | num_vertices, 76 | result->faces, 77 | level, 78 | result->M_matrices, 79 | &result->quad_faces 80 | ); 81 | 82 | return result; 83 | } 84 | void delete_subdivision_evaluator( void* subdivision_evaluator ) 85 | { 86 | delete static_cast< subdivision_evaluator_t* >( subdivision_evaluator ); 87 | } 88 | 89 | int num_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator_in ) 90 | { 91 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 92 | assert( subdivision_evaluator ); 93 | 94 | assert( subdivision_evaluator->quad_faces.size() % 4 == 0 ); 95 | return subdivision_evaluator->quad_faces.size() / 4; 96 | } 97 | void get_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator_in, int* refined_quad_faces_out ) 98 | { 99 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 100 | assert( subdivision_evaluator ); 101 | 102 | assert( refined_quad_faces_out ); 103 | std::copy( subdivision_evaluator->quad_faces.begin(), subdivision_evaluator->quad_faces.end(), refined_quad_faces_out ); 104 | } 105 | 106 | int num_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator_in ) 107 | { 108 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 109 | assert( subdivision_evaluator ); 110 | 111 | return subdivision_evaluator->M_matrices.rows(); 112 | } 113 | void get_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator_in, subdivision_evaluator_real_t* refined_vertices_out ) 114 | { 115 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 116 | assert( subdivision_evaluator ); 117 | assert( refined_vertices_out ); 118 | 119 | assert( subdivision_evaluator->vertices.size() == subdivision_evaluator->M_matrices.cols()*3 ); 120 | const Eigen::Map< const Eigen::Matrix< subdivision_skinning::vertices_t::value_type, Eigen::Dynamic, 3, Eigen::RowMajor > > vs( &subdivision_evaluator->vertices[0], subdivision_evaluator->M_matrices.cols(), 3 ); 121 | Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, 3, Eigen::RowMajor > > out( refined_vertices_out, subdivision_evaluator->M_matrices.rows(), 3 ); 122 | out = ( subdivision_evaluator->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); 123 | } 124 | void get_refined_vertices_of_subdivision_evaluator_with_control_vertices( const void* subdivision_evaluator_in, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ) 125 | { 126 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 127 | assert( subdivision_evaluator ); 128 | assert( vertex_dimension > 0 ); 129 | assert( control_vertices ); 130 | assert( refined_vertices_out ); 131 | 132 | const Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > vs( control_vertices, subdivision_evaluator->M_matrices.cols(), vertex_dimension ); 133 | Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > out( refined_vertices_out, subdivision_evaluator->M_matrices.rows(), vertex_dimension ); 134 | out = ( subdivision_evaluator->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); 135 | } 136 | 137 | void* new_subdivision_skinning_engine( const void* subdivision_evaluator_in, int num_transforms, const subdivision_evaluator_real_t* weights_in ) 138 | { 139 | assert( num_transforms > 0 ); 140 | assert( weights_in ); 141 | assert( subdivision_evaluator_in ); 142 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 143 | 144 | subdivision_skinning_engine_t* result = new subdivision_skinning_engine_t; 145 | result->num_transforms = num_transforms; 146 | result->M_matrices = subdivision_evaluator->M_matrices; 147 | const subdivision_matrix::MatrixXX_t weights = Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( weights_in, subdivision_evaluator->M_matrices.rows(), num_transforms ).cast< subdivision_matrix::real_t >(); 148 | result->engine = subdivision_skinning::new_engine_for_control_mesh( subdivision_evaluator->vertices, subdivision_evaluator->faces, subdivision_evaluator->M_matrices, weights ); 149 | 150 | return result; 151 | } 152 | void delete_subdivision_skinning_engine( void* subdivision_skinning_engine ) 153 | { 154 | delete static_cast< subdivision_skinning_engine_t* >( subdivision_skinning_engine ); 155 | } 156 | 157 | void compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( const void* subdivision_skinning_engine_in, const subdivision_evaluator_real_t* transforms_in, subdivision_evaluator_real_t* control_mesh_vertices_out ) 158 | { 159 | const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine_in ); 160 | assert( engine ); 161 | assert( engine->engine ); 162 | assert( transforms_in ); 163 | assert( control_mesh_vertices_out ); 164 | 165 | // Copy the transforms. 166 | std::vector< subdivision_skinning::transform_t > transforms( engine->num_transforms ); 167 | for( int i = 0; i < engine->num_transforms; ++i ) 168 | { 169 | transforms.at(i) = Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, 4, 4, Eigen::RowMajor > >( transforms_in + 16*i, 4, 4 ); 170 | } 171 | 172 | // Solve for the new control mesh vertices. 173 | subdivision_skinning::vertices_t control_mesh_vertices; 174 | update_control_vertices_given_handle_transforms( engine->engine, transforms, control_mesh_vertices ); 175 | 176 | // Copy to the output. 177 | assert( control_mesh_vertices.size() == engine->M_matrices.cols()*3 ); 178 | // Copy (and convert floating point formats if needed). 179 | Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, 3, Eigen::RowMajor > >( control_mesh_vertices_out, engine->M_matrices.cols(), 3 ) = 180 | Eigen::Map< Eigen::Matrix< subdivision_skinning::vertices_t::value_type, Eigen::Dynamic, 3, Eigen::RowMajor > >( &control_mesh_vertices[0], engine->M_matrices.cols(), 3 ); 181 | } 182 | 183 | int num_refined_vertices_of_subdivision_skinning_engine( const void* subdivision_skinning_engine ) 184 | { 185 | const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine ); 186 | assert( engine ); 187 | 188 | return engine->M_matrices.rows(); 189 | } 190 | void get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( const void* subdivision_skinning_engine, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ) 191 | { 192 | const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine ); 193 | assert( engine ); 194 | assert( vertex_dimension > 0 ); 195 | assert( control_vertices ); 196 | assert( refined_vertices_out ); 197 | 198 | const Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > vs( control_vertices, engine->M_matrices.cols(), vertex_dimension ); 199 | Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > out( refined_vertices_out, engine->M_matrices.rows(), vertex_dimension ); 200 | out = ( engine->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /subdivgui/subdivision_skinning_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "subdivision_skinning.h" 2 | #include "subdivision_matrices_osd_eigen.h" 3 | 4 | #include // std::copy 5 | 6 | // Static definitions. 7 | namespace 8 | { 9 | const int kDefaultLevel = 4; 10 | 11 | struct subdivision_evaluator_t 12 | { 13 | subdivision_skinning::vertices_t vertices; 14 | subdivision_skinning::faces_t faces; 15 | subdivision_matrix::SparseMatrix_t M_matrices; 16 | std::vector< subdivision_matrix::index_t > quad_faces; 17 | }; 18 | 19 | struct subdivision_skinning_engine_t 20 | { 21 | subdivision_matrix::SparseMatrix_t M_matrices; 22 | int num_transforms; 23 | subdivision_skinning::engine_t* engine; 24 | 25 | subdivision_skinning_engine_t() : engine( 0 ) {} 26 | ~subdivision_skinning_engine_t() 27 | { 28 | if( engine ) subdivision_skinning::delete_engine_t( engine ); 29 | engine = 0; 30 | } 31 | }; 32 | } 33 | 34 | 35 | extern "C" { 36 | 37 | #include "subdivision_skinning_wrapper.h" 38 | 39 | void* new_subdivision_evaluator( int num_vertices, const subdivision_evaluator_real_t* vertices, int num_faces, const int* faces, int level ) 40 | { 41 | assert( num_vertices > 0 ); 42 | assert( num_faces > 0 ); 43 | assert( vertices ); 44 | assert( faces ); 45 | assert( level > 0 ); 46 | 47 | // Create the result object. 48 | subdivision_evaluator_t* result = new subdivision_evaluator_t; 49 | 50 | // Copy the vertices. 51 | result->vertices.resize( num_vertices*3 ); 52 | std::copy( vertices, vertices + num_vertices*3, result->vertices.begin() ); 53 | 54 | // Copy the faces. 55 | result->faces.first.reserve( num_faces ); 56 | result->faces.second.reserve( num_faces*4 ); 57 | for( int fi = 0; fi < num_faces; ++fi ) 58 | { 59 | // Number of vertices in the face: 60 | result->faces.first.push_back( 0 ); 61 | for( int vi = 0; vi < 4; ++vi ) 62 | { 63 | const int vertex_index = faces[ 4*fi + vi ]; 64 | if( -1 != vertex_index ) 65 | { 66 | result->faces.second.push_back( vertex_index ); 67 | result->faces.first.back() += 1; 68 | } 69 | } 70 | assert( result->faces.first.back() == 3 || result->faces.first.back() == 4 ); 71 | } 72 | 73 | // Create M_matrices and quads. 74 | subdivision_matrix::compute_subdivision_coefficients_for_mesh( 75 | num_vertices, 76 | result->faces, 77 | level, 78 | result->M_matrices, 79 | &result->quad_faces 80 | ); 81 | 82 | return result; 83 | } 84 | void delete_subdivision_evaluator( void* subdivision_evaluator ) 85 | { 86 | delete static_cast< subdivision_evaluator_t* >( subdivision_evaluator ); 87 | } 88 | 89 | int num_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator_in ) 90 | { 91 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 92 | assert( subdivision_evaluator ); 93 | 94 | assert( subdivision_evaluator->quad_faces.size() % 4 == 0 ); 95 | return subdivision_evaluator->quad_faces.size() / 4; 96 | } 97 | void get_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator_in, int* refined_quad_faces_out ) 98 | { 99 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 100 | assert( subdivision_evaluator ); 101 | 102 | assert( refined_quad_faces_out ); 103 | std::copy( subdivision_evaluator->quad_faces.begin(), subdivision_evaluator->quad_faces.end(), refined_quad_faces_out ); 104 | } 105 | 106 | int num_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator_in ) 107 | { 108 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 109 | assert( subdivision_evaluator ); 110 | 111 | return subdivision_evaluator->M_matrices.rows(); 112 | } 113 | void get_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator_in, subdivision_evaluator_real_t* refined_vertices_out ) 114 | { 115 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 116 | assert( subdivision_evaluator ); 117 | assert( refined_vertices_out ); 118 | 119 | assert( subdivision_evaluator->vertices.size() == subdivision_evaluator->M_matrices.cols()*3 ); 120 | const Eigen::Map< const Eigen::Matrix< subdivision_skinning::vertices_t::value_type, Eigen::Dynamic, 3, Eigen::RowMajor > > vs( &subdivision_evaluator->vertices[0], subdivision_evaluator->M_matrices.cols(), 3 ); 121 | Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, 3, Eigen::RowMajor > > out( refined_vertices_out, subdivision_evaluator->M_matrices.rows(), 3 ); 122 | out = ( subdivision_evaluator->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); 123 | } 124 | void get_refined_vertices_of_subdivision_evaluator_with_control_vertices( const void* subdivision_evaluator_in, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ) 125 | { 126 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 127 | assert( subdivision_evaluator ); 128 | assert( vertex_dimension > 0 ); 129 | assert( control_vertices ); 130 | assert( refined_vertices_out ); 131 | 132 | const Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > vs( control_vertices, subdivision_evaluator->M_matrices.cols(), vertex_dimension ); 133 | Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > out( refined_vertices_out, subdivision_evaluator->M_matrices.rows(), vertex_dimension ); 134 | out = ( subdivision_evaluator->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); 135 | } 136 | 137 | void* new_subdivision_skinning_engine( const void* subdivision_evaluator_in, int num_transforms, const subdivision_evaluator_real_t* weights_in ) 138 | { 139 | assert( num_transforms > 0 ); 140 | assert( weights_in ); 141 | assert( subdivision_evaluator_in ); 142 | const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); 143 | 144 | subdivision_skinning_engine_t* result = new subdivision_skinning_engine_t; 145 | result->num_transforms = num_transforms; 146 | result->M_matrices = subdivision_evaluator->M_matrices; 147 | const subdivision_matrix::MatrixXX_t weights = Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( weights_in, subdivision_evaluator->M_matrices.rows(), num_transforms ).cast< subdivision_matrix::real_t >(); 148 | result->engine = subdivision_skinning::new_engine_for_control_mesh( subdivision_evaluator->vertices, subdivision_evaluator->faces, subdivision_evaluator->M_matrices, weights ); 149 | 150 | return result; 151 | } 152 | void delete_subdivision_skinning_engine( void* subdivision_skinning_engine ) 153 | { 154 | delete static_cast< subdivision_skinning_engine_t* >( subdivision_skinning_engine ); 155 | } 156 | 157 | void compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( const void* subdivision_skinning_engine_in, const subdivision_evaluator_real_t* transforms_in, subdivision_evaluator_real_t* control_mesh_vertices_out ) 158 | { 159 | const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine_in ); 160 | assert( engine ); 161 | assert( engine->engine ); 162 | assert( transforms_in ); 163 | assert( control_mesh_vertices_out ); 164 | 165 | // Copy the transforms. 166 | std::vector< subdivision_skinning::transform_t > transforms( engine->num_transforms ); 167 | for( int i = 0; i < engine->num_transforms; ++i ) 168 | { 169 | transforms.at(i) = Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, 4, 4, Eigen::RowMajor > >( transforms_in + 16*i, 4, 4 ); 170 | } 171 | 172 | // Solve for the new control mesh vertices. 173 | subdivision_skinning::vertices_t control_mesh_vertices; 174 | update_control_vertices_given_handle_transforms( engine->engine, transforms, control_mesh_vertices ); 175 | 176 | // Copy to the output. 177 | assert( control_mesh_vertices.size() == engine->M_matrices.cols()*3 ); 178 | // Copy (and convert floating point formats if needed). 179 | Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, 3, Eigen::RowMajor > >( control_mesh_vertices_out, engine->M_matrices.cols(), 3 ) = 180 | Eigen::Map< Eigen::Matrix< subdivision_skinning::vertices_t::value_type, Eigen::Dynamic, 3, Eigen::RowMajor > >( &control_mesh_vertices[0], engine->M_matrices.cols(), 3 ); 181 | } 182 | 183 | int num_refined_vertices_of_subdivision_skinning_engine( const void* subdivision_skinning_engine ) 184 | { 185 | const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine ); 186 | assert( engine ); 187 | 188 | return engine->M_matrices.rows(); 189 | } 190 | void get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( const void* subdivision_skinning_engine, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ) 191 | { 192 | const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine ); 193 | assert( engine ); 194 | assert( vertex_dimension > 0 ); 195 | assert( control_vertices ); 196 | assert( refined_vertices_out ); 197 | 198 | const Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > vs( control_vertices, engine->M_matrices.cols(), vertex_dimension ); 199 | Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > out( refined_vertices_out, engine->M_matrices.rows(), vertex_dimension ); 200 | out = ( engine->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /lib/subdivision_limit_mesh.cpp: -------------------------------------------------------------------------------- 1 | // c++ -g -std=c++11 -I/Users/yotam/Work/ext/OpenSubdiv/opensubdiv -I/Users/yotam/Work/ext/OpenSubdiv/regression subdivision_limit_mesh.cpp subdivision_matrices.cpp -o subdivision_limit_mesh -DSUBDIVISION_LIMIT_MESH_MAIN 2 | 3 | #include "subdivision_limit_mesh.h" 4 | #include "save_obj.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace subdivision_limit_mesh 10 | { 11 | 12 | void deduplicate_vertices_and_indices( subdivision_matrix::MatrixX3_t duplicated_vertices, std::vector< std::vector< real_t > >& unique_vertices, std::vector< std::vector< int > >& limit_quad_faces_out ); 13 | void efficient_deduplicate_vertices_and_indices( int num_u, int num_v, subdivision_matrix::MatrixX3_t duplicated_vertices, std::vector< std::vector< real_t > >& unique_vertices, std::vector< std::vector< int > >& limit_quad_faces_out ) ; 14 | 15 | // The same as subdivision_matrices::createUVs() but samples 0 and 1. 16 | // This is a helper function for compute_quad_limit_mesh_for_mesh(). 17 | void createUVsWithZeroAndOne( int NUM_U, int NUM_V, std::vector< real_t >& us, std::vector< real_t >& vs ) 18 | { 19 | assert( NUM_U > 0 ); 20 | assert( NUM_V > 0 ); 21 | 22 | us.clear(); 23 | vs.clear(); 24 | 25 | us.reserve( NUM_U * NUM_V ); 26 | vs.reserve( NUM_U * NUM_V ); 27 | 28 | /// 1 Create the u and v samples, which are identical to the ones in createUVs() 29 | /// but have 0 and 1 at the beginning and end. 30 | std::vector< real_t > u_samples, v_samples; 31 | u_samples.reserve( NUM_U + 2 ); 32 | u_samples.push_back( 0. ); 33 | for( int ui = 0; ui < NUM_U; ++ui ) 34 | { 35 | // Sample from .5 to .95, so that each sample has an equal piece of area. 36 | const real_t u = real_t( ui + 0.5 )/NUM_U; 37 | u_samples.push_back( u ); 38 | } 39 | u_samples.push_back( 1.0 ); 40 | assert( u_samples.size() == NUM_U + 2 ); 41 | 42 | v_samples.reserve( NUM_U + 2 ); 43 | v_samples.push_back( 0. ); 44 | for( int vi = 0; vi < NUM_V; ++vi ) 45 | { 46 | // Sample from .5 to .95, so that each sample has an equal piece of area. 47 | const real_t v = real_t( vi + 0.5 )/NUM_V; 48 | v_samples.push_back( v ); 49 | } 50 | v_samples.push_back( 1.0 ); 51 | assert( v_samples.size() == NUM_V + 2 ); 52 | 53 | /// 2 Put the Cartesian product in us and vs. 54 | for( int ui = 0; ui < u_samples.size(); ++ui ) 55 | { 56 | const real_t u = u_samples.at(ui); 57 | for( int vi = 0; vi < v_samples.size(); ++vi ) 58 | { 59 | const real_t v = v_samples.at(vi); 60 | 61 | us.push_back( u ); 62 | vs.push_back( v ); 63 | } 64 | } 65 | } 66 | 67 | void compute_quad_limit_mesh_for_mesh( 68 | const subdivision_matrix::MatrixX3_t& vertices, 69 | OpenSubdiv::HbrMesh* mesh, 70 | int num_u, int num_v, 71 | std::vector< std::vector< real_t > >& limit_vertices_out, 72 | std::vector< std::vector< int > >& limit_quad_faces_out 73 | ) 74 | { 75 | /// 1 createUVs just like subdivision_matrices::createUVs() except with 0 and 1 in each row/column. 76 | /// 2 Extract the M matrix for computing limit vertex positions. 77 | /// 3 Calculate the positions. 78 | /// 4 Create a quad mesh for every "num_u+2 x num_v+2" vertices (they sample a control mesh face). 79 | /// 5 De-duplicate the vertices. 80 | 81 | /// 1 82 | std::vector< real_t > us, vs; 83 | createUVsWithZeroAndOne( num_u, num_v, us, vs ); 84 | 85 | 86 | /// 2 87 | subdivision_matrix::SparseMatrix_t positions; 88 | subdivision_matrix::compute_subdivision_coefficients_for_mesh( 89 | vertices.rows(), mesh, 90 | us, vs, 91 | positions 92 | ); 93 | 94 | 95 | /// 3 96 | subdivision_matrix::MatrixX3_t intermediate_vertices = positions * vertices; 97 | // std::cout << intermediate_vertices.rows() << " " << intermediate_vertices.size() << std::endl; 98 | // limit_vertices_out = positions * vertices; 99 | 100 | 101 | /// 4 102 | // Because of how createUVsWithZeroAndOne() works, we know that 103 | // every "num_u+2 x num_v+2" vertices sample a control mesh quad face in a grid. 104 | // Create faces for the sub-quads that make up this grid. 105 | // The order of elements in 'limit_vertices_out' is, for each control mesh quad face: 106 | // (0,0), (0,.5/num_v), ..., (0,1), (.5/num_u,0), (.5/num_u,.5/num_v), ..., (.5/num_u,1), ... 107 | // In other words, every 'num_v+2' vertices forms a column (strip of constant u value). 108 | // The number of vertices should be an integer multiple of the number of control mesh quad faces 109 | // (n.b. the number of control mesh quad faces may be increased by extraordinary vertex sub-faces). 110 | assert( intermediate_vertices.rows() % ( (num_u+2)*(num_v+2) ) == 0 ); 111 | for( int off = 0; off < intermediate_vertices.rows(); off += (num_u+2)*(num_v+2) ) 112 | { 113 | // Iterate over each quad and add it to the faces. 114 | for( int u_off = 1; u_off < num_u + 2; ++u_off ) 115 | { 116 | for( int v_off = 1; v_off < num_v + 2; ++v_off ) 117 | { 118 | // TODO: Add the face: 119 | // (off + (u_off-1,v_off-1)), (off + (u_off-1,v_off)), (off + (u_off,v_off)), (off + (u_off,v_off-1)), clockwise 120 | // To convert from (off + (ui,vi)) to an actual vertex index, 121 | // it is something like: off + ui*(num_u+2) + vi. 122 | std::vector< int > quad{(off + (u_off-1)*(num_u+2) + v_off-1), 123 | (off + (u_off-1)*(num_u+2) + v_off), 124 | (off + u_off*(num_u+2) + v_off), 125 | (off + u_off*(num_u+2) + v_off-1)}; 126 | limit_quad_faces_out.push_back( quad ); 127 | } 128 | 129 | } 130 | } 131 | 132 | /// 5 133 | // TODO: De-duplicate vertices with the same position. 134 | // deduplicate_vertices_and_indices( intermediate_vertices, limit_vertices_out, limit_quad_faces_out ); 135 | efficient_deduplicate_vertices_and_indices( num_u+2, num_v+2, intermediate_vertices, limit_vertices_out, limit_quad_faces_out ); 136 | /// 6 Save the vertices and faces to an obj file. 137 | save_obj::save_mesh( "model.obj", limit_quad_faces_out, limit_vertices_out ); 138 | 139 | for( auto quad: limit_quad_faces_out ) 140 | { 141 | for( auto index : quad ) 142 | std::cout << index << " "; 143 | std::cout << std::endl; 144 | } 145 | /* test 146 | for( int i=0; i v; 148 | for( int j=0; j >& unique_vertices, std::vector< std::vector< int > >& limit_quad_faces_out ) 174 | { 175 | int old_num = duplicated_vertices.rows(); 176 | int old2new [ old_num ]; 177 | std::vector< real_t > vertex { 0, 0, 0 }; 178 | 179 | for( int i=0; i >& unique_vertices, std::vector< std::vector< int > >& limit_quad_faces_out ) 210 | { 211 | int old_num = duplicated_vertices.rows(); 212 | int old2new [ old_num ]; 213 | struct lookup { 214 | int index; 215 | std::vector< real_t > pos; 216 | }; 217 | std::vector< lookup > lookup_list; 218 | 219 | for( int i=0; i vertex { duplicated_vertices(i,0), duplicated_vertices(i,1), duplicated_vertices(i,2) }; 222 | 223 | int res = i % ( num_u*num_v ); 224 | if( res < num_v or res >= (num_u-1)*num_v or res % num_v == 0 or ( (res+1) % num_v ) == 0 ) { 225 | int p = 0; 226 | for( auto element : lookup_list ) { 227 | std::vector< real_t > pos = element.pos; 228 | if ( pos[0] == vertex[0] and pos[1] == vertex[1] and pos[2] == vertex[2] ) 229 | break; 230 | p++; 231 | } 232 | if( p != lookup_list.size() ) 233 | old2new[ i ] = lookup_list[p].index; 234 | else { 235 | unique_vertices.push_back( vertex ); 236 | old2new[ i ] = unique_vertices.size()-1; 237 | 238 | lookup new_lookup; 239 | new_lookup.index = old2new[ i ]; 240 | new_lookup.pos = vertex; 241 | lookup_list.push_back( new_lookup ); 242 | } 243 | 244 | } 245 | else { 246 | unique_vertices.push_back( vertex ); 247 | old2new[ i ] = unique_vertices.size()-1; 248 | } 249 | } 250 | 251 | // now re-write the vertex indices in faces. 252 | 253 | for( int i=0; i 4 | #include 5 | #include 6 | #include 7 | #include // atoi 8 | #include 9 | #include 10 | 11 | #include // std::fill 12 | 13 | extern "C" 14 | { 15 | #include "subdivision_skinning_wrapper.h" 16 | } 17 | 18 | namespace 19 | { 20 | 21 | // The Torus data from OpenSubdiv 22 | subdivision_evaluator_real_t TORUS_VS[] = { 1.25052, 0.517982, 0.353553, 0.597239, 0.247384, 0.353553, 0.597239, 0.247384, -0.353553, 1.25052, 0.517982, -0.353553, 0.517982, 1.25052, 0.353553, 0.247384, 0.597239, 0.353553, 0.247384, 0.597239, -0.353553, 0.517982, 1.25052, -0.353553, -0.517982, 1.25052, 0.353553, -0.247384, 0.597239, 0.353553, -0.247384, 0.597239, -0.353553, -0.517982, 1.25052, -0.353553, -1.25052, 0.517982, 0.353553, -0.597239, 0.247384, 0.353553, -0.597239, 0.247384, -0.353553, -1.25052, 0.517982, -0.353553, -1.25052, -0.517982, 0.353553, -0.597239, -0.247384, 0.353553, -0.597239, -0.247384, -0.353553, -1.25052, -0.517982, -0.353553, -0.517982, -1.25052, 0.353553, -0.247384, -0.597239, 0.353553, -0.247384, -0.597239, -0.353553, -0.517982, -1.25052, -0.353553, 0.517982, -1.25052, 0.353553, 0.247384, -0.597239, 0.353553, 0.247384, -0.597239, -0.353553, 0.517982, -1.25052, -0.353553, 1.25052, -0.517982, 0.353553, 0.597239, -0.247384, 0.353553, 0.597239, -0.247384, -0.353553, 1.25052, -0.517982, -0.353553 }; 23 | // #define TORUS_FACES { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } 24 | int TORUS_FACES[] = { 4, 5, 1, 0, 5, 6, 2, 1, 6, 7, 3, 2, 7, 4, 0, 3, 8, 9, 5, 4, 9, 10, 6, 5, 10, 11, 7, 6, 11, 8, 4, 7, 12, 13, 9, 8, 13, 14, 10, 9, 14, 15, 11, 10, 15, 12, 8, 11, 16, 17, 13, 12, 17, 18, 14, 13, 18, 19, 15, 14, 19, 16, 12, 15, 20, 21, 17, 16, 21, 22, 18, 17, 22, 23, 19, 18, 23, 20, 16, 19, 24, 25, 21, 20, 25, 26, 22, 21, 26, 27, 23, 22, 27, 24, 20, 23, 28, 29, 25, 24, 29, 30, 26, 25, 30, 31, 27, 26, 31, 28, 24, 27, 0, 1, 29, 28, 1, 2, 30, 29, 2, 3, 31, 30, 3, 0, 28, 31 }; 25 | 26 | void print_OBJ( std::ostream& out, int num_vs, subdivision_evaluator_real_t* vs, int num_faces, int* quad_faces, const std::string& header_message = "" ) 27 | { 28 | // Save an optional header message. 29 | out << "# OBJ: " << header_message << '\n'; 30 | 31 | // Save vertices. 32 | for( int i = 0; i < num_vs; ++i ) 33 | { 34 | out << "v " << vs[3*i+0] << ' ' << vs[3*i+1] << ' ' << vs[3*i+2] << '\n'; 35 | } 36 | 37 | out << '\n'; 38 | 39 | for( int i = 0; i < num_faces; ++i ) 40 | { 41 | // Vertices are 1-indexed. 42 | out << "f " << (1+quad_faces[ 4*i + 0 ]) << ' ' << (1+quad_faces[ 4*i + 1 ]) << ' ' << (1+quad_faces[ 4*i + 2 ]); 43 | if( -1 != quad_faces[ 4*i + 3 ] ) out << ' ' << (1+quad_faces[ 4*i + 3 ]) << '\n'; 44 | else out << '\n'; 45 | } 46 | } 47 | void save_OBJ( const std::string& out_path, int num_vs, subdivision_evaluator_real_t* vs, int num_faces, int* quad_faces, const std::string& header_message = "" ) 48 | { 49 | std::ofstream out( out_path ); 50 | print_OBJ( out, num_vs, vs, num_faces, quad_faces, header_message ); 51 | std::cout << "Saved a mesh to: " << out_path << '\n'; 52 | } 53 | 54 | bool load_OBJ( std::istream& in, int* num_vs_out, subdivision_evaluator_real_t** vs_out, int* num_faces_out, int** quad_faces_out ) 55 | { 56 | std::string line; 57 | std::istringstream linestr; 58 | 59 | std::vector< subdivision_evaluator_real_t > vs; 60 | std::vector< int > faces; 61 | 62 | int line_number = 0; 63 | std::string type; 64 | while( !( in >> std::ws ).eof() ) 65 | { 66 | line_number += 1; 67 | 68 | std::getline( in, line ); 69 | if( line.empty() ) continue; 70 | 71 | linestr.clear(); 72 | linestr.str( line ); 73 | linestr >> type; 74 | 75 | if( type == std::string("v") ) 76 | { 77 | subdivision_evaluator_real_t x(-31337), y(-31337), z(-31337); 78 | linestr >> x >> y >> z; 79 | vs.push_back( x ); 80 | vs.push_back( y ); 81 | vs.push_back( z ); 82 | if( !linestr ) 83 | { 84 | std::cerr << "Invalid vertex encountered on line " << line_number << ".\n"; 85 | return false; 86 | } 87 | } 88 | else if( type == std::string("f") ) 89 | { 90 | int num_face_vertices = 0; 91 | while( !( linestr >> std::ws ).eof() ) 92 | { 93 | std::string vi; 94 | linestr >> vi; 95 | num_face_vertices += 1; 96 | 97 | // Now look for a slash, in case there's a bundle. 98 | int slash = vi.find( "/" ); 99 | if( std::string::npos == slash ) 100 | { 101 | faces.push_back( atoi( vi.c_str() ) ); 102 | } 103 | else 104 | { 105 | faces.push_back( atoi( vi.substr( 0, slash ).c_str() ) ); 106 | } 107 | // OBJ are 1-indexed, and a negative number indexes from the end. 108 | // Nothing should equal 0. 109 | if( faces.back() == 0 ) 110 | { 111 | std::cerr << "Invalid face vertex index of 0 encountered on line " << line_number << ".\n"; 112 | return false; 113 | } 114 | } 115 | 116 | if( !( num_face_vertices == 3 || num_face_vertices == 4 ) ) 117 | { 118 | std::cerr << "Invalid face without three or four vertices encountered on line " << line_number << ".\n"; 119 | return false; 120 | } 121 | // A triangle has a -1 as the fourth vertex index. 122 | // UPDATE: push_back a 0, because 123 | if( 3 == num_face_vertices ) faces.push_back( 0 ); 124 | } 125 | } 126 | 127 | assert( vs.size() % 3 == 0 ); 128 | *num_vs_out = vs.size()/3; 129 | *vs_out = new subdivision_evaluator_real_t[ vs.size() ]; 130 | std::copy( vs.begin(), vs.end(), *vs_out ); 131 | 132 | assert( faces.size() % 4 == 0 ); 133 | *num_faces_out = faces.size()/4; 134 | *quad_faces_out = new int[ faces.size() ]; 135 | for( int i = 0; i < faces.size(); ++i ) 136 | { 137 | (*quad_faces_out)[i] = 138 | faces[i] >= 0 139 | ? faces[i] - 1 140 | // Negative indices add to the back. 141 | : vs.size() + faces[i] 142 | ; 143 | } 144 | 145 | return true; 146 | } 147 | bool load_OBJ( const std::string& in_path, int* num_vs_out, subdivision_evaluator_real_t** vs_out, int* num_faces_out, int** quad_faces_out ) 148 | { 149 | std::ifstream in( in_path ); 150 | return load_OBJ( in, num_vs_out, vs_out, num_faces_out, quad_faces_out ); 151 | } 152 | 153 | } 154 | 155 | 156 | int main( int argc, const char* argv[] ) 157 | { 158 | subdivision_evaluator_real_t* vs = &TORUS_VS[0]; 159 | int* faces = &TORUS_FACES[0]; 160 | int num_vs = sizeof( TORUS_VS )/sizeof( subdivision_evaluator_real_t )/3; 161 | int num_faces = sizeof( TORUS_FACES )/sizeof( int )/4; 162 | 163 | if( argc == 2 ) 164 | { 165 | const bool success = load_OBJ( argv[1], &num_vs, &vs, &num_faces, &faces ); 166 | if( !success ) return -1; 167 | } 168 | 169 | std::cout << "Number of vertices: " << num_vs << '\n'; 170 | std::cout << "Number of faces: " << num_faces << '\n'; 171 | 172 | save_OBJ( "original.obj", num_vs, vs, num_faces, faces ); 173 | 174 | void* eval = new_subdivision_evaluator( num_vs, vs, num_faces, faces, 4 ); 175 | 176 | const int num_refined_faces = num_refined_quad_faces_of_subdivision_evaluator( eval ); 177 | int* refined_faces = new int[ num_refined_faces*4 ]; 178 | get_refined_quad_faces_of_subdivision_evaluator( eval, refined_faces ); 179 | 180 | const int num_refined_vs = num_refined_vertices_of_subdivision_evaluator( eval ); 181 | subdivision_evaluator_real_t* refined_vs = new subdivision_evaluator_real_t[ num_refined_vs*3 ]; 182 | get_refined_vertices_of_subdivision_evaluator( eval, refined_vs ); 183 | 184 | save_OBJ( "original-refined.obj", num_refined_vs, refined_vs, num_refined_faces, refined_faces ); 185 | 186 | // Test the non-3 dimensional path by duplicating submitting 6-dimensional 187 | // vertices xyzxyz: 188 | { 189 | std::vector< subdivision_evaluator_real_t > control_vs6( num_vs*6 ); 190 | for( int i = 0; i < num_vs; ++i ) 191 | for( int c = 0; c < 3; ++c ) 192 | { 193 | control_vs6[ 6*i + 3 + c ] = control_vs6[ 6*i + c ] = vs[ 3*i + c ]; 194 | } 195 | std::vector< subdivision_evaluator_real_t > refined_vs6( num_refined_vs*6 ); 196 | get_refined_vertices_of_subdivision_evaluator_with_control_vertices( eval, 6, &control_vs6[0], &refined_vs6[0] ); 197 | // Verify the output: 198 | subdivision_evaluator_real_t total_diff = 0.; 199 | for( int i = 0; i < num_refined_vs; ++i ) 200 | for( int c = 0; c < 3; ++c ) 201 | { 202 | total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs6[ 6*i + 3 + c ] ); 203 | total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs[ 3*i + c ] ); 204 | } 205 | std::cout << "Total difference of 6-dimensional versus 3-dimensional (should be 0): " << total_diff << '\n'; 206 | } 207 | 208 | subdivision_evaluator_real_t* weights = new subdivision_evaluator_real_t[ num_refined_vs ]; 209 | std::fill( weights, weights + num_refined_vs, 1. ); 210 | void* engine = new_subdivision_skinning_engine( eval, 1, weights ); 211 | 212 | subdivision_evaluator_real_t* vs_modified = new subdivision_evaluator_real_t[ num_vs*3 ]; 213 | /// This produces identical output as input: 214 | // const subdivision_evaluator_real_t transforms[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; 215 | /// This produces output whose coordinates are scaled by 2: 216 | const subdivision_evaluator_real_t transforms[] = { 2,0,0,0, 0,2,0,0, 0,0,2,0, 0,0,0,2 }; 217 | compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( engine, (subdivision_evaluator_real_t*)transforms, vs_modified ); 218 | save_OBJ( "modified.obj", num_vs, vs_modified, num_faces, faces ); 219 | 220 | const int num_refined_vs2 = num_refined_vertices_of_subdivision_skinning_engine( engine ); 221 | std::cout << "num_refined_vs eval: " << num_refined_vs << '\n'; 222 | std::cout << "num_refined_vs engine: " << num_refined_vs2 << '\n'; 223 | 224 | get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 3, vs_modified, refined_vs ); 225 | save_OBJ( "modified-refined.obj", num_refined_vs, refined_vs, num_refined_faces, refined_faces ); 226 | 227 | // Test the non-3 dimensional path by duplicating submitting 6-dimensional 228 | // vertices xyzxyz: 229 | { 230 | std::vector< subdivision_evaluator_real_t > control_vs6( num_vs*6 ); 231 | for( int i = 0; i < num_vs; ++i ) 232 | for( int c = 0; c < 3; ++c ) 233 | { 234 | control_vs6[ 6*i + 3 + c ] = control_vs6[ 6*i + c ] = vs_modified[ 3*i + c ]; 235 | } 236 | std::vector< subdivision_evaluator_real_t > refined_vs6( num_refined_vs*6 ); 237 | get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 6, &control_vs6[0], &refined_vs6[0] ); 238 | // Verify the output: 239 | subdivision_evaluator_real_t total_diff = 0.; 240 | for( int i = 0; i < num_refined_vs; ++i ) 241 | for( int c = 0; c < 3; ++c ) 242 | { 243 | total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs6[ 6*i + 3 + c ] ); 244 | total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs[ 3*i + c ] ); 245 | } 246 | std::cout << "Total difference of 6-dimensional versus 3-dimensional (should be 0): " << total_diff << '\n'; 247 | } 248 | 249 | delete_subdivision_skinning_engine( engine ); 250 | delete_subdivision_evaluator( eval ); 251 | 252 | delete [] vs_modified; 253 | delete [] refined_faces; 254 | delete [] refined_vs; 255 | delete [] weights; 256 | 257 | if( argc == 2 ) 258 | { 259 | delete[] vs; 260 | delete[] faces; 261 | } 262 | 263 | return 0; 264 | } 265 | -------------------------------------------------------------------------------- /subdivgui/subdivision_skinning_wrapper_test.cpp: -------------------------------------------------------------------------------- 1 | // c++ -g subdivision_skinning_wrapper_test.cpp -o subdivision_skinning_wrapper_test ../../build/lib/libsubdivision_skinning.a ../../build/lib/libosdCPU.a 2 | // c++ -g subdivision_skinning_wrapper_test.cpp -o subdivision_skinning_wrapper_test ../../build/Debug/lib/libsubdivision_skinning.a ../../build/Debug/lib/libosdCPU.a 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include // atoi 9 | #include 10 | #include 11 | 12 | #include // std::fill 13 | 14 | extern "C" 15 | { 16 | #include "subdivision_skinning_wrapper.h" 17 | } 18 | 19 | namespace 20 | { 21 | 22 | subdivision_evaluator_real_t TORUS_VS[] = { 1.25052, 0.517982, 0.353553, 0.597239, 0.247384, 0.353553, 0.597239, 0.247384, -0.353553, 1.25052, 0.517982, -0.353553, 0.517982, 1.25052, 0.353553, 0.247384, 0.597239, 0.353553, 0.247384, 0.597239, -0.353553, 0.517982, 1.25052, -0.353553, -0.517982, 1.25052, 0.353553, -0.247384, 0.597239, 0.353553, -0.247384, 0.597239, -0.353553, -0.517982, 1.25052, -0.353553, -1.25052, 0.517982, 0.353553, -0.597239, 0.247384, 0.353553, -0.597239, 0.247384, -0.353553, -1.25052, 0.517982, -0.353553, -1.25052, -0.517982, 0.353553, -0.597239, -0.247384, 0.353553, -0.597239, -0.247384, -0.353553, -1.25052, -0.517982, -0.353553, -0.517982, -1.25052, 0.353553, -0.247384, -0.597239, 0.353553, -0.247384, -0.597239, -0.353553, -0.517982, -1.25052, -0.353553, 0.517982, -1.25052, 0.353553, 0.247384, -0.597239, 0.353553, 0.247384, -0.597239, -0.353553, 0.517982, -1.25052, -0.353553, 1.25052, -0.517982, 0.353553, 0.597239, -0.247384, 0.353553, 0.597239, -0.247384, -0.353553, 1.25052, -0.517982, -0.353553 }; 23 | // #define TORUS_FACES { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } 24 | int TORUS_FACES[] = { 4, 5, 1, 0, 5, 6, 2, 1, 6, 7, 3, 2, 7, 4, 0, 3, 8, 9, 5, 4, 9, 10, 6, 5, 10, 11, 7, 6, 11, 8, 4, 7, 12, 13, 9, 8, 13, 14, 10, 9, 14, 15, 11, 10, 15, 12, 8, 11, 16, 17, 13, 12, 17, 18, 14, 13, 18, 19, 15, 14, 19, 16, 12, 15, 20, 21, 17, 16, 21, 22, 18, 17, 22, 23, 19, 18, 23, 20, 16, 19, 24, 25, 21, 20, 25, 26, 22, 21, 26, 27, 23, 22, 27, 24, 20, 23, 28, 29, 25, 24, 29, 30, 26, 25, 30, 31, 27, 26, 31, 28, 24, 27, 0, 1, 29, 28, 1, 2, 30, 29, 2, 3, 31, 30, 3, 0, 28, 31 }; 25 | 26 | void print_OBJ( std::ostream& out, int num_vs, subdivision_evaluator_real_t* vs, int num_faces, int* quad_faces, const std::string& header_message = "" ) 27 | { 28 | // Save an optional header message. 29 | out << "# OBJ: " << header_message << '\n'; 30 | 31 | // Save vertices. 32 | for( int i = 0; i < num_vs; ++i ) 33 | { 34 | out << "v " << vs[3*i+0] << ' ' << vs[3*i+1] << ' ' << vs[3*i+2] << '\n'; 35 | } 36 | 37 | out << '\n'; 38 | 39 | for( int i = 0; i < num_faces; ++i ) 40 | { 41 | // Vertices are 1-indexed. 42 | out << "f " << (1+quad_faces[ 4*i + 0 ]) << ' ' << (1+quad_faces[ 4*i + 1 ]) << ' ' << (1+quad_faces[ 4*i + 2 ]); 43 | if( -1 != quad_faces[ 4*i + 3 ] ) out << ' ' << (1+quad_faces[ 4*i + 3 ]) << '\n'; 44 | else out << '\n'; 45 | } 46 | } 47 | void save_OBJ( const std::string& out_path, int num_vs, subdivision_evaluator_real_t* vs, int num_faces, int* quad_faces, const std::string& header_message = "" ) 48 | { 49 | std::ofstream out( out_path ); 50 | print_OBJ( out, num_vs, vs, num_faces, quad_faces, header_message ); 51 | std::cout << "Saved a mesh to: " << out_path << '\n'; 52 | } 53 | 54 | bool load_OBJ( std::istream& in, int* num_vs_out, subdivision_evaluator_real_t** vs_out, int* num_faces_out, int** quad_faces_out ) 55 | { 56 | std::string line; 57 | std::istringstream linestr; 58 | 59 | std::vector< subdivision_evaluator_real_t > vs; 60 | std::vector< int > faces; 61 | 62 | int line_number = 0; 63 | std::string type; 64 | while( !( in >> std::ws ).eof() ) 65 | { 66 | line_number += 1; 67 | 68 | std::getline( in, line ); 69 | if( line.empty() ) continue; 70 | 71 | linestr.clear(); 72 | linestr.str( line ); 73 | linestr >> type; 74 | 75 | if( type == std::string("v") ) 76 | { 77 | subdivision_evaluator_real_t x(-31337), y(-31337), z(-31337); 78 | linestr >> x >> y >> z; 79 | vs.push_back( x ); 80 | vs.push_back( y ); 81 | vs.push_back( z ); 82 | if( !linestr ) 83 | { 84 | std::cerr << "Invalid vertex encountered on line " << line_number << ".\n"; 85 | return false; 86 | } 87 | } 88 | else if( type == std::string("f") ) 89 | { 90 | int num_face_vertices = 0; 91 | while( !( linestr >> std::ws ).eof() ) 92 | { 93 | std::string vi; 94 | linestr >> vi; 95 | num_face_vertices += 1; 96 | 97 | // Now look for a slash, in case there's a bundle. 98 | int slash = vi.find( "/" ); 99 | if( std::string::npos == slash ) 100 | { 101 | faces.push_back( atoi( vi.c_str() ) ); 102 | } 103 | else 104 | { 105 | faces.push_back( atoi( vi.substr( 0, slash ).c_str() ) ); 106 | } 107 | // OBJ are 1-indexed, and a negative number indexes from the end. 108 | // Nothing should equal 0. 109 | if( faces.back() == 0 ) 110 | { 111 | std::cerr << "Invalid face vertex index of 0 encountered on line " << line_number << ".\n"; 112 | return false; 113 | } 114 | } 115 | 116 | if( !( num_face_vertices == 3 || num_face_vertices == 4 ) ) 117 | { 118 | std::cerr << "Invalid face without three or four vertices encountered on line " << line_number << ".\n"; 119 | return false; 120 | } 121 | // A triangle has a -1 as the fourth vertex index. 122 | // UPDATE: push_back a 0, because 123 | if( 3 == num_face_vertices ) faces.push_back( 0 ); 124 | } 125 | } 126 | 127 | assert( vs.size() % 3 == 0 ); 128 | *num_vs_out = vs.size()/3; 129 | *vs_out = new subdivision_evaluator_real_t[ vs.size() ]; 130 | std::copy( vs.begin(), vs.end(), *vs_out ); 131 | 132 | assert( faces.size() % 4 == 0 ); 133 | *num_faces_out = faces.size()/4; 134 | *quad_faces_out = new int[ faces.size() ]; 135 | for( int i = 0; i < faces.size(); ++i ) 136 | { 137 | (*quad_faces_out)[i] = 138 | faces[i] >= 0 139 | ? faces[i] - 1 140 | // Negative indices add to the back. 141 | : vs.size() + faces[i] 142 | ; 143 | } 144 | 145 | return true; 146 | } 147 | bool load_OBJ( const std::string& in_path, int* num_vs_out, subdivision_evaluator_real_t** vs_out, int* num_faces_out, int** quad_faces_out ) 148 | { 149 | std::ifstream in( in_path ); 150 | return load_OBJ( in, num_vs_out, vs_out, num_faces_out, quad_faces_out ); 151 | } 152 | 153 | } 154 | 155 | 156 | int main( int argc, const char* argv[] ) 157 | { 158 | subdivision_evaluator_real_t* vs = &TORUS_VS[0]; 159 | int* faces = &TORUS_FACES[0]; 160 | int num_vs = sizeof( TORUS_VS )/sizeof( subdivision_evaluator_real_t )/3; 161 | int num_faces = sizeof( TORUS_FACES )/sizeof( int )/4; 162 | 163 | if( argc == 2 ) 164 | { 165 | const bool success = load_OBJ( argv[1], &num_vs, &vs, &num_faces, &faces ); 166 | if( !success ) return -1; 167 | } 168 | 169 | std::cout << "Number of vertices: " << num_vs << '\n'; 170 | std::cout << "Number of faces: " << num_faces << '\n'; 171 | 172 | save_OBJ( "original.obj", num_vs, vs, num_faces, faces ); 173 | 174 | void* eval = new_subdivision_evaluator( num_vs, vs, num_faces, faces, 4 ); 175 | 176 | const int num_refined_faces = num_refined_quad_faces_of_subdivision_evaluator( eval ); 177 | int* refined_faces = new int[ num_refined_faces*4 ]; 178 | get_refined_quad_faces_of_subdivision_evaluator( eval, refined_faces ); 179 | 180 | const int num_refined_vs = num_refined_vertices_of_subdivision_evaluator( eval ); 181 | subdivision_evaluator_real_t* refined_vs = new subdivision_evaluator_real_t[ num_refined_vs*3 ]; 182 | get_refined_vertices_of_subdivision_evaluator( eval, refined_vs ); 183 | 184 | save_OBJ( "original-refined.obj", num_refined_vs, refined_vs, num_refined_faces, refined_faces ); 185 | 186 | // Test the non-3 dimensional path by duplicating submitting 6-dimensional 187 | // vertices xyzxyz: 188 | { 189 | std::vector< subdivision_evaluator_real_t > control_vs6( num_vs*6 ); 190 | for( int i = 0; i < num_vs; ++i ) 191 | for( int c = 0; c < 3; ++c ) 192 | { 193 | control_vs6[ 6*i + 3 + c ] = control_vs6[ 6*i + c ] = vs[ 3*i + c ]; 194 | } 195 | std::vector< subdivision_evaluator_real_t > refined_vs6( num_refined_vs*6 ); 196 | get_refined_vertices_of_subdivision_evaluator_with_control_vertices( eval, 6, &control_vs6[0], &refined_vs6[0] ); 197 | // Verify the output: 198 | subdivision_evaluator_real_t total_diff = 0.; 199 | for( int i = 0; i < num_refined_vs; ++i ) 200 | for( int c = 0; c < 3; ++c ) 201 | { 202 | total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs6[ 6*i + 3 + c ] ); 203 | total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs[ 3*i + c ] ); 204 | } 205 | std::cout << "Total difference of 6-dimensional versus 3-dimensional (should be 0): " << total_diff << '\n'; 206 | } 207 | 208 | subdivision_evaluator_real_t* weights = new subdivision_evaluator_real_t[ num_refined_vs ]; 209 | std::fill( weights, weights + num_refined_vs, 1. ); 210 | void* engine = new_subdivision_skinning_engine( eval, 1, weights ); 211 | 212 | subdivision_evaluator_real_t* vs_modified = new subdivision_evaluator_real_t[ num_vs*3 ]; 213 | /// This produces identical output as input: 214 | // const subdivision_evaluator_real_t transforms[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; 215 | /// This produces output whose coordinates are scaled by 2: 216 | const subdivision_evaluator_real_t transforms[] = { 2,0,0,0, 0,2,0,0, 0,0,2,0, 0,0,0,2 }; 217 | compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( engine, (subdivision_evaluator_real_t*)transforms, vs_modified ); 218 | save_OBJ( "modified.obj", num_vs, vs_modified, num_faces, faces ); 219 | 220 | const int num_refined_vs2 = num_refined_vertices_of_subdivision_skinning_engine( engine ); 221 | std::cout << "num_refined_vs eval: " << num_refined_vs << '\n'; 222 | std::cout << "num_refined_vs engine: " << num_refined_vs2 << '\n'; 223 | 224 | get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 3, vs_modified, refined_vs ); 225 | save_OBJ( "modified-refined.obj", num_refined_vs, refined_vs, num_refined_faces, refined_faces ); 226 | 227 | // Test the non-3 dimensional path by duplicating submitting 6-dimensional 228 | // vertices xyzxyz: 229 | { 230 | std::vector< subdivision_evaluator_real_t > control_vs6( num_vs*6 ); 231 | for( int i = 0; i < num_vs; ++i ) 232 | for( int c = 0; c < 3; ++c ) 233 | { 234 | control_vs6[ 6*i + 3 + c ] = control_vs6[ 6*i + c ] = vs_modified[ 3*i + c ]; 235 | } 236 | std::vector< subdivision_evaluator_real_t > refined_vs6( num_refined_vs*6 ); 237 | get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 6, &control_vs6[0], &refined_vs6[0] ); 238 | // Verify the output: 239 | subdivision_evaluator_real_t total_diff = 0.; 240 | for( int i = 0; i < num_refined_vs; ++i ) 241 | for( int c = 0; c < 3; ++c ) 242 | { 243 | total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs6[ 6*i + 3 + c ] ); 244 | total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs[ 3*i + c ] ); 245 | } 246 | std::cout << "Total difference of 6-dimensional versus 3-dimensional (should be 0): " << total_diff << '\n'; 247 | } 248 | 249 | delete_subdivision_skinning_engine( engine ); 250 | delete_subdivision_evaluator( eval ); 251 | 252 | delete [] vs_modified; 253 | delete [] refined_faces; 254 | delete [] refined_vs; 255 | delete [] weights; 256 | 257 | if( argc == 2 ) 258 | { 259 | delete[] vs; 260 | delete[] faces; 261 | } 262 | 263 | return 0; 264 | } 265 | -------------------------------------------------------------------------------- /lib/subdivision_engine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | clang++ -std=c++11 -stdlib=libc++ -g -I../../opensubdiv -I../../regression subdivision_matrices_eigen.cpp subdivision_matrices.cpp subdivision_engine.cpp -o subdivision -DSUBDIVISION_ENGINE_MAIN -I/opt/local/include/eigen3 3 | */ 4 | 5 | #include "subdivision_engine.h" 6 | #include 7 | #include 8 | #include 9 | 10 | const float EPS = 1e-7; 11 | const int resolution = 10; 12 | #define CUBE_VS 0.000000, -1.414214, 1.000000, 1.414214, 0.000000, 1.000000, -1.414214, 0.000000, 1.000000, 0.000000, 1.414214, 1.000000, -1.414214, 0.000000, -1.000000, 0.000000, 1.414214, -1.000000, 0.000000, -1.414214, -1.000000, 1.414214, 0.000000, -1.000000 13 | #define CUBE_FACES { {0, 1, 3, 2}, {2, 3, 5, 4}, {4, 5, 7, 6}, {6, 7, 1, 0}, {1, 7, 5, 3}, {6, 0, 2, 4} } 14 | 15 | #define TORUS_VS 1.25052, 0.517982, 0.353553, 0.597239, 0.247384, 0.353553, 0.597239, 0.247384, -0.353553, 1.25052, 0.517982, -0.353553, 0.517982, 1.25052, 0.353553, 0.247384, 0.597239, 0.353553, 0.247384, 0.597239, -0.353553, 0.517982, 1.25052, -0.353553, -0.517982, 1.25052, 0.353553, -0.247384, 0.597239, 0.353553, -0.247384, 0.597239, -0.353553, -0.517982, 1.25052, -0.353553, -1.25052, 0.517982, 0.353553, -0.597239, 0.247384, 0.353553, -0.597239, 0.247384, -0.353553, -1.25052, 0.517982, -0.353553, -1.25052, -0.517982, 0.353553, -0.597239, -0.247384, 0.353553, -0.597239, -0.247384, -0.353553, -1.25052, -0.517982, -0.353553, -0.517982, -1.25052, 0.353553, -0.247384, -0.597239, 0.353553, -0.247384, -0.597239, -0.353553, -0.517982, -1.25052, -0.353553, 0.517982, -1.25052, 0.353553, 0.247384, -0.597239, 0.353553, 0.247384, -0.597239, -0.353553, 0.517982, -1.25052, -0.353553, 1.25052, -0.517982, 0.353553, 0.597239, -0.247384, 0.353553, 0.597239, -0.247384, -0.353553, 1.25052, -0.517982, -0.353553 16 | #define TORUS_FACES { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } 17 | 18 | namespace subdivision_matrix 19 | { 20 | 21 | MatrixXX_t shepard( const MatrixX3_t& positions, const MatrixX3_t& handle_positions ) 22 | { 23 | int row_num = positions.rows(); 24 | int col_num = handle_positions.rows(); 25 | 26 | MatrixXX_t weights( row_num, col_num ); 27 | 28 | for ( int i = 0; i < row_num; i++ ) { 29 | int flag = -1; 30 | for ( int j = 0; j < col_num; j++ ) { 31 | vertex_t diff = positions.row( i ) - handle_positions.row( j ); 32 | weights( i, j ) = diff.dot( diff ); 33 | // if a position is on the handle, mark the handle's index 34 | if ( weights( i, j ) <= EPS ) 35 | flag = j; 36 | } 37 | // if a position is on any handle, make its weight 1.0. 38 | if ( flag >= 0 ) { 39 | for ( int j = 0; j < col_num; j++ ) { 40 | weights( i, j ) = ( flag == j ? 1.0 : 0.0 ); 41 | } 42 | continue; 43 | } 44 | 45 | for ( int j = 0; j < col_num; j++ ) { 46 | weights( i, j ) = 1.0 / weights( i, j ); 47 | } 48 | weights.row( i ) /= weights.row( i ).sum(); 49 | 50 | } 51 | 52 | return weights; 53 | } 54 | 55 | //-------------------------------------- PREPARE METHODS -------------------------------------- 56 | void prepare( const subdivision_control_mesh& mesh, const MatrixX3_t& handle_positions, const char* weight_function, 57 | std::vector< MatrixX4_t > & W_Array ) 58 | { 59 | /// 1 Get the sparse subdivision coefficients at many places around the mesh. 60 | /// 2 Compute the weights and areas for every point around the mesh. 61 | 62 | /// 1 63 | std::vector< real_t > us, vs; 64 | createUVs( resolution, resolution, us, vs ); 65 | 66 | SparseMatrix_t M_matrices, Du_matrices, Dv_matrices; 67 | compute_subdivision_coefficients_for_mesh( 68 | mesh.vs.rows(), 69 | mesh.faces, 70 | us, vs, 71 | M_matrices, &Du_matrices, &Dv_matrices ); 72 | 73 | 74 | std::cout << M_matrices.rows() << ' ' << M_matrices.cols() << std::endl; 75 | 76 | prepare( mesh.vs, M_matrices, Du_matrices, Dv_matrices, handle_positions, weight_function, W_Array ); 77 | } 78 | 79 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, 80 | const MatrixXX_t& weights, std::vector< MatrixX4_t > & W_Array ) 81 | { 82 | /// areas is a column vector whose number of rows equals to the number of all positions. 83 | DiagonalMatrix_t areas( M_matrices.rows() ); 84 | // std::cout << "areas size: " << M_matrices.rows() << '\n'; 85 | areas.setIdentity(); 86 | prepare( vs, M_matrices, areas, weights, W_Array ); 87 | } 88 | 89 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const DiagonalMatrix_t& areas, 90 | const MatrixXX_t& weights, std::vector< MatrixX4_t > & W_Array ) 91 | { 92 | /// Compute the weights and areas for every point around the mesh. 93 | /// areas is a column vector whose number of rows equals to the number of all positions. 94 | 95 | /// build system. 96 | const MatrixXX_t system = ( M_matrices.transpose() * areas * M_matrices ).eval(); 97 | 98 | /// set up LLT solver and examine if the system can be inverted. 99 | Eigen::LLT< MatrixXX_t > llt; 100 | llt.compute( system ); 101 | if ( Eigen::Success == llt.info () ) 102 | ; //std::cout << "succeed." << std::endl; 103 | else if ( Eigen::NumericalIssue == llt.info () ) 104 | std::cout << "numerical issue." << std::endl; 105 | 106 | 107 | /// set up an identity matrix for compute the inverse of system. 108 | MatrixX4_t controls( vs.rows(), 4 ); 109 | controls.leftCols( 3 ) = vs; 110 | controls.col( 3 ).setOnes(); 111 | 112 | // std::cout << "M_matrices rows: " << M_matrices.rows() << '\n'; 113 | // std::cout << "M_matrices cols: " << M_matrices.cols() << '\n'; 114 | // std::cout << "weights size: " << weights.col(0).asDiagonal().rows() << '\n'; 115 | 116 | for( int k = 0; k < weights.cols(); k++ ) { 117 | const MatrixX4_t extra = ( controls.transpose() * M_matrices.transpose() * areas * weights.col(k).asDiagonal() * M_matrices ).transpose(); 118 | W_Array.push_back( llt.solve( extra ) ); 119 | } 120 | 121 | return; 122 | } 123 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const DiagonalMatrix_t& areas, 124 | const MatrixX3_t handle_positions, const char* weight_function, 125 | std::vector< MatrixX4_t > & W_Array ) 126 | { 127 | /// Compute the weights and areas for every point around the mesh. 128 | /// areas is a column vector whose number of rows equals to the number of all positions. 129 | 130 | MatrixX3_t positions( M_matrices.rows(), 3 ); 131 | positions = M_matrices * vs; 132 | 133 | /// weights are a dense matrix whose number of rows equales to the number of all positions, 134 | /// and the number of columns equals to the number of handles. 135 | MatrixXX_t weights; 136 | if( strcmp( weight_function, "harmonic" ) == 0) { 137 | // weights = harmonic( mesh, positions, handle_positions ); 138 | 139 | } else if ( strcmp( weight_function, "shepard" ) == 0 ) { 140 | weights = shepard( positions, handle_positions ); 141 | } 142 | 143 | prepare( vs, M_matrices, areas, weights, W_Array ); 144 | } 145 | 146 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const SparseMatrix_t& Du_matrices, const SparseMatrix_t& Dv_matrices, 147 | const MatrixX3_t& handle_positions, const char* weight_function, 148 | std::vector< MatrixX4_t > & W_Array ) 149 | { 150 | /// areas is a column vector whose number of rows equals to the number of all positions. 151 | DiagonalMatrix_t areas( M_matrices.rows() ); 152 | MatrixX3_t du_list = Du_matrices * vs; 153 | MatrixX3_t dv_list = Dv_matrices * vs; 154 | Eigen::Matrix< real_t, 1, 3 > vec1, vec2; 155 | for( int i = 0; i < M_matrices.rows(); ++i ) 156 | { 157 | vec1 = du_list.row( i ); 158 | vec2 = dv_list.row( i ); 159 | // Divide by the number of rows, which is like multiplying by dt. 160 | areas.diagonal()[i] = ( vec1.cross( vec2 ).norm() ) / M_matrices.rows(); 161 | } 162 | 163 | prepare( vs, M_matrices, areas, handle_positions, weight_function, W_Array ); 164 | } 165 | void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, 166 | const MatrixX3_t& handle_positions, const char* weight_function, 167 | std::vector< MatrixX4_t > & W_Array ) 168 | { 169 | /// areas is a column vector whose number of rows equals to the number of all positions. 170 | DiagonalMatrix_t areas( M_matrices.rows() ); 171 | areas.setIdentity(); 172 | prepare( vs, M_matrices, areas, handle_positions, weight_function, W_Array ); 173 | } 174 | 175 | void naive_prepare( const MatrixX3_t& vs, const MatrixX3_t& handle_positions, 176 | std::vector< MatrixX4_t > & W_Array ) 177 | { 178 | MatrixXX_t weights = shepard( vs, handle_positions ); 179 | 180 | // build the homogeneous coordinates for control points. 181 | MatrixX4_t controls( vs.rows(), 4 ); 182 | controls.leftCols( 3 ) = vs; 183 | controls.col( 3 ).setOnes(); 184 | for( int k = 0; k < weights.cols(); k++ ) { 185 | MatrixXX_t wi( weights.rows(), 4 ); 186 | for ( int i = 0; i < 4; i++ ) 187 | wi.col(i) = weights.col(k); 188 | 189 | W_Array.push_back( controls.cwiseProduct( wi ) ); 190 | } 191 | // std::cout << "naive prepare finished\n"; 192 | } 193 | 194 | //-------------------------------------- PREPARE METHODS END-------------------------------------- 195 | 196 | void solve( const std::vector< MatrixX4_t >& W_Array, 197 | const std::vector< transform_t >& transforms, 198 | MatrixX3_t & result ) 199 | { 200 | 201 | assert( W_Array.size() == transforms.size() ); 202 | 203 | MatrixXX_t rhs( W_Array[0] ); 204 | rhs.setZero(); 205 | 206 | for( int i = 0; i < transforms.size(); i++ ) 207 | { 208 | rhs += ( W_Array[i] * transforms[i].transpose() ); 209 | } 210 | 211 | result = rhs.leftCols( 3 ); 212 | 213 | return; 214 | } 215 | 216 | 217 | void compute_target_surface( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, 218 | const MatrixX3_t& handle_positions, const std::vector< transform_t >& transforms, 219 | MatrixX3_t& result ) 220 | { 221 | MatrixX3_t positions( M_matrices.rows(), 3 ); 222 | positions = M_matrices * vs; 223 | 224 | std::vector< MatrixX4_t > W_Array; 225 | naive_prepare( positions, handle_positions, W_Array ); 226 | 227 | solve( W_Array, transforms, result ); 228 | 229 | // std::cout << "target surface computed.\n"; 230 | } 231 | 232 | VectorX_t piecewise_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ) 233 | { 234 | MatrixX3_t diff_mat = deformed_surface - target_surface; 235 | VectorX_t result = diff_mat.rowwise().norm(); 236 | 237 | return result; 238 | } 239 | 240 | real_t norm_of_piecewise_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ) 241 | { 242 | MatrixX3_t diff_mat = deformed_surface - target_surface; 243 | return diff_mat.norm(); 244 | } 245 | 246 | 247 | real_t hausdorff_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ) 248 | { 249 | assert( target_surface.rows() == deformed_surface.rows() ); 250 | int num = target_surface.rows(); 251 | 252 | VectorX_t min_diffs( num, 1 ); 253 | for( int i = 0; i < num; i++ ) { 254 | VectorX_t diffs = ( deformed_surface.rowwise() - target_surface.row(i) ).rowwise().norm(); 255 | min_diffs(i) = diffs.minCoeff(); 256 | } 257 | 258 | return min_diffs.maxCoeff(); 259 | } 260 | } 261 | 262 | #ifdef SUBDIVISION_ENGINE_MAIN 263 | void usage( const char* argv0 ) 264 | { 265 | std::cerr << "Usage: " << argv0 << " test_subdivision_engine\n"; 266 | exit( -1 ); 267 | } 268 | 269 | void print_mesh( const subdivision_matrix::subdivision_control_mesh& mesh ); 270 | 271 | void test( const char *type ) 272 | { 273 | subdivision_matrix::MatrixX3_t handle_positions; 274 | handle_positions.resize(2,3); 275 | handle_positions << 0.8, 0.8, 0.7, 276 | 0.2, -0.3, -0.2; 277 | 278 | // make a mesh 279 | subdivision_matrix::subdivision_control_mesh mesh; 280 | 281 | // use the cube! has problem! 282 | // mesh.vs.resize( 8, 3 ); 283 | // mesh.vs << CUBE_VS; 284 | // mesh.faces = CUBE_FACES; 285 | 286 | mesh.vs.resize( 32, 3 ); 287 | mesh.vs << TORUS_VS; 288 | mesh.faces = TORUS_FACES; 289 | 290 | 291 | std::vector< subdivision_matrix::MatrixX4_t > W_Array; 292 | if ( strcmp( type, "naive") == 0 ) 293 | subdivision_matrix::naive_prepare( mesh.vs, handle_positions, W_Array ); 294 | else 295 | subdivision_matrix::prepare( mesh, handle_positions, "shepard", W_Array ); 296 | 297 | for (auto W_i : W_Array ) 298 | std::cout << W_i << std::endl; 299 | 300 | std::vector< subdivision_matrix::transform_t > transforms; 301 | subdivision_matrix::transform_t t1, t2; 302 | t1.setIdentity(); 303 | t2.setIdentity(); 304 | transforms.push_back( t1 ); 305 | transforms.push_back( t2 ); 306 | 307 | subdivision_matrix::MatrixX3_t new_controls; 308 | subdivision_matrix::solve( W_Array, transforms, new_controls ); 309 | 310 | std::cout << "result\n" << new_controls << std::endl; 311 | } 312 | 313 | int main( int argc, char* argv[] ) 314 | { 315 | using std::string; 316 | 317 | // if( argc != 1 ) usage( argv[0] ); 318 | 319 | if( argc == 1 ) 320 | test( "ours" ); 321 | else 322 | test( argv[1]); 323 | 324 | return 0; 325 | } 326 | #endif 327 | void print_mesh( const subdivision_matrix::subdivision_control_mesh& mesh ) 328 | { 329 | std::cout << "vertices\n"; 330 | std::cout << mesh.vs << std::endl; 331 | std::cout << "faces\n"; 332 | int face_offset = 0; 333 | for( const auto& num_face_vertices : mesh.faces.first ) 334 | { 335 | for( int fvi = 0; fvi < num_face_vertices; ++fvi ) 336 | { 337 | std::cout << mesh.faces.second.at( face_offset + fvi ) << ' '; 338 | } 339 | face_offset += num_face_vertices; 340 | std::cout << '\n'; 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /lib/subdivision_matrices_osd.cpp: -------------------------------------------------------------------------------- 1 | // c++ -g -std=c++11 -I../../opensubdiv -I../../regression subdivision_matrices_osd.cpp -o subdivision_matrices_osd -DSUBDIVISION_MATRICES_OSD_MAIN ../../build/lib/libosdCPU.a 2 | 3 | #include "subdivision_matrices_osd.h" 4 | 5 | #include 6 | #include 7 | #include // unique_ptr 8 | #include // fill 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace 16 | { 17 | 18 | /* 19 | Given the number of vertices in the mesh 'num_vertices' 20 | and a vector of faces 'faces', each element of which is a vector of vertex indices, 21 | returns a new mesh object suitable for passing to 22 | Free the resulting mesh with "delete" when finished with it. 23 | */ 24 | OpenSubdiv::HbrMesh* newMeshFromNumVerticesAndFaces( int num_vertices, const subdivision_matrix::faces_t& faces ) 25 | { 26 | assert( num_vertices > 0 ); 27 | 28 | // Following: http://graphics.pixar.com/opensubdiv/forum.html?place=msg%2Fopensubdiv%2FzKq9vG_azHQ%2FDb2K7L23BykJ 29 | 30 | static OpenSubdiv::HbrCatmarkSubdivision CATMULL_CLARK; 31 | 32 | auto mesh = new OpenSubdiv::HbrMesh( &CATMULL_CLARK ); 33 | 34 | OpenSubdiv::OsdVertex default_vertex_value; 35 | for( int vi = 0; vi < num_vertices; ++vi ) mesh->NewVertex( vi, default_vertex_value ); 36 | 37 | /// TODO Q: Is this necessary? 38 | // int ptex_index = 0; 39 | int face_offset = 0; 40 | for( const auto& num_face_vertices : faces.first ) 41 | { 42 | OpenSubdiv::HbrFace* face = mesh->NewFace( num_face_vertices, &faces.second[face_offset], 0 ); 43 | 44 | /* 45 | face->SetPtexIndex( ptex_index ); 46 | if( f.size() != 4 ) { 47 | ptex_index += f.size(); 48 | } else { 49 | ptex_index += 1; 50 | } 51 | */ 52 | 53 | face_offset += num_face_vertices; 54 | } 55 | 56 | mesh->SetInterpolateBoundaryMethod( OpenSubdiv::HbrMesh::k_InterpolateBoundaryAlwaysSharp ); 57 | 58 | mesh->Finish(); 59 | 60 | return mesh; 61 | } 62 | 63 | } 64 | 65 | namespace subdivision_matrix 66 | { 67 | 68 | /* 69 | Given the number of vertices in the mesh 'num_vertices' 70 | and a vector of faces 'faces', each element of which is a vector of vertex indices, and 71 | a positive integer 'level' indicating the level of refinement, 72 | fills 'positions_out' with sparse vectors such that the position 73 | for the i-th uv value in the original call to precomputeStencils() can be obtained by: 74 | \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second 75 | 76 | The optional parameter 'quad_faces_out', if specified, is a sequence of quad faces 77 | obtained by subdivision, where each face is four indices into 'positions_out'. 78 | */ 79 | void compute_subdivision_coefficients_for_mesh( 80 | int num_vertices, 81 | const faces_t& faces, 82 | const int level, 83 | std::vector< sparse_vector_t >& positions_out, 84 | std::vector< index_t >* quad_faces_out 85 | ) 86 | { 87 | auto mesh = newMeshFromNumVerticesAndFaces( num_vertices, faces ); 88 | compute_subdivision_coefficients_for_mesh( num_vertices, mesh, level, positions_out, quad_faces_out ); 89 | delete mesh; 90 | } 91 | 92 | void compute_subdivision_coefficients_for_mesh( 93 | int num_coarse_vertices, 94 | OpenSubdiv::HbrMesh* hmesh, 95 | const int level, 96 | std::vector< sparse_vector_t >& positions_out, 97 | std::vector< index_t >* quad_faces_out 98 | ) 99 | { 100 | assert( num_coarse_vertices == hmesh->GetNumVertices() ); 101 | 102 | assert( level > 0 ); 103 | positions_out.clear(); 104 | if( quad_faces_out ) quad_faces_out->clear(); 105 | 106 | std::unique_ptr< OpenSubdiv::FarMesh > fmesh; 107 | // Adaptive would give us limit vertex positions, however: 108 | // Adaptive is only supported for Catmull-Clark subdivision. 109 | // Adaptive doesn't support getting quads. 110 | // So, we use non-adaptive aka uniform subdivision. 111 | { 112 | // The comment in "osdutil/refiner.cpp" says: 113 | // XXX:gelder 114 | // Had problems with patchArrayVector in patchTables not working 115 | // unless firstLevel is passed as 1 here. Bug in refiner? 116 | OpenSubdiv::FarMeshFactory factory( hmesh, level, false /* adaptive */, 1 /* firstLevel */ ); 117 | fmesh.reset( factory.Create() ); 118 | } 119 | 120 | 121 | // Following "osdutil/refiner.cpp" 122 | 123 | /// Refiner::Initialize() 124 | // Subdivision tables describe the addition steps with coefficients 125 | // needed to perform subdivision 126 | const OpenSubdiv::FarSubdivisionTables* ftable = fmesh->GetSubdivisionTables(); 127 | 128 | 129 | const int kNumDimensions = std::min( 100, num_coarse_vertices ); 130 | { 131 | /// UniformEvaluator::Initialize() 132 | std::unique_ptr< OpenSubdiv::OsdCpuComputeContext > _computeContext( OpenSubdiv::OsdCpuComputeContext::Create(fmesh->GetSubdivisionTables(), fmesh->GetVertexEditTables()) ); 133 | // The vertex buffer must have the FarMesh's vertex count, which includes 134 | // the refined vertices, not the HbrMesh's vertex count, which only has 135 | // the coarse vertices. 136 | std::unique_ptr< OpenSubdiv::OsdCpuVertexBuffer > _vertexBuffer( OpenSubdiv::OsdCpuVertexBuffer::Create( kNumDimensions, fmesh->GetNumVertices() ) ); 137 | 138 | for( int coarse_index = 0; coarse_index < num_coarse_vertices; coarse_index += kNumDimensions ) 139 | { 140 | /// UniformEvaluator::SetCoarsePositions() 141 | // Each vertex stores its indicator function. 142 | // The identity matrix is the indicator function. 143 | // We are iterating vertex-by-vertex, so set the coarse_index column of the 144 | // identity matrix. 145 | { 146 | // vbuffer: v0d0 v0d1 v0d2 ... v0dN v1d0 v1d1 v1d2 ... v1dN v2d0 v2d1 ... 147 | float* vbuffer = _vertexBuffer->BindCpuBuffer(); 148 | // TODO Q: Do I need to fill it with zeros, or can I just 149 | // unset the last iteration's 1? 150 | // In other words, does Refine() overwrite these values? 151 | std::fill( vbuffer, vbuffer + kNumDimensions * num_coarse_vertices, 0. ); 152 | // A slice of 'kNumDimensions' columns of the identity matrix: 153 | for( int i = coarse_index; i < std::min( coarse_index + kNumDimensions, num_coarse_vertices ); ++i ) 154 | { 155 | vbuffer[ i-coarse_index + i*kNumDimensions ] = 1.; 156 | } 157 | } 158 | 159 | /// UniformEvaluator::Refine() 160 | { 161 | OpenSubdiv::OsdCpuComputeController cpuComputeController; 162 | cpuComputeController.Refine( 163 | _computeContext.get(), 164 | fmesh->GetKernelBatches(), 165 | _vertexBuffer.get() 166 | ); 167 | // TODO Q: Is this Synchronize() call necessary? 168 | cpuComputeController.Synchronize(); 169 | } 170 | 171 | 172 | /// UniformEvaluator::GetRefinedPositions() 173 | { 174 | const int numRefinedVerts = ftable->GetNumVertices(level); 175 | const int firstVertexOffset = ftable->GetFirstVertexOffset(level); 176 | const float* vbuffer = _vertexBuffer->BindCpuBuffer(); 177 | 178 | if (numRefinedVerts == 0) { 179 | std::cerr << "ERROR: No refinement occurred.\n"; 180 | return; 181 | } 182 | 183 | if( 0 == coarse_index ) 184 | { 185 | // We clear it upon entry to this function. 186 | assert( positions_out.empty() ); 187 | positions_out.resize( numRefinedVerts ); 188 | for( int vi = 0; vi < numRefinedVerts; ++vi ) positions_out.at( vi ).reserve( 16 ); 189 | } 190 | else 191 | { 192 | assert( positions_out.size() == numRefinedVerts ); 193 | } 194 | 195 | const float* off = vbuffer + firstVertexOffset*kNumDimensions; 196 | const float kEps = 1e-10; 197 | for( int vi = 0; vi < numRefinedVerts; ++vi ) 198 | { 199 | for( int i = coarse_index; i < std::min( coarse_index + kNumDimensions, num_coarse_vertices ); ++i ) 200 | { 201 | const auto& val = off[ i - coarse_index ]; 202 | if( fabs( val ) > kEps ) 203 | { 204 | positions_out.at( vi ).push_back( std::make_pair( i, val ) ); 205 | } 206 | } 207 | off += kNumDimensions; 208 | } 209 | } 210 | } 211 | } 212 | 213 | // Faces 214 | if( quad_faces_out ) 215 | { 216 | // We clear it upon entry to this function. 217 | assert( quad_faces_out->empty() ); 218 | 219 | // From "osdutil/refiner.cpp"'s Refiner::Initialize(): 220 | // Find quads array at given level 221 | const OpenSubdiv::FarPatchTables* ptables = fmesh->GetPatchTables(); 222 | const OpenSubdiv::FarPatchTables::PatchArrayVector& parrays = ptables->GetPatchArrayVector(); 223 | // parrays doesn't contain base mesh, so it starts with level==1 224 | assert( level-1 < parrays.size() ); 225 | const OpenSubdiv::FarPatchTables::PatchArray& parray = parrays[level-1]; 226 | 227 | // Global index of the first point in this array 228 | const int firstVertexOffset = ftable->GetFirstVertexOffset(level); 229 | 230 | 231 | /// From "osdutil/refiner.cpp"'s Refiner::GetRefinedQuads(): 232 | const int numUniformQuads = (int) parray.GetNumPatches(); 233 | quad_faces_out->resize(numUniformQuads * 4); 234 | 235 | const unsigned int *quadIndices = ptables->GetFaceVertices( level ); 236 | for( int i = 0; i < numUniformQuads*4; ++i ) 237 | { 238 | quad_faces_out->at(i) = quadIndices[i] - firstVertexOffset; 239 | } 240 | } 241 | } 242 | 243 | } // ~namespace subdivision_matrix() 244 | 245 | #ifdef SUBDIVISION_MATRICES_OSD_MAIN 246 | void print_sparse_vectors( const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::string& label ); 247 | void print_faces( const std::vector< subdivision_matrix::index_t >& faces, const std::string& label ); 248 | void print_OBJ( const std::vector& original_positions, const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::vector< subdivision_matrix::index_t >& faces, const std::string& label ); 249 | 250 | #define TORUS 32, { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } 251 | #define CUBE 8, { {0, 1, 3, 2}, {2, 3, 5, 4}, {4, 5, 7, 6}, {6, 7, 1, 0}, {1, 7, 5, 3}, {6, 0, 2, 4} } 252 | 253 | void test_compute_coefficients( int level, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) 254 | { 255 | using namespace subdivision_matrix; 256 | 257 | // std::vector< sparse_vector_t > position_coeffs; 258 | std::vector< index_t > faces; 259 | compute_subdivision_coefficients_for_mesh( 260 | // Cube 261 | // CUBE, 262 | // Torus 263 | TORUS, 264 | level, 265 | position_coeffs, &faces 266 | ); 267 | 268 | print_sparse_vectors( position_coeffs, "position_coeffs: " ); 269 | print_faces( faces, "faces: " ); 270 | } 271 | 272 | // -I/path/to/regression 273 | #include 274 | #include 275 | #include 276 | void test_shape_utils( int level, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) 277 | { 278 | using namespace subdivision_matrix; 279 | 280 | // Create a torus. 281 | std::vector orgPositions; 282 | auto mesh = simpleHbr( catmark_torus.c_str(), kCatmark, orgPositions, true); 283 | // auto mesh = simpleHbr( catmark_cube.c_str(), kCatmark, orgPositions, true); 284 | 285 | // std::vector< sparse_vector_t > position_coeffs; 286 | std::vector< index_t > faces; 287 | compute_subdivision_coefficients_for_mesh( 288 | mesh->GetNumVertices(), 289 | mesh, 290 | level, 291 | position_coeffs, &faces 292 | ); 293 | 294 | print_sparse_vectors( position_coeffs, "position_coeffs: " ); 295 | print_faces( faces, "faces: " ); 296 | print_OBJ( orgPositions, position_coeffs, faces, "reconstructed OBJ" ); 297 | } 298 | 299 | #include 300 | #include 301 | void usage( const char* argv0 ) 302 | { 303 | std::cerr << "Usage: " << argv0 << " test_shape_utils|test_compute_coefficients level\n"; 304 | exit( -1 ); 305 | } 306 | int main( int argc, char* argv[] ) 307 | { 308 | using std::string; 309 | 310 | if( argc != 3 ) usage( argv[0] ); 311 | 312 | const int level = atoi( argv[2] ); 313 | if( level <= 0 ) usage( argv[0] ); 314 | 315 | const string test_name = string(argv[1]); 316 | 317 | std::vector< subdivision_matrix::sparse_vector_t > position_coeffs; 318 | if( string("test_shape_utils") == test_name ) 319 | { 320 | test_shape_utils( level, position_coeffs ); 321 | } 322 | else if( string("test_compute_coefficients") == test_name ) 323 | { 324 | test_compute_coefficients( level, position_coeffs ); 325 | } 326 | else 327 | { 328 | usage( argv[0] ); 329 | } 330 | 331 | // TODO: Do something with position_coeffs 332 | 333 | return 0; 334 | } 335 | 336 | void print_sparse_vectors( const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::string& label = "" ) 337 | { 338 | std::cout << label << "{"; 339 | for( const auto vec : sparse_vectors ) 340 | { 341 | std::cout << "\n { "; 342 | for( const auto p : vec ) 343 | { 344 | std::cout << "{ " << p.first << ", " << p.second << " }, "; 345 | } 346 | std::cout << "},"; 347 | } 348 | std::cout << "}\n"; 349 | } 350 | void print_faces( const std::vector< subdivision_matrix::index_t >& faces, const std::string& label ) 351 | { 352 | std::cout << label << "{\n"; 353 | for( int fi = 0; fi < faces.size(); ++fi ) 354 | { 355 | if( fi > 0 && fi % 4 == 0 ) std::cout << " },\n"; 356 | if( fi % 4 == 0 ) std::cout << " {"; 357 | std::cout << ' ' << faces.at(fi); 358 | } 359 | std::cout << "}\n"; 360 | } 361 | #include 362 | void print_OBJ( const std::vector& original_positions, const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::vector< subdivision_matrix::index_t >& faces, const std::string& label ) 363 | { 364 | std::cout << "# OBJ: " << label << '\n'; 365 | 366 | assert( original_positions.size() % 3 == 0 ); 367 | std::vector< std::valarray< float > > ovs( original_positions.size() / 3, { 0., 0., 0. } ); 368 | for( int i = 0; i < ovs.size(); ++i ) 369 | { 370 | assert( ovs.at( i ).size() == 3 ); 371 | ovs.at( i )[ 0 ] = original_positions.at( 3*i + 0 ); 372 | ovs.at( i )[ 1 ] = original_positions.at( 3*i + 1 ); 373 | ovs.at( i )[ 2 ] = original_positions.at( 3*i + 2 ); 374 | } 375 | 376 | for( int i = 0; i < sparse_vectors.size(); ++i ) 377 | { 378 | std::valarray< float > pos( { 0., 0., 0. } ); 379 | for( const auto& i_w : sparse_vectors.at( i ) ) 380 | { 381 | pos += i_w.second * ovs.at( i_w.first ); 382 | } 383 | std::cout << "v " << pos[0] << ' ' << pos[1] << ' ' << pos[2] << '\n'; 384 | } 385 | 386 | assert( faces.size() % 4 == 0 ); 387 | for( int i = 0; i+3 < faces.size(); i += 4 ) 388 | { 389 | std::cout << "f " << (faces[i+0]+1) << ' ' << (faces[i+1]+1) << ' ' << (faces[i+2]+1) << ' ' << (faces[i+3]+1) << '\n'; 390 | } 391 | } 392 | 393 | #endif 394 | -------------------------------------------------------------------------------- /lib/subdivision_matrices.cpp: -------------------------------------------------------------------------------- 1 | // c++ -g -std=c++11 -I/Users/yotam/Work/ext/OpenSubdiv/opensubdiv -I/Users/yotam/Work/ext/OpenSubdiv/regression subdivision_matrices.cpp -o subdivision_matrices -DSUBDIVISION_MATRICES_MAIN 2 | 3 | #include "subdivision_matrices.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace 9 | { 10 | 11 | /* 12 | Given the number of vertices in the mesh 'num_vertices' 13 | and a vector of faces 'faces', each element of which is a vector of vertex indices, 14 | returns a new mesh object suitable for passing to 15 | Free the resulting mesh with "delete" when finished with it. 16 | */ 17 | OpenSubdiv::HbrMesh* newMeshFromNumVerticesAndFaces( int num_vertices, const subdivision_matrix::faces_t& faces ) 18 | { 19 | assert( num_vertices > 0 ); 20 | 21 | // Following: http://graphics.pixar.com/opensubdiv/forum.html?place=msg%2Fopensubdiv%2FzKq9vG_azHQ%2FDb2K7L23BykJ 22 | 23 | static OpenSubdiv::HbrCatmarkSubdivision CATMULL_CLARK; 24 | 25 | auto mesh = new OpenSubdiv::HbrMesh( &CATMULL_CLARK ); 26 | 27 | OpenSubdiv::FarStencilFactoryVertex default_vertex_value; 28 | for( int vi = 0; vi < num_vertices; ++vi ) mesh->NewVertex( vi, default_vertex_value ); 29 | 30 | /// TODO Q: Is this necessary? 31 | // int ptex_index = 0; 32 | int face_offset = 0; 33 | for( const auto& num_face_vertices : faces.first ) 34 | { 35 | OpenSubdiv::HbrFace* face = mesh->NewFace( num_face_vertices, &faces.second[face_offset], 0 ); 36 | 37 | /* 38 | face->SetPtexIndex( ptex_index ); 39 | if( f.size() != 4 ) { 40 | ptex_index += f.size(); 41 | } else { 42 | ptex_index += 1; 43 | } 44 | */ 45 | 46 | face_offset += num_face_vertices; 47 | } 48 | 49 | mesh->SetInterpolateBoundaryMethod( OpenSubdiv::HbrMesh::k_InterpolateBoundaryAlwaysSharp ); 50 | 51 | mesh->Finish(); 52 | 53 | return mesh; 54 | } 55 | 56 | } 57 | 58 | namespace subdivision_matrix 59 | { 60 | 61 | /* 62 | Given a desired number of "u" and "v" parameters sampling the range [0,1]x[0,1], 63 | returns in the output vectors 'us' and 'vs' coordinates sampling the range 64 | in equal intervals. 65 | */ 66 | void createUVs( int NUM_U, int NUM_V, std::vector< real_t >& us, std::vector< real_t >& vs ) 67 | { 68 | assert( NUM_U > 0 ); 69 | assert( NUM_V > 0 ); 70 | 71 | us.clear(); 72 | vs.clear(); 73 | 74 | us.reserve( NUM_U * NUM_V ); 75 | vs.reserve( NUM_U * NUM_V ); 76 | 77 | /// XXX NOTE: Keep this in sync with subdivision_limit_mesh::createUVsWithZeroAndOne() 78 | for( int ui = 0; ui < NUM_U; ++ui ) 79 | { 80 | // Sample from .5 to .95, so that each sample has an equal piece of area. 81 | const real_t u = real_t( ui + 0.5 )/NUM_U; 82 | // const real_t u = real_t( ui )/(NUM_U-1); 83 | for( int vi = 0; vi < NUM_V; ++vi ) 84 | { 85 | // Sample from .5 to .95, so that each sample has an equal piece of area. 86 | const real_t v = real_t( vi + 0.5 )/NUM_V; 87 | // const real_t v = real_t( vi )/(NUM_V-1); 88 | 89 | us.push_back( u ); 90 | vs.push_back( v ); 91 | } 92 | } 93 | } 94 | 95 | /* 96 | Given a mesh, 97 | two same-length vectors 'us' and 'vs' where the ( us[i], vs[i] ) are the uv locations, 98 | and an optional integer parameter 'reflevel' ("In most cases only approximate evaluation 99 | is done by linearly interpolating between limit values after refLevel subdivision steps."), 100 | returns a FarStencilTables object suitable for calling UpdateValues() and UpdateDerivs(). 101 | */ 102 | OpenSubdiv::FarStencilTables precomputeStencils( 103 | OpenSubdiv::HbrMesh* mesh, 104 | const std::vector< float >& us, 105 | const std::vector< float >& vs, 106 | int reflevel = 5 107 | ) 108 | { 109 | assert( mesh ); 110 | assert( us.size() == vs.size() ); 111 | assert( !us.empty() ); 112 | assert( reflevel > 0 ); 113 | 114 | OpenSubdiv::FarStencilTables controlStencils; 115 | 116 | OpenSubdiv::FarStencilTablesFactory<> factory(mesh); 117 | 118 | // std::cout << "Stencil num faces: " << mesh->GetNumFaces() << '\n'; 119 | 120 | // Check if we are using catmull-clark subdivision. 121 | // const bool catmark = 0 != dynamic_cast< OpenSubdiv::HbrCatmarkSubdivision* >( mesh->GetSubdivision() ); 122 | 123 | // XXXX UDPATE: mesh->GetNumFaces() changes during calls to AppendStencils()! 124 | // We end up with lots of duplicate stencils if we don't store the 125 | // initial value of GetNumFaces(). Better yet, store GetNumCoarseFaces(). 126 | // I checked the HbrMesh code and the initial non-NULL faces are all 127 | // coarse. I'm not sure how a NULL face could get into the structure, 128 | // so let's assert that faces aren't NULL. 129 | const int nfaces = mesh->GetNumCoarseFaces(); 130 | for( int i = 0; i < nfaces; ++i ) { 131 | 132 | const OpenSubdiv::HbrFace* f = mesh->GetFace(i); 133 | assert( f ); 134 | 135 | const int nv = f->GetNumVertices(); 136 | 137 | // TODO Q: Why doesn't this die with loop subdivision? 138 | // if( catmark && nv!=4 ) { 139 | if( nv!=4 ) { 140 | 141 | // if the face is not a quad, we have to iterate over sub-quad(rants) 142 | for (int j=0; jGetNumVertices(); ++j) { 143 | 144 | // std::cout << "Non-quad face with " << nv << " vertices.\n"; 145 | 146 | factory.SetCurrentFace(i,j); 147 | 148 | const int appended = factory.AppendStencils( &controlStencils, us.size(), &us[0], &vs[0], reflevel ); 149 | // std::cout << "Appended " << appended << " stencils.\n"; 150 | } 151 | } else { 152 | 153 | factory.SetCurrentFace(i); 154 | 155 | const int appended = factory.AppendStencils( &controlStencils, us.size(), &us[0], &vs[0], reflevel ); 156 | // std::cout << "Appended " << appended << " stencils.\n"; 157 | } 158 | } 159 | 160 | return controlStencils; 161 | } 162 | 163 | /* 164 | Given OpenSubdiv::FarStencilTables as returned by precomputeStencils() 165 | and the number of control points 'num_control_points' 166 | fills 'positions_out' with sparse vectors such that the position 167 | for the i-th uv value in the original call to precomputeStencils() can be obtained by: 168 | \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second 169 | 170 | The optional parameters 'du_out' and 'dv_out', if specified, are similar to 'positions_out' 171 | except that the above summation results in du and dv. 172 | */ 173 | void sparseVectorsForPositionsAndDerivativesFromFarStencilTables( 174 | const OpenSubdiv::FarStencilTables& controlStencils, 175 | int num_control_points, 176 | std::vector< sparse_vector_t >& positions_out, 177 | std::vector< sparse_vector_t >* du_out = nullptr, 178 | std::vector< sparse_vector_t >* dv_out = nullptr 179 | ) 180 | { 181 | /// 1 Create a vector of control points, where each control point is a sparse vector containing only its integer index. 182 | /// 2 Create a vector of output coefficients, whose length is the total number 183 | /// of all positions on the mesh we are evaluating. 184 | /// 3 Create adapters for passing to OpenSubdiv::FarStencilTables.UpdateValues(). 185 | /// 4 Call OpenSubdiv::FarStencilTables.UpdateValues(). 186 | /// 5 Repeat with OpenSubdiv::FarStencilTables.UpdateDerivs(). 187 | 188 | assert( num_control_points > 0 ); 189 | // The derivative outputs, du_out and dv_out, 190 | // must be either both null or both given. 191 | assert( ( du_out == nullptr ) == ( dv_out == nullptr ) ); 192 | // du_out and dv_out must be distinct unless they are both null. 193 | assert( du_out != dv_out || du_out == nullptr ); 194 | 195 | /// 1 196 | // controlPoints are just indices! 197 | std::vector< sparse_vector_t > controlPoints( num_control_points ); 198 | static const real_t kMagicValue{ 31337 }; 199 | for( int i = 0; i < num_control_points; ++i ) controlPoints[i].push_back( std::make_pair( i, kMagicValue ) ); 200 | 201 | 202 | /// 2 203 | positions_out.clear(); 204 | positions_out.resize( controlStencils.GetNumStencils() ); 205 | 206 | 207 | /// 3 208 | struct SparseVectorAdapter 209 | { 210 | sparse_vector_t* data; 211 | 212 | void Clear() { assert( data ); data->clear(); } 213 | void AddWithWeight( const SparseVectorAdapter& other, real_t value ) 214 | { 215 | assert( data ); 216 | assert( other.data ); 217 | assert( other.data->size() == 1 ); 218 | assert( (*other.data)[0].second == kMagicValue ); 219 | 220 | const index_t index = (*other.data)[0].first; 221 | data->push_back( std::make_pair( index, value ) ); 222 | } 223 | }; 224 | 225 | std::vector< SparseVectorAdapter > controlPoints_adapters( controlPoints.size() ); 226 | for( int i = 0; i < controlPoints_adapters.size(); ++i ) controlPoints_adapters[i].data = &controlPoints[i]; 227 | 228 | std::vector< SparseVectorAdapter > positions_adapters( positions_out.size() ); 229 | for( int i = 0; i < positions_adapters.size(); ++i ) positions_adapters[i].data = &positions_out[i]; 230 | 231 | 232 | /// 4 233 | controlStencils.UpdateValues< SparseVectorAdapter >( &controlPoints_adapters[0], &positions_adapters[0] ); 234 | 235 | 236 | if( du_out && dv_out ) 237 | { 238 | du_out->clear(); 239 | dv_out->clear(); 240 | du_out->resize( controlStencils.GetNumStencils() ); 241 | dv_out->resize( controlStencils.GetNumStencils() ); 242 | 243 | std::vector< SparseVectorAdapter > du_adapters( du_out->size() ); 244 | std::vector< SparseVectorAdapter > dv_adapters( dv_out->size() ); 245 | for( int i = 0; i < du_adapters.size(); ++i ) du_adapters[i].data = &(*du_out)[i]; 246 | for( int i = 0; i < du_adapters.size(); ++i ) dv_adapters[i].data = &(*dv_out)[i]; 247 | 248 | controlStencils.UpdateDerivs< SparseVectorAdapter >( &controlPoints_adapters[0], &du_adapters[0], &dv_adapters[0] ); 249 | } 250 | } 251 | 252 | /* 253 | Given the number of vertices in the mesh 'num_vertices' 254 | and a vector of faces 'faces', each element of which is a vector of vertex indices, and 255 | two same-length vectors 'us' and 'vs' where the ( us[i], vs[i] ) are the uv locations, 256 | fills 'positions_out' with sparse vectors such that the position 257 | for the i-th uv value in the original call to precomputeStencils() can be obtained by: 258 | \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second 259 | 260 | The optional parameters 'du_out' and 'dv_out', if specified, are similar to 'positions_out' 261 | except that the above summation results in du and dv. 262 | */ 263 | void compute_subdivision_coefficients_for_mesh( 264 | int num_vertices, 265 | const faces_t& faces, 266 | const std::vector< real_t >& us, 267 | const std::vector< real_t >& vs, 268 | std::vector< sparse_vector_t >& positions_out, 269 | std::vector< sparse_vector_t >* du_out, 270 | std::vector< sparse_vector_t >* dv_out 271 | ) 272 | { 273 | auto mesh = newMeshFromNumVerticesAndFaces( num_vertices, faces ); 274 | compute_subdivision_coefficients_for_mesh( num_vertices, mesh, us, vs, positions_out, du_out, dv_out ); 275 | delete mesh; 276 | } 277 | 278 | void compute_subdivision_coefficients_for_mesh( 279 | int num_vertices, 280 | OpenSubdiv::HbrMesh* mesh, 281 | const std::vector< real_t >& us, 282 | const std::vector< real_t >& vs, 283 | std::vector< sparse_vector_t >& positions_out, 284 | std::vector< sparse_vector_t >* du_out, 285 | std::vector< sparse_vector_t >* dv_out 286 | ) 287 | { 288 | std::vector< float > real_us, real_vs; 289 | for ( auto u : us ) 290 | real_us.push_back( u ); 291 | for ( auto v : vs ) 292 | real_vs.push_back( v ); 293 | 294 | auto precomputed = precomputeStencils( mesh, real_us, real_vs ); 295 | sparseVectorsForPositionsAndDerivativesFromFarStencilTables( precomputed, num_vertices, positions_out, du_out, dv_out ); 296 | } 297 | 298 | } // ~namespace subdivision_matrix() 299 | 300 | #ifdef SUBDIVISION_MATRICES_MAIN 301 | void print_sparse_vectors( const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::string& label ); 302 | 303 | #define TORUS 32, { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } 304 | #define CUBE 8, { {0, 1, 3, 2}, {2, 3, 5, 4}, {4, 5, 7, 6}, {6, 7, 1, 0}, {1, 7, 5, 3}, {6, 0, 2, 4} } 305 | 306 | void test_newMesh_sparseVectors( int resolution, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) 307 | { 308 | using namespace subdivision_matrix; 309 | 310 | auto mesh = newMeshFromNumVerticesAndFaces( 311 | // Cube 312 | // CUBE 313 | // Torus 314 | TORUS 315 | ); 316 | 317 | std::vector< float > us, vs; 318 | createUVs( resolution, resolution, us, vs ); 319 | OpenSubdiv::FarStencilTables controlStencils = precomputeStencils( mesh, us, vs ); 320 | 321 | // std::vector< sparse_vector_t > position_coeffs; 322 | std::vector< sparse_vector_t > du_coeffs; 323 | std::vector< sparse_vector_t > dv_coeffs; 324 | sparseVectorsForPositionsAndDerivativesFromFarStencilTables( controlStencils, mesh->GetNumVertices(), position_coeffs, &du_coeffs, &dv_coeffs ); 325 | 326 | print_sparse_vectors( position_coeffs, "position_coeffs: " ); 327 | print_sparse_vectors( du_coeffs, "du_coeffs: " ); 328 | print_sparse_vectors( dv_coeffs, "dv_coeffs: " ); 329 | 330 | delete mesh; 331 | } 332 | 333 | void test_compute_coefficients( int resolution, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) 334 | { 335 | using namespace subdivision_matrix; 336 | 337 | std::vector< real_t > us, vs; 338 | createUVs( resolution, resolution, us, vs ); 339 | 340 | // std::vector< sparse_vector_t > position_coeffs; 341 | std::vector< sparse_vector_t > du_coeffs; 342 | std::vector< sparse_vector_t > dv_coeffs; 343 | compute_subdivision_coefficients_for_mesh( 344 | // Cube 345 | // CUBE, 346 | // Torus 347 | TORUS, 348 | us, vs, 349 | position_coeffs, &du_coeffs, &dv_coeffs 350 | ); 351 | 352 | print_sparse_vectors( position_coeffs, "position_coeffs: " ); 353 | print_sparse_vectors( du_coeffs, "du_coeffs: " ); 354 | print_sparse_vectors( dv_coeffs, "dv_coeffs: " ); 355 | } 356 | 357 | // -I/path/to/regression 358 | #include 359 | #include 360 | #include 361 | void test_shape_utils( int resolution, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) 362 | { 363 | using namespace subdivision_matrix; 364 | 365 | // Create a torus. 366 | std::vector orgPositions; 367 | auto mesh = simpleHbr( catmark_torus.c_str(), kCatmark, orgPositions, true); 368 | // auto mesh = simpleHbr( catmark_cube.c_str(), kCatmark, orgPositions, true); 369 | 370 | std::vector< float > us, vs; 371 | createUVs( resolution, resolution, us, vs ); 372 | OpenSubdiv::FarStencilTables controlStencils = precomputeStencils( mesh, us, vs ); 373 | 374 | 375 | // auto p_weights = controlStencils.GetWeights(); 376 | // auto du_weights = controlStencils.GetDuWeights(); 377 | // auto dv_weights = controlStencils.GetDvWeights(); 378 | 379 | 380 | // std::vector< sparse_vector_t > position_coeffs; 381 | std::vector< sparse_vector_t > du_coeffs; 382 | std::vector< sparse_vector_t > dv_coeffs; 383 | sparseVectorsForPositionsAndDerivativesFromFarStencilTables( controlStencils, mesh->GetNumVertices(), position_coeffs, &du_coeffs, &dv_coeffs ); 384 | 385 | print_sparse_vectors( position_coeffs, "position_coeffs: " ); 386 | print_sparse_vectors( du_coeffs, "du_coeffs: " ); 387 | print_sparse_vectors( dv_coeffs, "dv_coeffs: " ); 388 | } 389 | 390 | #include 391 | #include 392 | void usage( const char* argv0 ) 393 | { 394 | std::cerr << "Usage: " << argv0 << " test_shape_utils|test_compute_coefficients|test_newMesh_sparseVectors resolution\n"; 395 | exit( -1 ); 396 | } 397 | int main( int argc, char* argv[] ) 398 | { 399 | using std::string; 400 | 401 | if( argc != 3 ) usage( argv[0] ); 402 | 403 | const int resolution = atoi( argv[2] ); 404 | if( resolution < 0 ) usage( argv[0] ); 405 | 406 | const string test_name = string(argv[1]); 407 | 408 | std::vector< subdivision_matrix::sparse_vector_t > position_coeffs; 409 | if( string("test_shape_utils") == test_name ) 410 | { 411 | test_shape_utils( resolution, position_coeffs ); 412 | } 413 | else if( string("test_compute_coefficients") == test_name ) 414 | { 415 | test_compute_coefficients( resolution, position_coeffs ); 416 | } 417 | else if( string("test_newMesh_sparseVectors") == test_name ) 418 | { 419 | test_newMesh_sparseVectors( resolution, position_coeffs ); 420 | } 421 | else 422 | { 423 | usage( argv[0] ); 424 | } 425 | 426 | // TODO: Do something with position_coeffs 427 | 428 | return 0; 429 | } 430 | 431 | void print_sparse_vectors( const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::string& label = "" ) 432 | { 433 | std::cout << label << "{"; 434 | for( const auto vec : sparse_vectors ) 435 | { 436 | std::cout << "\n { "; 437 | for( const auto p : vec ) 438 | { 439 | std::cout << "{ " << p.first << ", " << p.second << " }, "; 440 | } 441 | std::cout << " },"; 442 | } 443 | std::cout << "}\n"; 444 | } 445 | 446 | #endif 447 | --------------------------------------------------------------------------------