├── .appveyor.yml ├── .gitmodules ├── .travis.yml ├── CatmullClark.h ├── LICENSE.txt ├── README.md ├── examples ├── CMakeLists.txt ├── ConcurrentBitField.h ├── README.md ├── glad │ ├── glad.c │ ├── glad.h │ └── khrplatform.h ├── mesh_info.c ├── meshes │ ├── ArmorGuy.ccm │ ├── Bigguy.ccm │ ├── Bishop.ccm │ ├── Car.ccm │ ├── Imrod.ccm │ ├── Monsterfrog.ccm │ ├── Rook.ccm │ └── T-Rex.ccm ├── obj_to_ccm.c ├── subd_cpu.c └── subd_gpu.c └── glsl ├── CatmullClark_Scatter.glsl ├── cc_CageEdgePoints_Scatter.glsl ├── cc_CageFacePoints_Scatter.glsl ├── cc_CageVertexPoints_Scatter.glsl ├── cc_EdgePoints_Scatter.glsl ├── cc_FacePoints_Scatter.glsl ├── cc_RefineCageCreases.glsl ├── cc_RefineCageHalfedges.glsl ├── cc_RefineCageVertexUvs.glsl ├── cc_RefineCreases.glsl ├── cc_RefineHalfedges.glsl ├── cc_RefineVertexUvs.glsl └── cc_VertexPoints_Scatter.glsl /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | pull_requests: 3 | do_not_increment_build_number: true 4 | os: Visual Studio 2017 5 | test: off 6 | branches: 7 | only: 8 | - master 9 | clone_folder: C:\catmark 10 | install: 11 | - git submodule update --init --recursive 12 | build_script: 13 | - cd C:\catmark\examples 14 | - mkdir build 15 | - cd build 16 | - cmake -G "Visual Studio 15 2017 Win64" .. 17 | - set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 18 | - set MSBuildOptions=/v:m /p:Configuration=Release /logger:%MSBuildLogger% 19 | - msbuild %MSBuildOptions% CatmullClark.sln 20 | 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "examples/submodules/glfw"] 2 | path = examples/submodules/glfw 3 | url = https://github.com/glfw/glfw.git 4 | [submodule "examples/submodules/dj_opengl"] 5 | path = examples/submodules/dj_opengl 6 | url = https://github.com/jdupuy/dj_opengl.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: false 3 | dist: trusty 4 | branches: 5 | only: 6 | - master 7 | matrix: 8 | include: 9 | - os: linux 10 | compiler: gcc-4.8 11 | addons: 12 | apt: 13 | packages: 14 | - cmake 15 | - g++-4.8 16 | - libglu1-mesa-dev 17 | - libxxf86vm-dev 18 | - libxrandr-dev 19 | - libxinerama-dev 20 | - libxcursor-dev 21 | - libxi-dev 22 | - libx11-dev 23 | script: 24 | - cmake --version 25 | - cd examples 26 | - mkdir build 27 | - cd build 28 | - cmake .. 29 | - make -j 4 30 | 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------ 2 | This software is available under 2 licenses -- choose whichever you prefer. 3 | ------------------------------------------------------------------------------ 4 | ALTERNATIVE A - MIT License 5 | Copyright (c) 2019 Jonathan Dupuy 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | of the Software, and to permit persons to whom the Software is furnished to do 11 | so, subject to the following conditions: 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | ------------------------------------------------------------------------------ 22 | ALTERNATIVE B - Public Domain (www.unlicense.org) 23 | This is free and unencumbered software released into the public domain. 24 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 25 | software, either in source code form or as a compiled binary, for any purpose, 26 | commercial or non-commercial, and by any means. 27 | In jurisdictions that recognize copyright laws, the author or authors of this 28 | software dedicate any and all copyright interest in the software to the public 29 | domain. We make this dedication for the benefit of the public at large and to 30 | the detriment of our heirs and successors. We intend this dedication to be an 31 | overt act of relinquishment in perpetuity of all present and future rights to 32 | this software under copyright law. 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 37 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 38 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 39 | ------------------------------------------------------------------------------ 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/jdupuy/HalfedgeCatmullClark.svg?branch=master)](https://travis-ci.com/jdupuy/HalfedgeCatmullClark) 2 | [![Build status](https://ci.appveyor.com/api/projects/status/sqogwscp9vscyo7s?svg=true)](https://ci.appveyor.com/project/jdupuy/halfedgecatmullclark) 3 | 4 | This repository provides source code to reproduce some of the results of my paper ["A Halfedge Refinement Rule for Parallel Catmull-Clark Subdivision"](https://onrendering.com/). 5 | The key contribution of this paper is to provide super simple algorithms to compute 6 | Catmull-Clark subdivision in parallel with support for semi-sharp creases. The algorithms are compiled in the C header-only library `CatmullClark.h`. In addition you will find a direct GLSL port of these algorithms in the 7 | `glsl/` folder. For various usage examples, see the `examples/` folder. 8 | 9 | ### License 10 | 11 | Apart from the submodules folder, the code from this repository is released in public domain. You can do anything you want with them. You have no legal obligation to do anything else, although I appreciate attribution. 12 | 13 | It is also licensed under the MIT open source license, if you have lawyers who are unhappy with public domain. 14 | 15 | ### Cloning 16 | 17 | Clone the repository and all its submodules using the following command: 18 | ```sh 19 | git clone --recursive git@github.com:jdupuy/HalfedgeCatmullClark.git 20 | ``` 21 | 22 | If you accidentally omitted the `--recursive` flag when cloning the repository you can retrieve the submodules like so: 23 | ```sh 24 | git submodule update --init --recursive 25 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.1) 2 | 3 | project(CatmullClark) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_C_STANDARD_REQUIRED ON) 7 | #set(CMAKE_C_FLAGS "-Os -march=native") 8 | #set(CMAKE_C_FLAGS "-g") 9 | 10 | find_package(OpenMP) 11 | if (OPENMP_FOUND) 12 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 13 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 14 | set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") 15 | endif() 16 | 17 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) 18 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) 19 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 20 | 21 | add_subdirectory(submodules/glfw) 22 | include_directories(submodules/glfw/include) 23 | include_directories(submodules/dj_opengl) 24 | include_directories(..) 25 | 26 | add_executable(obj_to_ccm obj_to_ccm.c) 27 | add_executable(mesh_info mesh_info.c) 28 | add_executable(subd_cpu subd_cpu.c) 29 | 30 | add_executable(bench_cpu subd_cpu.c) 31 | target_compile_definitions(bench_cpu PUBLIC -DFLAG_BENCH) 32 | 33 | add_executable(subd_gpu subd_gpu.c glad/glad.c) 34 | target_link_libraries(subd_gpu glfw) 35 | target_compile_definitions( 36 | subd_gpu PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/" 37 | ) 38 | IF (NOT WIN32) 39 | target_link_libraries(subd_gpu pthread) 40 | ENDIF() 41 | 42 | 43 | add_executable(bench_gpu subd_gpu.c glad/glad.c) 44 | target_link_libraries(bench_gpu glfw) 45 | target_compile_definitions( 46 | bench_gpu PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/" -DFLAG_BENCH 47 | ) 48 | IF (NOT WIN32) 49 | target_link_libraries(bench_gpu pthread) 50 | ENDIF() 51 | -------------------------------------------------------------------------------- /examples/ConcurrentBitField.h: -------------------------------------------------------------------------------- 1 | /* cbf.h - public domain library for building binary trees in parallel 2 | by Jonathan Dupuy 3 | 4 | Do this: 5 | #define CBF_IMPLEMENTATION 6 | before you include this file in *one* C or C++ file to create the implementation. 7 | 8 | // i.e. it should look like this: 9 | #include ... 10 | #include ... 11 | #include ... 12 | #define CBF_IMPLEMENTATION 13 | #include "cbf.h" 14 | 15 | INTERFACING 16 | define CBF_ASSERT(x) to avoid using assert.h 17 | define CBF_MALLOC(x) to use your own memory allocator 18 | define CBF_FREE(x) to use your own memory deallocator 19 | define CBF_MEMCPY(dst, src, num) to use your own memcpy routine 20 | define CBF_MEMSET(ptr, value, num) to use your own memset routine 21 | */ 22 | 23 | #ifndef CBF_INCLUDE_CBF_H 24 | #define CBF_INCLUDE_CBF_H 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #ifdef CBF_STATIC 31 | #define CBFDEF static 32 | #else 33 | #define CBFDEF extern 34 | #endif 35 | 36 | #include 37 | 38 | // data-structure 39 | typedef struct cbf_BitField cbf_BitField; 40 | 41 | // create / destroy bitfield 42 | CBFDEF cbf_BitField *cbf_Create(int64_t size); 43 | CBFDEF void cbf_Release(cbf_BitField *tree); 44 | 45 | // O(1) queries 46 | CBFDEF int64_t cbf_Size(const cbf_BitField *bf); 47 | CBFDEF int64_t cbf_BitCount(const cbf_BitField *bf); 48 | 49 | // O(Lg(size)) queries 50 | CBFDEF int64_t cbf_DecodeBit(const cbf_BitField *cbf, int64_t handle); 51 | CBFDEF int64_t cbf_EncodeBit(const cbf_BitField *cbf, int64_t bitID); 52 | 53 | // manipulation 54 | CBFDEF void cbf_Clear(cbf_BitField *cbf); 55 | CBFDEF uint64_t cbf_GetBit(const cbf_BitField *cbf, int64_t bitID); 56 | CBFDEF void cbf_SetBit(cbf_BitField *cbf, int64_t bitID, uint64_t bitValue); 57 | CBFDEF void cbf_Reduce(cbf_BitField *cbf); 58 | typedef void (*cbf_UpdateCallback)(cbf_BitField *cbf, 59 | const int64_t bitID, 60 | const void *userData); 61 | CBFDEF void cbf_Update(cbf_BitField *cbf, 62 | cbf_UpdateCallback updater, 63 | const void *userData); 64 | 65 | // serialization 66 | CBFDEF int64_t cbf_HeapMaxDepth(const cbf_BitField *cbf); 67 | CBFDEF int64_t cbf_HeapByteSize(const cbf_BitField *cbf); 68 | CBFDEF const char *cbf_GetHeap(const cbf_BitField *cbf); 69 | CBFDEF void cbf_SetHeap(cbf_BitField *tree, const char *heapToCopy); 70 | 71 | #ifdef __cplusplus 72 | } // extern "C" 73 | #endif 74 | 75 | // 76 | // 77 | //// end header file /////////////////////////////////////////////////////////// 78 | #endif // CBF_INCLUDE_CBF_H 79 | 80 | #ifdef CBF_IMPLEMENTATION 81 | 82 | #include 83 | 84 | #ifndef CBF_ASSERT 85 | # include 86 | # define CBF_ASSERT(x) assert(x) 87 | #endif 88 | 89 | #ifndef CBF_MALLOC 90 | # include 91 | # define CBF_MALLOC(x) (malloc(x)) 92 | # define CBF_FREE(x) (free(x)) 93 | #else 94 | # ifndef CBF_FREE 95 | # error CBF_MALLOC defined without CBF_FREE 96 | # endif 97 | #endif 98 | 99 | #ifndef CBF_MEMCPY 100 | # include 101 | # define CBF_MEMCPY(dst, src, num) memcpy(dst, src, num) 102 | #endif 103 | 104 | #ifndef CBF_MEMSET 105 | # include 106 | # define CBF_MEMSET(ptr, value, num) memset(ptr, value, num) 107 | #endif 108 | 109 | #ifndef _OPENMP 110 | # define CBF_ATOMIC 111 | # define CBF_PARALLEL_FOR 112 | # define CBF_BARRIER 113 | #else 114 | # if defined(_WIN32) 115 | # define CBF_ATOMIC __pragma("omp atomic" ) 116 | # define CBF_PARALLEL_FOR __pragma("omp parallel for") 117 | # define CBF_BARRIER __pragma("omp barrier") 118 | # else 119 | # define CBF_ATOMIC _Pragma("omp atomic" ) 120 | # define CBF_PARALLEL_FOR _Pragma("omp parallel for") 121 | # define CBF_BARRIER _Pragma("omp barrier") 122 | # endif 123 | #endif 124 | 125 | 126 | /******************************************************************************* 127 | * NextPowerOfTwo -- Returns the upper power of two value 128 | * 129 | * if the input is already a power of two, its value is returned. 130 | * 131 | */ 132 | static int64_t cbf__NextPowerOfTwo(int64_t x) 133 | { 134 | x--; 135 | x|= x >> 1; 136 | x|= x >> 2; 137 | x|= x >> 4; 138 | x|= x >> 8; 139 | x|= x >> 16; 140 | x|= x >> 32; 141 | x++; 142 | 143 | return x; 144 | } 145 | 146 | 147 | /******************************************************************************* 148 | * FindLSB -- Returns the position of the least significant bit 149 | * 150 | */ 151 | static inline int64_t cbf__FindLSB(uint64_t x) 152 | { 153 | int64_t lsb = 0; 154 | 155 | while (((x >> lsb) & 1u) == 0u) { 156 | ++lsb; 157 | } 158 | 159 | return lsb; 160 | } 161 | 162 | 163 | /******************************************************************************* 164 | * FindMSB -- Returns the position of the most significant bit 165 | * 166 | */ 167 | static inline int64_t cbf__FindMSB(uint64_t x) 168 | { 169 | int64_t msb = 0; 170 | 171 | while (x > 1u) { 172 | ++msb; 173 | x = x >> 1; 174 | } 175 | 176 | return msb; 177 | } 178 | 179 | 180 | /******************************************************************************* 181 | * MinValue -- Returns the minimum value between two inputs 182 | * 183 | */ 184 | static inline uint64_t cbf__MinValue(uint64_t a, uint64_t b) 185 | { 186 | return a < b ? a : b; 187 | } 188 | 189 | 190 | /******************************************************************************* 191 | * SetBitValue -- Sets the value of a bit stored in a bitfield 192 | * 193 | */ 194 | static void 195 | cbf__SetBitValue(uint64_t *bitField, int64_t bitID, uint64_t bitValue) 196 | { 197 | const uint64_t bitMask = ~(1ULL << bitID); 198 | 199 | CBF_ATOMIC 200 | (*bitField)&= bitMask; 201 | CBF_ATOMIC 202 | (*bitField)|= (bitValue << bitID); 203 | } 204 | 205 | 206 | /******************************************************************************* 207 | * GetBitValue -- Sets the value of a bit stored in a bitfield 208 | * 209 | */ 210 | static uint64_t cbf__GetBitValue(const uint64_t *bitField, int64_t bitID) 211 | { 212 | return ((*bitField) >> bitID) & 1ULL; 213 | } 214 | 215 | 216 | /******************************************************************************* 217 | * BitfieldInsert -- Inserts data in range [offset, offset + count - 1] 218 | * 219 | */ 220 | static inline void 221 | cbf__BitFieldInsert( 222 | uint64_t *bitField, 223 | int64_t bitOffset, 224 | int64_t bitCount, 225 | uint64_t bitData 226 | ) { 227 | CBF_ASSERT(bitOffset < 64 && bitCount <= 64 && bitOffset + bitCount <= 64); 228 | uint64_t bitMask = ~(~(0xFFFFFFFFFFFFFFFFULL << bitCount) << bitOffset); 229 | CBF_ATOMIC 230 | (*bitField)&= bitMask; 231 | CBF_ATOMIC 232 | (*bitField)|= (bitData << bitOffset); 233 | } 234 | 235 | 236 | /******************************************************************************* 237 | * BitFieldExtract -- Extracts bits [bitOffset, bitOffset + bitCount - 1] from 238 | * a bitfield, returning them in the least significant bits of the result. 239 | * 240 | */ 241 | static inline uint64_t 242 | cbf__BitFieldExtract( 243 | const uint64_t bitField, 244 | int64_t bitOffset, 245 | int64_t bitCount 246 | ) { 247 | CBF_ASSERT(bitOffset < 64 && bitCount < 64 && bitOffset + bitCount <= 64); 248 | uint64_t bitMask = ~(0xFFFFFFFFFFFFFFFFULL << bitCount); 249 | 250 | return (bitField >> bitOffset) & bitMask; 251 | } 252 | 253 | 254 | /******************************************************************************* 255 | * Parallel Binary Tree Data-Structure 256 | * 257 | */ 258 | struct cbf_BitField { 259 | uint64_t *heap; 260 | }; 261 | 262 | 263 | /******************************************************************************* 264 | * Node Data-Structure 265 | * 266 | * This data-structure is used internally to lookup data in the heap. 267 | * 268 | */ 269 | typedef struct { 270 | uint64_t id : 58; // heapID 271 | uint64_t depth: 6; // log2(heapID) 272 | } cbf__Node; 273 | 274 | 275 | /******************************************************************************* 276 | * CreateNode -- Constructor for the Node data structure 277 | * 278 | */ 279 | static cbf__Node cbf__CreateNode(uint64_t id, int64_t depth) 280 | { 281 | cbf__Node node; 282 | 283 | node.id = id; 284 | node.depth = depth; 285 | 286 | return node; 287 | } 288 | 289 | 290 | /******************************************************************************* 291 | * ParentNode -- Computes the parent of the input node 292 | * 293 | */ 294 | static cbf__Node cbf__ParentNode(const cbf__Node node) 295 | { 296 | return cbf__CreateNode(node.id >> 1, node.depth - 1); 297 | } 298 | 299 | 300 | /******************************************************************************* 301 | * LeftSiblingNode -- Computes the left sibling of the input node 302 | * 303 | */ 304 | static cbf__Node cbf__LeftSiblingNode(const cbf__Node node) 305 | { 306 | return cbf__CreateNode(node.id & (~1u), node.depth); 307 | } 308 | 309 | 310 | /******************************************************************************* 311 | * HeapByteSize -- Computes the number of Bytes to allocate for the bitfield 312 | * 313 | * For a tree of max depth D, the number of Bytes is 2^(D-1). 314 | * Note that 2 bits are "wasted" in the sense that they only serve 315 | * to round the required number of bytes to a power of two. 316 | * 317 | */ 318 | static int64_t cbf__HeapByteSize(uint64_t heapMaxDepth) 319 | { 320 | return 1LL << (heapMaxDepth - 1); 321 | } 322 | 323 | 324 | /******************************************************************************* 325 | * HeapUint64Size -- Computes the number of uints to allocate for the bitfield 326 | * 327 | */ 328 | static inline int64_t cbf__HeapUint64Size(int64_t treeMaxDepth) 329 | { 330 | return cbf__HeapByteSize(treeMaxDepth) >> 3; 331 | } 332 | 333 | 334 | /******************************************************************************* 335 | * BitFieldUint64Index -- Computes the index for accessing the bitfield in the heap 336 | * 337 | */ 338 | static int64_t cbf__BitFieldUint64Index(const cbf_BitField *cbf) 339 | { 340 | return (3LL << (cbf_HeapMaxDepth(cbf) - 6)); 341 | } 342 | 343 | 344 | /******************************************************************************* 345 | * NodeBitID -- Returns the bit index that stores data associated with a given node 346 | * 347 | * For a tree of max depth D and given an index in [0, 2^(D+1) - 1], this 348 | * functions is used to emulate the behaviour of a lookup in an array, i.e., 349 | * uint[nodeID]. It provides the first bit in memory that stores 350 | * information associated with the element of index nodeID. 351 | * 352 | * For data located at level d, the bit offset is 2^d x (3 - d + D) 353 | * We then offset this quantity by the index by (nodeID - 2^d) x (D + 1 - d) 354 | * Note that the null index (nodeID = 0) is also supported. 355 | * 356 | */ 357 | static inline int64_t cbf__NodeBitID(const cbf_BitField *tree, const cbf__Node node) 358 | { 359 | int64_t tmp1 = 2LL << node.depth; 360 | int64_t tmp2 = 1LL + cbf_HeapMaxDepth(tree) - node.depth; 361 | 362 | return tmp1 + node.id * tmp2; 363 | } 364 | 365 | 366 | /******************************************************************************* 367 | * NodeBitSize -- Returns the number of bits storing the input node value 368 | * 369 | */ 370 | static inline int64_t 371 | cbf__NodeBitSize(const cbf_BitField *tree, const cbf__Node node) 372 | { 373 | return cbf_HeapMaxDepth(tree) - node.depth + 1; 374 | } 375 | 376 | 377 | /******************************************************************************* 378 | * HeapArgs 379 | * 380 | * The CBF heap data structure uses an array of 64-bit words to store its data. 381 | * Whenever we need to access a certain bit range, we need to query two such 382 | * words (because sometimes the requested bit range overlaps two 64-bit words). 383 | * The HeapArg data structure provides arguments for reading from and/or 384 | * writing to the two 64-bit words that bound the queries range. 385 | * 386 | */ 387 | typedef struct { 388 | uint64_t *bitFieldLSB, *bitFieldMSB; 389 | int64_t bitOffsetLSB; 390 | int64_t bitCountLSB, bitCountMSB; 391 | } cbf__HeapArgs; 392 | 393 | cbf__HeapArgs 394 | cbf__CreateHeapArgs(const cbf_BitField *tree, const cbf__Node node, int64_t bitCount) 395 | { 396 | int64_t alignedBitOffset = cbf__NodeBitID(tree, node); 397 | int64_t maxBufferIndex = cbf__HeapUint64Size(cbf_HeapMaxDepth(tree)) - 1; 398 | int64_t bufferIndexLSB = (alignedBitOffset >> 6); 399 | int64_t bufferIndexMSB = cbf__MinValue(bufferIndexLSB + 1, maxBufferIndex); 400 | cbf__HeapArgs args; 401 | 402 | args.bitOffsetLSB = alignedBitOffset & 63; 403 | args.bitCountLSB = cbf__MinValue(64 - args.bitOffsetLSB, bitCount); 404 | args.bitCountMSB = bitCount - args.bitCountLSB; 405 | args.bitFieldLSB = &tree->heap[bufferIndexLSB]; 406 | args.bitFieldMSB = &tree->heap[bufferIndexMSB]; 407 | 408 | return args; 409 | } 410 | 411 | 412 | /******************************************************************************* 413 | * HeapWrite -- Sets bitCount bits located at nodeID to bitData 414 | * 415 | * Note that this procedure writes to at most two uint64 elements. 416 | * Two elements are relevant whenever the specified interval overflows 64-bit 417 | * words. 418 | * 419 | */ 420 | static void 421 | cbf__HeapWriteExplicit( 422 | cbf_BitField *tree, 423 | const cbf__Node node, 424 | int64_t bitCount, 425 | uint64_t bitData 426 | ) { 427 | cbf__HeapArgs args = cbf__CreateHeapArgs(tree, node, bitCount); 428 | 429 | cbf__BitFieldInsert(args.bitFieldLSB, 430 | args.bitOffsetLSB, 431 | args.bitCountLSB, 432 | bitData); 433 | cbf__BitFieldInsert(args.bitFieldMSB, 434 | 0u, 435 | args.bitCountMSB, 436 | bitData >> args.bitCountLSB); 437 | } 438 | 439 | static void 440 | cbf__HeapWrite(cbf_BitField *tree, const cbf__Node node, uint64_t bitData) 441 | { 442 | cbf__HeapWriteExplicit(tree, node, cbf__NodeBitSize(tree, node), bitData); 443 | } 444 | 445 | 446 | /******************************************************************************* 447 | * HeapRead -- Returns bitCount bits located at nodeID 448 | * 449 | * Note that this procedure reads from two uint64 elements. 450 | * This is because the data is not necessarily aligned with 64-bit 451 | * words. 452 | * 453 | */ 454 | static uint64_t 455 | cbf__HeapReadExplicit( 456 | const cbf_BitField *tree, 457 | const cbf__Node node, 458 | int64_t bitCount 459 | ) { 460 | cbf__HeapArgs args = cbf__CreateHeapArgs(tree, node, bitCount); 461 | uint64_t lsb = cbf__BitFieldExtract(*args.bitFieldLSB, 462 | args.bitOffsetLSB, 463 | args.bitCountLSB); 464 | uint64_t msb = cbf__BitFieldExtract(*args.bitFieldMSB, 465 | 0u, 466 | args.bitCountMSB); 467 | 468 | return (lsb | (msb << args.bitCountLSB)); 469 | } 470 | 471 | CBFDEF uint64_t cbf__HeapRead(const cbf_BitField *tree, const cbf__Node node) 472 | { 473 | return cbf__HeapReadExplicit(tree, node, cbf__NodeBitSize(tree, node)); 474 | } 475 | 476 | 477 | /******************************************************************************* 478 | * SetBit -- Set a specific bit to either 0 or 1 in the bitfield 479 | * 480 | */ 481 | CBFDEF void cbf_SetBit(cbf_BitField *cbf, int64_t bitID, uint64_t bitValue) 482 | { 483 | uint64_t *bitField = &cbf->heap[cbf__BitFieldUint64Index(cbf)]; 484 | 485 | cbf__SetBitValue(&bitField[bitID >> 6], bitID & 63, bitValue); 486 | } 487 | 488 | 489 | /******************************************************************************* 490 | * GetBit -- Returns a specific bit value in the bitfield 491 | * 492 | */ 493 | CBFDEF uint64_t cbf_GetBit(const cbf_BitField *cbf, int64_t bitID) 494 | { 495 | const uint64_t *bitField = &cbf->heap[cbf__BitFieldUint64Index(cbf)]; 496 | 497 | return cbf__GetBitValue(&bitField[bitID >> 6], bitID & 63); 498 | } 499 | 500 | 501 | /******************************************************************************* 502 | * Clear -- Clears the bitfield 503 | * 504 | */ 505 | CBFDEF void cbf_Clear(cbf_BitField *cbf) 506 | { 507 | int64_t heapDepth = cbf_HeapMaxDepth(cbf); 508 | 509 | CBF_MEMSET(cbf->heap, 0, cbf_HeapByteSize(cbf)); 510 | cbf->heap[0] = 1ULL << heapDepth; 511 | } 512 | 513 | 514 | /******************************************************************************* 515 | * GetHeap -- Returns a read-only pointer to the heap memory 516 | * 517 | */ 518 | CBFDEF const char *cbf_GetHeap(const cbf_BitField *tree) 519 | { 520 | return (const char *)tree->heap; 521 | } 522 | 523 | 524 | /******************************************************************************* 525 | * SetHeap -- Sets the heap memory from a read-only buffer 526 | * 527 | */ 528 | CBFDEF void cbf_SetHeap(cbf_BitField *tree, const char *buffer) 529 | { 530 | CBF_MEMCPY(tree->heap, buffer, cbf_HeapByteSize(tree)); 531 | } 532 | 533 | 534 | /******************************************************************************* 535 | * HeapByteSize -- Returns the amount of bytes consumed by the CBF heap 536 | * 537 | */ 538 | CBFDEF int64_t cbf_HeapByteSize(const cbf_BitField *tree) 539 | { 540 | return cbf__HeapByteSize(cbf_HeapMaxDepth(tree)); 541 | } 542 | 543 | 544 | /******************************************************************************* 545 | * Reduce -- Sums the 2 elements below the current slot 546 | * 547 | */ 548 | CBFDEF void cbf_Reduce(cbf_BitField *tree) 549 | { 550 | int64_t depth = cbf_HeapMaxDepth(tree); 551 | uint64_t minNodeID = (1ULL << depth); 552 | uint64_t maxNodeID = (2ULL << depth); 553 | 554 | // prepass: processes deepest levels in parallel 555 | CBF_PARALLEL_FOR 556 | for (uint64_t nodeID = minNodeID; nodeID < maxNodeID; nodeID+= 64u) { 557 | cbf__Node heapNode = cbf__CreateNode(nodeID, depth); 558 | int64_t alignedBitOffset = cbf__NodeBitID(tree, heapNode); 559 | uint64_t bitField = tree->heap[alignedBitOffset >> 6]; 560 | uint64_t bitData = 0u; 561 | 562 | // 2-bits 563 | bitField = (bitField & 0x5555555555555555ULL) 564 | + ((bitField >> 1) & 0x5555555555555555ULL); 565 | bitData = bitField; 566 | tree->heap[(alignedBitOffset - minNodeID) >> 6] = bitData; 567 | 568 | // 3-bits 569 | bitField = (bitField & 0x3333333333333333ULL) 570 | + ((bitField >> 2) & 0x3333333333333333ULL); 571 | bitData = ((bitField >> 0) & (7ULL << 0)) 572 | | ((bitField >> 1) & (7ULL << 3)) 573 | | ((bitField >> 2) & (7ULL << 6)) 574 | | ((bitField >> 3) & (7ULL << 9)) 575 | | ((bitField >> 4) & (7ULL << 12)) 576 | | ((bitField >> 5) & (7ULL << 15)) 577 | | ((bitField >> 6) & (7ULL << 18)) 578 | | ((bitField >> 7) & (7ULL << 21)) 579 | | ((bitField >> 8) & (7ULL << 24)) 580 | | ((bitField >> 9) & (7ULL << 27)) 581 | | ((bitField >> 10) & (7ULL << 30)) 582 | | ((bitField >> 11) & (7ULL << 33)) 583 | | ((bitField >> 12) & (7ULL << 36)) 584 | | ((bitField >> 13) & (7ULL << 39)) 585 | | ((bitField >> 14) & (7ULL << 42)) 586 | | ((bitField >> 15) & (7ULL << 45)); 587 | cbf__HeapWriteExplicit(tree, cbf__CreateNode(nodeID >> 2, depth - 2), 48ULL, bitData); 588 | 589 | // 4-bits 590 | bitField = (bitField & 0x0F0F0F0F0F0F0F0FULL) 591 | + ((bitField >> 4) & 0x0F0F0F0F0F0F0F0FULL); 592 | bitData = ((bitField >> 0) & (15ULL << 0)) 593 | | ((bitField >> 4) & (15ULL << 4)) 594 | | ((bitField >> 8) & (15ULL << 8)) 595 | | ((bitField >> 12) & (15ULL << 12)) 596 | | ((bitField >> 16) & (15ULL << 16)) 597 | | ((bitField >> 20) & (15ULL << 20)) 598 | | ((bitField >> 24) & (15ULL << 24)) 599 | | ((bitField >> 28) & (15ULL << 28)); 600 | cbf__HeapWriteExplicit(tree, cbf__CreateNode(nodeID >> 3, depth - 3), 32ULL, bitData); 601 | 602 | // 5-bits 603 | bitField = (bitField & 0x00FF00FF00FF00FFULL) 604 | + ((bitField >> 8) & 0x00FF00FF00FF00FFULL); 605 | bitData = ((bitField >> 0) & (31ULL << 0)) 606 | | ((bitField >> 11) & (31ULL << 5)) 607 | | ((bitField >> 22) & (31ULL << 10)) 608 | | ((bitField >> 33) & (31ULL << 15)); 609 | cbf__HeapWriteExplicit(tree, cbf__CreateNode(nodeID >> 4, depth - 4), 20ULL, bitData); 610 | 611 | // 6-bits 612 | bitField = (bitField & 0x0000FFFF0000FFFFULL) 613 | + ((bitField >> 16) & 0x0000FFFF0000FFFFULL); 614 | bitData = ((bitField >> 0) & (63ULL << 0)) 615 | | ((bitField >> 26) & (63ULL << 6)); 616 | cbf__HeapWriteExplicit(tree, cbf__CreateNode(nodeID >> 5, depth - 5), 12ULL, bitData); 617 | 618 | // 7-bits 619 | bitField = (bitField & 0x00000000FFFFFFFFULL) 620 | + ((bitField >> 32) & 0x00000000FFFFFFFFULL); 621 | bitData = bitField; 622 | cbf__HeapWriteExplicit(tree, cbf__CreateNode(nodeID >> 6, depth - 6), 7ULL, bitData); 623 | } 624 | CBF_BARRIER 625 | depth-= 6; 626 | 627 | // iterate over elements atomically 628 | while (--depth >= 0) { 629 | uint64_t minNodeID = 1ULL << depth; 630 | uint64_t maxNodeID = 2ULL << depth; 631 | 632 | CBF_PARALLEL_FOR 633 | for (uint64_t j = minNodeID; j < maxNodeID; ++j) { 634 | uint64_t x0 = cbf__HeapRead(tree, cbf__CreateNode(j << 1 , depth + 1)); 635 | uint64_t x1 = cbf__HeapRead(tree, cbf__CreateNode(j << 1 | 1, depth + 1)); 636 | 637 | cbf__HeapWrite(tree, cbf__CreateNode(j, depth), x0 + x1); 638 | } 639 | CBF_BARRIER 640 | } 641 | } 642 | 643 | 644 | /******************************************************************************* 645 | * Bitfield Ctor 646 | * 647 | */ 648 | CBFDEF cbf_BitField *cbf_Create(int64_t size) 649 | { 650 | cbf_BitField *cbf = (cbf_BitField *)CBF_MALLOC(sizeof(*cbf)); 651 | int64_t heapDepth = cbf__FindMSB(cbf__NextPowerOfTwo(size)); 652 | 653 | // the bitfield has to be at least 2^6 bits wide 654 | if (heapDepth < 6) heapDepth = 6; 655 | 656 | cbf->heap = (uint64_t *)CBF_MALLOC(cbf__HeapByteSize(heapDepth)); 657 | cbf->heap[0] = 1ULL << heapDepth; 658 | 659 | cbf_Clear(cbf); 660 | 661 | return cbf; 662 | } 663 | 664 | 665 | /******************************************************************************* 666 | * Buffer Dtor 667 | * 668 | */ 669 | CBFDEF void cbf_Release(cbf_BitField *cbf) 670 | { 671 | CBF_FREE(cbf->heap); 672 | CBF_FREE(cbf); 673 | } 674 | 675 | 676 | /******************************************************************************* 677 | * Update -- Split or merge each node in parallel 678 | * 679 | * The user provides an updater function that is responsible for 680 | * splitting or merging each node. 681 | * 682 | */ 683 | CBFDEF void 684 | cbf_Update(cbf_BitField *cbt, cbf_UpdateCallback updater, const void *userData) 685 | { 686 | CBF_PARALLEL_FOR 687 | for (int64_t handle = 0; handle < cbf_BitCount(cbt); ++handle) { 688 | updater(cbt, cbf_DecodeBit(cbt, handle), userData); 689 | } 690 | CBF_BARRIER 691 | 692 | cbf_Reduce(cbt); 693 | } 694 | 695 | 696 | /******************************************************************************* 697 | * Capacity -- Returns capacity of the bitfield in base 2 logarithm 698 | * 699 | */ 700 | CBFDEF int64_t cbf_HeapMaxDepth(const cbf_BitField *cbf) 701 | { 702 | return cbf__FindLSB(cbf->heap[0]); 703 | } 704 | CBFDEF int64_t cbf_Size(const cbf_BitField *cbf) 705 | { 706 | return 1LL << cbf_HeapMaxDepth(cbf); 707 | } 708 | 709 | 710 | /******************************************************************************* 711 | * BitCount -- Returns the number of bits set to one in the bit field 712 | * 713 | */ 714 | CBFDEF int64_t cbf_BitCount(const cbf_BitField *cbf) 715 | { 716 | return cbf__HeapRead(cbf, cbf__CreateNode(1u, 0)); 717 | } 718 | 719 | 720 | /******************************************************************************* 721 | * DecodeNode -- Returns the leaf node associated to index nodeID 722 | * 723 | * This is procedure is for iterating over the one-valued bits. 724 | * 725 | */ 726 | CBFDEF int64_t cbf_DecodeBit(const cbf_BitField *cbf, int64_t handle) 727 | { 728 | CBF_ASSERT(handle < cbf_BitCount(cbf) && "handle > NodeCount"); 729 | CBF_ASSERT(handle >= 0 && "handle < 0"); 730 | 731 | cbf__Node node = cbf__CreateNode(1u, 0); 732 | int64_t bitFieldSize = cbf_Size(cbf); 733 | 734 | while (node.id < bitFieldSize) { 735 | cbf__Node leftChildNode = cbf__CreateNode(node.id<<= 1u, ++node.depth); 736 | uint64_t heapValue = cbf__HeapRead(cbf, leftChildNode); 737 | uint64_t b = (uint64_t)handle < heapValue ? 0u : 1u; 738 | 739 | node.id|= b; 740 | handle-= heapValue * b; 741 | } 742 | 743 | return (node.id ^ bitFieldSize); 744 | } 745 | 746 | 747 | /******************************************************************************* 748 | * EncodeNode -- Returns the handle associated with the corresponding bitID 749 | * 750 | * This does the inverse of the DecodeNode routine. Note that this mapping 751 | * has the property that any bit set to 0 will be mapped to the ID of the next 752 | * bit set to one in the bit field. 753 | * 754 | */ 755 | CBFDEF int64_t cbf_EncodeBit(const cbf_BitField *cbf, int64_t bitID) 756 | { 757 | int64_t bitFieldSize = cbf_Size(cbf); 758 | cbf__Node node = cbf__CreateNode(bitID + bitFieldSize, cbf_HeapMaxDepth(cbf)); 759 | int64_t handle = 0; 760 | 761 | while (node.id > 1u) { 762 | cbf__Node sibling = cbf__LeftSiblingNode(node); 763 | uint64_t bitCount = cbf__HeapRead(cbf, sibling); 764 | 765 | handle+= (node.id & 1u) * bitCount; 766 | node = cbf__ParentNode(node); 767 | } 768 | 769 | return handle; 770 | } 771 | 772 | 773 | #undef CBF_ATOMIC 774 | #undef CBF_PARALLEL_FOR 775 | #undef CBF_BARRIER 776 | #endif 777 | 778 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | This folder contains the following programs: 2 | 3 | ### obj_to_ccm 4 | This program creates a serial mesh file format (labelled .ccm) from an input OBJ file. In turn, these .ccm files can be used as input for the subsequent programs. A list of .ccm meshes is provided in the `meshes/` folder. Note that the included OBJ parser supports the OBJ files provided in the OpenSubdiv repo, which sometimes includes (non-standard) semi-sharp crease tags. 5 | 6 | ### mesh_info 7 | This program is useful to display properties of a .ccm mesh file. 8 | 9 | ### subd_cpu 10 | This code provides a basic example to compute a subdivision in parallel on the CPU. It is compiled into two programs: `subd_cpu` and `bench_cpu`. By default, the former program subdivides a .ccm mesh and exports each subdivision level into several .obj files. The latter program runs the subdivision 100 times and displays timings. 11 | Typical usage is the following: 12 | ```sh 13 | subd_cpu pathToCcm.ccm maxSubdivisionDepth 1 14 | ``` 15 | where `maxSubdivisionDepth` is an integer value that controls the target subdivision level and 16 | the third argument is a flag to export the resulting subdivisions to .obj files (value should be 0 or 1). 17 | 18 | 19 | ### subd_gpu 20 | This code provides a basic example to compute a subdivision in parallel on the GPU using OpenGL shaders. The shaders require hardware support for the GLSL extension `GL_NV_shader_atomic_float`. The code is compiled into two programs: `subd_gpu` and `bench_gpu`. By default, the former program subdivides a .ccm mesh and exports each subdivision level into several .obj files. The latter program runs the subdivision 100 times and displays timings. 21 | Typical usage is the following: 22 | ```sh 23 | subd_cpu pathToCcm.ccm maxSubdivisionDepth 1 24 | ``` 25 | where `maxSubdivisionDepth` is an integer value that controls the target subdivision level and 26 | the third argument is a flag to export the resulting subdivisions to .obj files (value should be 0 or 1). 27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/glad/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2018 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * The master copy of khrplatform.h is maintained in the Khronos EGL 30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry 31 | * The last semantic modification to khrplatform.h was at commit ID: 32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 33 | * 34 | * Adopters may modify this file to suit their platform. Adopters are 35 | * encouraged to submit platform specific modifications to the Khronos 36 | * group so that they can be included in future versions of this file. 37 | * Please submit changes by filing pull requests or issues on 38 | * the EGL Registry repository linked above. 39 | * 40 | * 41 | * See the Implementer's Guidelines for information about where this file 42 | * should be located on your system and for more details of its use: 43 | * http://www.khronos.org/registry/implementers_guide.pdf 44 | * 45 | * This file should be included as 46 | * #include 47 | * by Khronos client API header files that use its types and defines. 48 | * 49 | * The types in khrplatform.h should only be used to define API-specific types. 50 | * 51 | * Types defined in khrplatform.h: 52 | * khronos_int8_t signed 8 bit 53 | * khronos_uint8_t unsigned 8 bit 54 | * khronos_int16_t signed 16 bit 55 | * khronos_uint16_t unsigned 16 bit 56 | * khronos_int32_t signed 32 bit 57 | * khronos_uint32_t unsigned 32 bit 58 | * khronos_int64_t signed 64 bit 59 | * khronos_uint64_t unsigned 64 bit 60 | * khronos_intptr_t signed same number of bits as a pointer 61 | * khronos_uintptr_t unsigned same number of bits as a pointer 62 | * khronos_ssize_t signed size 63 | * khronos_usize_t unsigned size 64 | * khronos_float_t signed 32 bit floating point 65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 67 | * nanoseconds 68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 69 | * khronos_boolean_enum_t enumerated boolean type. This should 70 | * only be used as a base type when a client API's boolean type is 71 | * an enum. Client APIs which use an integer or other type for 72 | * booleans cannot use this as the base type for their boolean. 73 | * 74 | * Tokens defined in khrplatform.h: 75 | * 76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 77 | * 78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 80 | * 81 | * Calling convention macros defined in this file: 82 | * KHRONOS_APICALL 83 | * KHRONOS_APIENTRY 84 | * KHRONOS_APIATTRIBUTES 85 | * 86 | * These may be used in function prototypes as: 87 | * 88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 89 | * int arg1, 90 | * int arg2) KHRONOS_APIATTRIBUTES; 91 | */ 92 | 93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) 94 | # define KHRONOS_STATIC 1 95 | #endif 96 | 97 | /*------------------------------------------------------------------------- 98 | * Definition of KHRONOS_APICALL 99 | *------------------------------------------------------------------------- 100 | * This precedes the return type of the function in the function prototype. 101 | */ 102 | #if defined(KHRONOS_STATIC) 103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the 104 | * header compatible with static linking. */ 105 | # define KHRONOS_APICALL 106 | #elif defined(_WIN32) 107 | # define KHRONOS_APICALL __declspec(dllimport) 108 | #elif defined (__SYMBIAN32__) 109 | # define KHRONOS_APICALL IMPORT_C 110 | #elif defined(__ANDROID__) 111 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 112 | #else 113 | # define KHRONOS_APICALL 114 | #endif 115 | 116 | /*------------------------------------------------------------------------- 117 | * Definition of KHRONOS_APIENTRY 118 | *------------------------------------------------------------------------- 119 | * This follows the return type of the function and precedes the function 120 | * name in the function prototype. 121 | */ 122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) 123 | /* Win32 but not WinCE */ 124 | # define KHRONOS_APIENTRY __stdcall 125 | #else 126 | # define KHRONOS_APIENTRY 127 | #endif 128 | 129 | /*------------------------------------------------------------------------- 130 | * Definition of KHRONOS_APIATTRIBUTES 131 | *------------------------------------------------------------------------- 132 | * This follows the closing parenthesis of the function prototype arguments. 133 | */ 134 | #if defined (__ARMCC_2__) 135 | #define KHRONOS_APIATTRIBUTES __softfp 136 | #else 137 | #define KHRONOS_APIATTRIBUTES 138 | #endif 139 | 140 | /*------------------------------------------------------------------------- 141 | * basic type definitions 142 | *-----------------------------------------------------------------------*/ 143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 144 | 145 | 146 | /* 147 | * Using 148 | */ 149 | #include 150 | typedef int32_t khronos_int32_t; 151 | typedef uint32_t khronos_uint32_t; 152 | typedef int64_t khronos_int64_t; 153 | typedef uint64_t khronos_uint64_t; 154 | #define KHRONOS_SUPPORT_INT64 1 155 | #define KHRONOS_SUPPORT_FLOAT 1 156 | 157 | #elif defined(__VMS ) || defined(__sgi) 158 | 159 | /* 160 | * Using 161 | */ 162 | #include 163 | typedef int32_t khronos_int32_t; 164 | typedef uint32_t khronos_uint32_t; 165 | typedef int64_t khronos_int64_t; 166 | typedef uint64_t khronos_uint64_t; 167 | #define KHRONOS_SUPPORT_INT64 1 168 | #define KHRONOS_SUPPORT_FLOAT 1 169 | 170 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 171 | 172 | /* 173 | * Win32 174 | */ 175 | typedef __int32 khronos_int32_t; 176 | typedef unsigned __int32 khronos_uint32_t; 177 | typedef __int64 khronos_int64_t; 178 | typedef unsigned __int64 khronos_uint64_t; 179 | #define KHRONOS_SUPPORT_INT64 1 180 | #define KHRONOS_SUPPORT_FLOAT 1 181 | 182 | #elif defined(__sun__) || defined(__digital__) 183 | 184 | /* 185 | * Sun or Digital 186 | */ 187 | typedef int khronos_int32_t; 188 | typedef unsigned int khronos_uint32_t; 189 | #if defined(__arch64__) || defined(_LP64) 190 | typedef long int khronos_int64_t; 191 | typedef unsigned long int khronos_uint64_t; 192 | #else 193 | typedef long long int khronos_int64_t; 194 | typedef unsigned long long int khronos_uint64_t; 195 | #endif /* __arch64__ */ 196 | #define KHRONOS_SUPPORT_INT64 1 197 | #define KHRONOS_SUPPORT_FLOAT 1 198 | 199 | #elif 0 200 | 201 | /* 202 | * Hypothetical platform with no float or int64 support 203 | */ 204 | typedef int khronos_int32_t; 205 | typedef unsigned int khronos_uint32_t; 206 | #define KHRONOS_SUPPORT_INT64 0 207 | #define KHRONOS_SUPPORT_FLOAT 0 208 | 209 | #else 210 | 211 | /* 212 | * Generic fallback 213 | */ 214 | #include 215 | typedef int32_t khronos_int32_t; 216 | typedef uint32_t khronos_uint32_t; 217 | typedef int64_t khronos_int64_t; 218 | typedef uint64_t khronos_uint64_t; 219 | #define KHRONOS_SUPPORT_INT64 1 220 | #define KHRONOS_SUPPORT_FLOAT 1 221 | 222 | #endif 223 | 224 | 225 | /* 226 | * Types that are (so far) the same on all platforms 227 | */ 228 | typedef signed char khronos_int8_t; 229 | typedef unsigned char khronos_uint8_t; 230 | typedef signed short int khronos_int16_t; 231 | typedef unsigned short int khronos_uint16_t; 232 | 233 | /* 234 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 235 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 236 | * to be the only LLP64 architecture in current use. 237 | */ 238 | #ifdef _WIN64 239 | typedef signed long long int khronos_intptr_t; 240 | typedef unsigned long long int khronos_uintptr_t; 241 | typedef signed long long int khronos_ssize_t; 242 | typedef unsigned long long int khronos_usize_t; 243 | #else 244 | typedef signed long int khronos_intptr_t; 245 | typedef unsigned long int khronos_uintptr_t; 246 | typedef signed long int khronos_ssize_t; 247 | typedef unsigned long int khronos_usize_t; 248 | #endif 249 | 250 | #if KHRONOS_SUPPORT_FLOAT 251 | /* 252 | * Float type 253 | */ 254 | typedef float khronos_float_t; 255 | #endif 256 | 257 | #if KHRONOS_SUPPORT_INT64 258 | /* Time types 259 | * 260 | * These types can be used to represent a time interval in nanoseconds or 261 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 262 | * of nanoseconds since some arbitrary system event (e.g. since the last 263 | * time the system booted). The Unadjusted System Time is an unsigned 264 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 265 | * may be either signed or unsigned. 266 | */ 267 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 268 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 269 | #endif 270 | 271 | /* 272 | * Dummy value used to pad enum types to 32 bits. 273 | */ 274 | #ifndef KHRONOS_MAX_ENUM 275 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 276 | #endif 277 | 278 | /* 279 | * Enumerated boolean type 280 | * 281 | * Values other than zero should be considered to be true. Therefore 282 | * comparisons should not be made against KHRONOS_TRUE. 283 | */ 284 | typedef enum { 285 | KHRONOS_FALSE = 0, 286 | KHRONOS_TRUE = 1, 287 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 288 | } khronos_boolean_enum_t; 289 | 290 | #endif /* __khrplatform_h_ */ 291 | -------------------------------------------------------------------------------- /examples/mesh_info.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define CBF_IMPLEMENTATION 8 | #include "ConcurrentBitField.h" 9 | #undef CBF_IMPLEMENTATION 10 | 11 | #define CC_IMPLEMENTATION 12 | #include "CatmullClark.h" 13 | 14 | #define LOG(fmt, ...) fprintf(stdout, fmt "\n", ##__VA_ARGS__); fflush(stdout); 15 | 16 | static void usage(const char *appname) 17 | { 18 | LOG("usage: %s path_to_ccm maxDepth", appname); 19 | } 20 | 21 | double ByteToGiByte(int64_t size) 22 | { 23 | return (double)size / (1 << 30); 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | int32_t maxDepth; 29 | cc_Mesh *mesh; 30 | int32_t nonQuadCount = 0; 31 | int32_t creaseCount = 0; 32 | int32_t boundaryCount = 0; 33 | 34 | if (argc < 3) { 35 | usage(argv[0]); 36 | return 0; 37 | } 38 | 39 | mesh = ccm_Load(argv[1]); 40 | maxDepth = atoi(argv[2]); 41 | 42 | // boundaries 43 | boundaryCount = 2 * ccm_EdgeCount(mesh) - ccm_HalfedgeCount(mesh); 44 | 45 | // count non quads 46 | for (int32_t faceID = 0; faceID < ccm_FaceCount(mesh); ++faceID) { 47 | const int32_t halfedgeID = ccm_FaceToHalfedgeID(mesh, faceID); 48 | int32_t halfedgeIt = ccm_HalfedgeNextID(mesh, halfedgeID); 49 | int32_t cycleLength = 1; 50 | 51 | while (halfedgeIt != halfedgeID) { 52 | ++cycleLength; 53 | halfedgeIt = ccm_HalfedgeNextID(mesh, halfedgeIt); 54 | } 55 | 56 | if (cycleLength != 4) 57 | ++nonQuadCount; 58 | } 59 | 60 | // creases 61 | for (int32_t edgeID = 0; edgeID < ccm_EdgeCount(mesh); ++edgeID) { 62 | if (ccm_CreaseSharpness(mesh, edgeID)) { 63 | ++creaseCount; 64 | } 65 | } 66 | creaseCount-= boundaryCount; 67 | 68 | LOG("(non Quads: %i; boundaries: %i; creases: %i)", 69 | nonQuadCount, 70 | boundaryCount, 71 | creaseCount); 72 | LOG("(UVs: %i)", ccm_UvCount(mesh)); 73 | 74 | for (int32_t depth = 0; depth <= maxDepth; ++depth) { 75 | LOG("depth %i: H= %i F= %i E= %i V= %i C= %i", 76 | depth, 77 | ccm_HalfedgeCountAtDepth(mesh, depth), 78 | ccm_FaceCountAtDepth(mesh, depth), 79 | ccm_EdgeCountAtDepth(mesh, depth), 80 | ccm_VertexCountAtDepth(mesh, depth), 81 | ccm_CreaseCountAtDepth(mesh, depth)); 82 | } 83 | 84 | for (int32_t depth = 0; depth <= maxDepth; ++depth) { 85 | int32_t Href = 0, Vref = 0, Fref = 0, Eref = 0, Cref = 0; 86 | 87 | for (int32_t d = 1; d <= depth; ++d) { 88 | Href+= ccm_HalfedgeCountAtDepth(mesh, d); 89 | Vref+= ccm_VertexCountAtDepth(mesh, d); 90 | Eref+= ccm_EdgeCountAtDepth(mesh, d); 91 | Fref+= ccm_FaceCountAtDepth(mesh, d); 92 | Cref+= ccm_CreaseCountAtDepth(mesh, d); 93 | } 94 | 95 | LOG("depth %i: Hcum= %i (ref: %i)\n" 96 | " Fcum= %i (ref: %i)\n" 97 | " Ecum= %i (ref: %i)\n" 98 | " Vcum= %i (ref: %i)\n" 99 | " Ccum= %i (ref: %i)\n", 100 | depth, 101 | ccs_CumulativeHalfedgeCountAtDepth(mesh, depth), 102 | Href, 103 | ccs_CumulativeFaceCountAtDepth(mesh, depth), 104 | Fref, 105 | ccs_CumulativeEdgeCountAtDepth(mesh, depth), 106 | Eref, 107 | ccs_CumulativeVertexCountAtDepth(mesh, depth), 108 | Vref, 109 | ccs_CumulativeCreaseCountAtDepth(mesh, depth), 110 | Cref); 111 | } 112 | 113 | ccm_Release(mesh); 114 | 115 | return 1; 116 | } 117 | -------------------------------------------------------------------------------- /examples/meshes/ArmorGuy.ccm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/HalfedgeCatmullClark/08f99cbb32203bbbc27fa970038a127986f6e6c0/examples/meshes/ArmorGuy.ccm -------------------------------------------------------------------------------- /examples/meshes/Bigguy.ccm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/HalfedgeCatmullClark/08f99cbb32203bbbc27fa970038a127986f6e6c0/examples/meshes/Bigguy.ccm -------------------------------------------------------------------------------- /examples/meshes/Bishop.ccm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/HalfedgeCatmullClark/08f99cbb32203bbbc27fa970038a127986f6e6c0/examples/meshes/Bishop.ccm -------------------------------------------------------------------------------- /examples/meshes/Car.ccm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/HalfedgeCatmullClark/08f99cbb32203bbbc27fa970038a127986f6e6c0/examples/meshes/Car.ccm -------------------------------------------------------------------------------- /examples/meshes/Imrod.ccm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/HalfedgeCatmullClark/08f99cbb32203bbbc27fa970038a127986f6e6c0/examples/meshes/Imrod.ccm -------------------------------------------------------------------------------- /examples/meshes/Monsterfrog.ccm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/HalfedgeCatmullClark/08f99cbb32203bbbc27fa970038a127986f6e6c0/examples/meshes/Monsterfrog.ccm -------------------------------------------------------------------------------- /examples/meshes/Rook.ccm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/HalfedgeCatmullClark/08f99cbb32203bbbc27fa970038a127986f6e6c0/examples/meshes/Rook.ccm -------------------------------------------------------------------------------- /examples/meshes/T-Rex.ccm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/HalfedgeCatmullClark/08f99cbb32203bbbc27fa970038a127986f6e6c0/examples/meshes/T-Rex.ccm -------------------------------------------------------------------------------- /examples/obj_to_ccm.c: -------------------------------------------------------------------------------- 1 | #define CC_IMPLEMENTATION 2 | #include "CatmullClark.h" 3 | 4 | #define CBF_IMPLEMENTATION 5 | #include "ConcurrentBitField.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef CC_ASSERT 12 | # include 13 | # define CC_ASSERT(x) assert(x) 14 | #endif 15 | 16 | #ifndef CC_LOG 17 | # include 18 | # define CC_LOG(format, ...) do { fprintf(stdout, format "\n", ##__VA_ARGS__); fflush(stdout); } while(0) 19 | #endif 20 | 21 | #ifndef CC_MALLOC 22 | # include 23 | # define CC_MALLOC(x) (malloc(x)) 24 | # define CC_FREE(x) (free(x)) 25 | #else 26 | # ifndef CC_FREE 27 | # error CC_MALLOC defined without CC_FREE 28 | # endif 29 | #endif 30 | 31 | #ifndef CC_MEMCPY 32 | # include 33 | # define CC_MEMCPY(dest, src, count) memcpy(dest, src, count) 34 | #endif 35 | 36 | #ifndef CC_MEMSET 37 | # include 38 | # define CC_MEMSET(ptr, value, num) memset(ptr, value, num) 39 | #endif 40 | 41 | #ifndef _OPENMP 42 | # ifndef CC_ATOMIC 43 | # define CC_ATOMIC 44 | # endif 45 | # ifndef CC_PARALLEL_FOR 46 | # define CC_PARALLEL_FOR 47 | # endif 48 | # ifndef CC_BARRIER 49 | # define CC_BARRIER 50 | # endif 51 | #else 52 | # if defined(_WIN32) 53 | # ifndef CC_ATOMIC 54 | # define CC_ATOMIC __pragma("omp atomic" ) 55 | # endif 56 | # ifndef CC_PARALLEL_FOR 57 | # define CC_PARALLEL_FOR __pragma("omp parallel for") 58 | # endif 59 | # ifndef CC_BARRIER 60 | # define CC_BARRIER __pragma("omp barrier") 61 | # endif 62 | # else 63 | # ifndef CC_ATOMIC 64 | # define CC_ATOMIC _Pragma("omp atomic" ) 65 | # endif 66 | # ifndef CC_PARALLEL_FOR 67 | # define CC_PARALLEL_FOR _Pragma("omp parallel for") 68 | # endif 69 | # ifndef CC_BARRIER 70 | # define CC_BARRIER _Pragma("omp barrier") 71 | # endif 72 | # endif 73 | #endif 74 | 75 | 76 | /******************************************************************************* 77 | * ComputeTwins -- Computes the twin of each half edge 78 | * 79 | * This routine is what effectively converts a traditional "indexed mesh" 80 | * into a halfedge mesh (in the case where all the primitives are the same). 81 | * 82 | */ 83 | typedef struct { 84 | int32_t halfedgeID; 85 | uint64_t hashID; 86 | } TwinComputationData; 87 | 88 | static int32_t 89 | BinarySearch( 90 | const TwinComputationData *array, 91 | int32_t arraySize, 92 | uint64_t hashID 93 | ) { 94 | int32_t a = 0, b = arraySize - 1; 95 | 96 | while (a <= b) { 97 | const int32_t c = (a + b) / 2; 98 | 99 | if (array[c].hashID < hashID) { 100 | a = c + 1; 101 | } else { 102 | b = c - 1; 103 | } 104 | } 105 | 106 | return (array[a].hashID == hashID) ? array[a].halfedgeID : -1; 107 | } 108 | 109 | static void 110 | SortTwinComputationData(TwinComputationData *array, uint32_t arraySize) 111 | { 112 | for (uint32_t d2 = 1u; d2 < arraySize; d2*= 2u) { 113 | for (uint32_t d1 = d2; d1 >= 1u; d1/= 2u) { 114 | const uint32_t mask = (0xFFFFFFFEu * d1); 115 | 116 | CC_PARALLEL_FOR 117 | for (uint32_t i = 0; i < (arraySize / 2); ++i) { 118 | const uint32_t i1 = ((i << 1) & mask) | (i & ~(mask >> 1)); 119 | const uint32_t i2 = i1 | d1; 120 | const TwinComputationData t1 = array[i1]; 121 | const TwinComputationData t2 = array[i2]; 122 | const TwinComputationData min = t1.hashID < t2.hashID ? t1 : t2; 123 | const TwinComputationData max = t1.hashID < t2.hashID ? t2 : t1; 124 | 125 | if ((i & d2) == 0) { 126 | array[i1] = min; 127 | array[i2] = max; 128 | } else { 129 | array[i1] = max; 130 | array[i2] = min; 131 | } 132 | } 133 | CC_BARRIER 134 | } 135 | } 136 | } 137 | 138 | static int32_t RoundUpToPowerOfTwo(int32_t x) 139 | { 140 | x--; 141 | x|= x >> 1; 142 | x|= x >> 2; 143 | x|= x >> 4; 144 | x|= x >> 8; 145 | x|= x >> 16; 146 | x++; 147 | 148 | return x; 149 | } 150 | 151 | static void ComputeTwins(cc_Mesh *mesh) 152 | { 153 | const int32_t halfedgeCount = ccm_HalfedgeCount(mesh); 154 | const int32_t vertexCount = ccm_VertexCount(mesh); 155 | const int32_t tableSize = RoundUpToPowerOfTwo(halfedgeCount); 156 | TwinComputationData *table = (TwinComputationData *)CC_MALLOC(tableSize * sizeof(*table)); 157 | 158 | CC_PARALLEL_FOR 159 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 160 | const int32_t nextID = ccm_HalfedgeNextID(mesh, halfedgeID); 161 | const int32_t v0 = ccm_HalfedgeVertexID(mesh, halfedgeID); 162 | const int32_t v1 = ccm_HalfedgeVertexID(mesh, nextID); 163 | 164 | table[halfedgeID].halfedgeID = halfedgeID; 165 | table[halfedgeID].hashID = (uint64_t)v0 + (uint64_t)vertexCount * v1; 166 | } 167 | CC_BARRIER 168 | 169 | CC_PARALLEL_FOR 170 | for (int32_t halfedgeID = halfedgeCount; halfedgeID < tableSize; ++halfedgeID) { 171 | table[halfedgeID].hashID = ~0ULL; 172 | } 173 | CC_BARRIER 174 | 175 | SortTwinComputationData(table, tableSize); 176 | 177 | CC_PARALLEL_FOR 178 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 179 | const int32_t nextID = ccm_HalfedgeNextID(mesh, halfedgeID); 180 | const int32_t v0 = ccm_HalfedgeVertexID(mesh, halfedgeID); 181 | const int32_t v1 = ccm_HalfedgeVertexID(mesh, nextID); 182 | const uint64_t hashID = (uint64_t)v1 + (uint64_t)vertexCount * v0; 183 | const int32_t twinID = BinarySearch(table, halfedgeCount - 1, hashID); 184 | 185 | mesh->halfedges[halfedgeID].twinID = twinID; 186 | } 187 | CC_BARRIER 188 | 189 | CC_FREE(table); 190 | } 191 | 192 | 193 | /******************************************************************************* 194 | * ComputeCreaseNeighbors -- Computes the neighbors of each crease 195 | * 196 | */ 197 | static void ComputeCreaseNeighbors(cc_Mesh *mesh) 198 | { 199 | const int32_t edgeCount = ccm_EdgeCount(mesh); 200 | 201 | CC_PARALLEL_FOR 202 | for (int32_t edgeID = 0; edgeID < edgeCount; ++edgeID) { 203 | const float sharpness = ccm_CreaseSharpness(mesh, edgeID); 204 | 205 | if (sharpness > 0.0f) { 206 | const int32_t halfedgeID = ccm_EdgeToHalfedgeID(mesh, edgeID); 207 | const int32_t nextID = ccm_HalfedgeNextID(mesh, halfedgeID); 208 | int32_t prevCreaseCount = 0; 209 | int32_t prevCreaseID = -1; 210 | int32_t nextCreaseCount = 0; 211 | int32_t nextCreaseID = -1; 212 | int32_t halfedgeIt; 213 | 214 | for (halfedgeIt = ccm_NextVertexHalfedgeID(mesh, halfedgeID); 215 | halfedgeIt != halfedgeID && halfedgeIt >= 0; 216 | halfedgeIt = ccm_NextVertexHalfedgeID(mesh, halfedgeIt)) { 217 | const float s = ccm_HalfedgeSharpness(mesh, halfedgeIt); 218 | 219 | if (s > 0.0f) { 220 | prevCreaseID = ccm_HalfedgeEdgeID(mesh, halfedgeIt); 221 | ++prevCreaseCount; 222 | } 223 | } 224 | 225 | if (prevCreaseCount == 1 && halfedgeIt == halfedgeID) { 226 | mesh->creases[edgeID].prevID = prevCreaseID; 227 | } 228 | 229 | if (ccm_HalfedgeSharpness(mesh, nextID) > 0.0f) { 230 | nextCreaseID = ccm_HalfedgeEdgeID(mesh, nextID); 231 | ++nextCreaseCount; 232 | } 233 | 234 | for (halfedgeIt = ccm_NextVertexHalfedgeID(mesh, nextID); 235 | halfedgeIt != nextID && halfedgeIt >= 0; 236 | halfedgeIt = ccm_NextVertexHalfedgeID(mesh, halfedgeIt)) { 237 | const float s = ccm_HalfedgeSharpness(mesh, halfedgeIt); 238 | const int32_t twinID = ccm_HalfedgeTwinID(mesh, halfedgeIt); 239 | 240 | // twin check is to avoid counting for halfedgeID 241 | if (s > 0.0f && twinID != halfedgeID) { 242 | nextCreaseID = ccm_HalfedgeEdgeID(mesh, halfedgeIt); 243 | ++nextCreaseCount; 244 | } 245 | } 246 | 247 | if (nextCreaseCount == 1 && halfedgeIt == nextID) { 248 | mesh->creases[edgeID].nextID = nextCreaseID; 249 | } 250 | } 251 | } 252 | CC_BARRIER 253 | } 254 | 255 | 256 | /******************************************************************************* 257 | * MakeBoundariesSharp -- Tags boundary edges as sharp 258 | * 259 | * Following the Pixar standard, we tag boundary halfedges as sharp. 260 | * See "Subdivision Surfaces in Character Animation" by DeRose et al. 261 | * Note that we tag the sharpness value to 16 as subdivision can't go deeper 262 | * without overflowing 32-bit integers. 263 | * 264 | */ 265 | static void MakeBoundariesSharp(cc_Mesh *mesh) 266 | { 267 | const int32_t edgeCount = ccm_EdgeCount(mesh); 268 | 269 | CC_PARALLEL_FOR 270 | for (int32_t edgeID = 0; edgeID < edgeCount; ++edgeID) { 271 | const int32_t halfedgeID = ccm_EdgeToHalfedgeID(mesh, edgeID); 272 | const int32_t twinID = ccm_HalfedgeTwinID(mesh, halfedgeID); 273 | 274 | if (twinID < 0) { 275 | mesh->creases[edgeID].sharpness = 16.0f; 276 | } 277 | } 278 | CC_BARRIER 279 | } 280 | 281 | 282 | /******************************************************************************* 283 | * LoadFaceMappings -- Computes the mappings for the faces of the mesh 284 | * 285 | */ 286 | static int32_t FaceScroll(int32_t id, int32_t direction, int32_t maxValue) 287 | { 288 | const int32_t n = maxValue - 1; 289 | const int32_t d = direction; 290 | const int32_t u = (d + 1) >> 1; // in [0, 1] 291 | const int32_t un = u * n; // precomputation 292 | 293 | return (id == un) ? (n - un) : (id + d); 294 | } 295 | 296 | static int32_t 297 | ScrollFaceHalfedgeID( 298 | int32_t halfedgeID, 299 | int32_t halfedgeFaceBeginID, 300 | int32_t halfedgeFaceEndID, 301 | int32_t direction 302 | ) { 303 | const int32_t faceHalfedgeCount = halfedgeFaceEndID - halfedgeFaceBeginID; 304 | const int32_t localHalfedgeID = halfedgeID - halfedgeFaceBeginID; 305 | const int32_t nextHalfedgeID = FaceScroll(localHalfedgeID, 306 | direction, 307 | faceHalfedgeCount); 308 | 309 | return halfedgeFaceBeginID + nextHalfedgeID; 310 | } 311 | 312 | static void LoadFaceMappings(cc_Mesh *mesh, const cbf_BitField *faceIterator) 313 | { 314 | const int32_t halfedgeCount = ccm_HalfedgeCount(mesh); 315 | const int32_t faceCount = cbf_BitCount(faceIterator) - 1; 316 | 317 | mesh->faceToHalfedgeIDs = (int32_t *)CC_MALLOC(sizeof(int32_t) * faceCount); 318 | mesh->faceCount = faceCount; 319 | 320 | CC_PARALLEL_FOR 321 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 322 | const int32_t tmp = cbf_EncodeBit(faceIterator, halfedgeID); 323 | const int32_t faceID = tmp - (cbf_GetBit(faceIterator, halfedgeID) ^ 1); 324 | 325 | mesh->halfedges[halfedgeID].faceID = faceID; 326 | } 327 | CC_BARRIER 328 | 329 | CC_PARALLEL_FOR 330 | for (int32_t faceID = 0; faceID < faceCount; ++faceID) { 331 | mesh->faceToHalfedgeIDs[faceID] = cbf_DecodeBit(faceIterator, faceID); 332 | } 333 | CC_BARRIER 334 | 335 | 336 | CC_PARALLEL_FOR 337 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 338 | const int32_t faceID = mesh->halfedges[halfedgeID].faceID; 339 | const int32_t beginID = cbf_DecodeBit(faceIterator, faceID); 340 | const int32_t endID = cbf_DecodeBit(faceIterator, faceID + 1); 341 | const int32_t nextID = ScrollFaceHalfedgeID(halfedgeID, beginID, endID, +1); 342 | const int32_t prevID = ScrollFaceHalfedgeID(halfedgeID, beginID, endID, -1); 343 | 344 | mesh->halfedges[halfedgeID].nextID = nextID; 345 | mesh->halfedges[halfedgeID].prevID = prevID; 346 | } 347 | CC_BARRIER 348 | } 349 | 350 | 351 | /******************************************************************************* 352 | * LoadEdgeMappings -- Computes the mappings for the edges of the mesh 353 | * 354 | * Catmull-Clark subdivision requires access to the edges of an input mesh. 355 | * Since we are dealing with a halfedge representation, we virtually 356 | * have to iterate the halfedges in a sparse way (an edge is a pair of 357 | * neighboring halfedges in the general case, except for boundary edges 358 | * where it only consists of a single halfedge). 359 | * This function builds a data-structure that allows to do just that: 360 | * for each halfedge pair, we only consider the one that has the largest 361 | * halfedgeID. This allows to treat boundary and regular edges seamlessly. 362 | * 363 | */ 364 | static void LoadEdgeMappings(cc_Mesh *mesh) 365 | { 366 | const int32_t halfedgeCount = ccm_HalfedgeCount(mesh); 367 | cc_Halfedge *halfedges = mesh->halfedges; 368 | cbf_BitField *edgeIterator = cbf_Create(halfedgeCount); 369 | int32_t edgeCount; 370 | 371 | CC_PARALLEL_FOR 372 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 373 | int32_t twinID = ccm_HalfedgeTwinID(mesh, halfedgeID); 374 | int32_t bitValue = halfedgeID > twinID ? 1 : 0; 375 | 376 | cbf_SetBit(edgeIterator, halfedgeID, bitValue); 377 | } 378 | CC_BARRIER 379 | 380 | cbf_Reduce(edgeIterator); 381 | edgeCount = cbf_BitCount(edgeIterator); 382 | 383 | mesh->edgeToHalfedgeIDs = (int32_t *)CC_MALLOC(sizeof(int32_t) * edgeCount); 384 | mesh->edgeCount = edgeCount; 385 | 386 | CC_PARALLEL_FOR 387 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 388 | const int32_t twinID = ccm_HalfedgeTwinID(mesh, halfedgeID); 389 | const int32_t bitID = cc__Max(halfedgeID, twinID); 390 | 391 | halfedges[halfedgeID].edgeID = cbf_EncodeBit(edgeIterator, bitID); 392 | } 393 | CC_BARRIER 394 | 395 | CC_PARALLEL_FOR 396 | for (int32_t edgeID = 0; edgeID < edgeCount; ++edgeID) { 397 | mesh->edgeToHalfedgeIDs[edgeID] = cbf_DecodeBit(edgeIterator, edgeID); 398 | } 399 | CC_BARRIER 400 | 401 | cbf_Release(edgeIterator); 402 | } 403 | 404 | 405 | /******************************************************************************* 406 | * LoadVertexHalfedges -- Computes an iterator over one halfedge per vertex 407 | * 408 | * Catmull-Clark subdivision requires access to the halfedges that surround 409 | * the vertices of an input mesh. 410 | * This function determines a halfedge ID that starts from a 411 | * given vertex within that vertex. We distinguish two cases: 412 | * 1- If the vertex is a lying on a boundary, we stored the halfedge that 413 | * allows for iteration in the forward sense. 414 | * 2- Otherwise we store the largest halfedge ID. 415 | * 416 | */ 417 | static void LoadVertexHalfedges(cc_Mesh *mesh) 418 | { 419 | const int32_t halfedgeCount = ccm_HalfedgeCount(mesh); 420 | const int32_t vertexCount = ccm_VertexCount(mesh); 421 | 422 | mesh->vertexToHalfedgeIDs = (int32_t *)CC_MALLOC(sizeof(int32_t) * vertexCount); 423 | 424 | CC_PARALLEL_FOR 425 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 426 | const int32_t vertexID = ccm_HalfedgeVertexID(mesh, halfedgeID); 427 | int32_t maxHalfedgeID = halfedgeID; 428 | int32_t boundaryHalfedgeID = halfedgeID; 429 | int32_t iterator; 430 | 431 | for (iterator = ccm_NextVertexHalfedgeID(mesh, halfedgeID); 432 | iterator >= 0 && iterator != halfedgeID; 433 | iterator = ccm_NextVertexHalfedgeID(mesh, iterator)) { 434 | maxHalfedgeID = cc__Max(maxHalfedgeID, iterator); 435 | boundaryHalfedgeID = iterator; 436 | } 437 | 438 | // affect max halfedge ID to vertex 439 | if /*boundary involved*/ (iterator < 0) { 440 | if (halfedgeID == boundaryHalfedgeID) { 441 | mesh->vertexToHalfedgeIDs[vertexID] = boundaryHalfedgeID; 442 | } 443 | } else { 444 | if (halfedgeID == maxHalfedgeID) { 445 | mesh->vertexToHalfedgeIDs[vertexID] = maxHalfedgeID; 446 | } 447 | } 448 | } 449 | CC_BARRIER 450 | } 451 | 452 | 453 | /******************************************************************************* 454 | * ObjReadFace -- Reads an OBJ face 455 | * 456 | * OBJ files can describe a face according to 4 different formats. 457 | * This function supports each format. It returns the number of vertices 458 | * read for the current face. 459 | * 460 | */ 461 | static int32_t ObjReadFace(const char *str, cc_Halfedge **halfedges) 462 | { 463 | const char *formats[] = {"%d/%*d/%*d%n", "%d//%*d%n", "%d/%*d%n", "%d%n"}; 464 | int32_t halfedgeCount = 0; 465 | int32_t i = 0, n = 0, v; 466 | 467 | for (int32_t formatID = 0; formatID < 4; ++formatID) { 468 | while (sscanf(&str[i], formats[formatID], &v, &n) == 1 && n > 0) { 469 | int32_t vt = 0; 470 | 471 | if (sscanf(&str[i], "%d/%d", &v, &vt) == 1) { 472 | vt = 1; 473 | } 474 | CC_ASSERT(v >= 1 && vt >= 1 && "cc: unsupported relative index"); 475 | 476 | if (halfedges != NULL) { 477 | (*halfedges)->twinID = -1; 478 | (*halfedges)->edgeID = -1; 479 | (*halfedges)->vertexID = v - 1; 480 | (*halfedges)->uvID = vt - 1; 481 | (*halfedges)+= 1; 482 | } 483 | 484 | i+= n; 485 | ++halfedgeCount; 486 | } 487 | } 488 | 489 | CC_ASSERT(halfedgeCount > 2); 490 | 491 | return halfedgeCount; 492 | } 493 | 494 | 495 | /******************************************************************************* 496 | * ObjReadVertex -- Reads an OBJ vertex 497 | * 498 | */ 499 | static int32_t ObjReadVertex(const char *buf, cc_VertexPoint **vertex) 500 | { 501 | float *v = (*vertex)->array; 502 | 503 | if (sscanf(buf, "%f %f %f", &v[0], &v[1], &v[2]) == 3) { 504 | ++(*vertex); 505 | 506 | return 1; 507 | } 508 | 509 | return 0; 510 | } 511 | 512 | 513 | /******************************************************************************* 514 | * ObjReadUv -- Reads an OBJ texture coordinate 515 | * 516 | */ 517 | static int32_t ObjReadUv(const char *buf, cc_VertexUv **uv) 518 | { 519 | float *vt = (*uv)->array; 520 | 521 | if (sscanf(buf, "%f %f", &vt[0], &vt[1]) == 2) { 522 | ++(*uv); 523 | 524 | return 1; 525 | } 526 | 527 | return 0; 528 | } 529 | 530 | 531 | /******************************************************************************* 532 | * ObjReadCrease -- Reads crease attribute 533 | * 534 | * This is a brute force approach: for each crease attribute, we iterate 535 | * over all half edges until we find those that should be sharpened. 536 | * 537 | */ 538 | static void 539 | ObjReadCrease( 540 | cc_Mesh *mesh, 541 | const char *buffer 542 | ) { 543 | const int32_t creaseCount = ccm_CreaseCount(mesh); 544 | int32_t v0, v1; 545 | float s; 546 | 547 | if (sscanf(buffer, "t crease 2/1/0 %i %i %f", &v0, &v1, &s) == 3) { 548 | for (int32_t edgeID = 0; edgeID < creaseCount; ++edgeID) { 549 | const int32_t halfedgeID = ccm_EdgeToHalfedgeID(mesh, edgeID); 550 | const int32_t nextID = ccm_HalfedgeNextID(mesh, halfedgeID); 551 | const int32_t hv0 = ccm_HalfedgeVertexID(mesh, halfedgeID); 552 | const int32_t hv1 = ccm_HalfedgeVertexID(mesh, nextID); 553 | const bool b1 = (hv0 == v0) || (hv0 == v1); 554 | const bool b2 = (hv1 == v0) || (hv1 == v1); 555 | 556 | if (b1 && b2) { 557 | mesh->creases[edgeID].sharpness = s; 558 | } 559 | } 560 | } 561 | } 562 | 563 | 564 | /******************************************************************************* 565 | * ObjLoadMeshData -- Loads an OBJ mesh into a halfedge mesh 566 | * 567 | */ 568 | static bool 569 | ObjLoadMeshData(FILE *stream, cc_Mesh *mesh, cbf_BitField *faceIterator) 570 | { 571 | cc_Halfedge *halfedges = mesh->halfedges; 572 | cc_VertexPoint *vertexPoints = mesh->vertexPoints; 573 | cc_VertexUv *uvs = mesh->uvs; 574 | int32_t halfedgeCounter = 0; 575 | int32_t vertexCounter = 0; 576 | int32_t uvCounter = 0; 577 | char buffer[1024]; 578 | 579 | cbf_SetBit(faceIterator, 0, 1u); 580 | 581 | while(fgets(buffer, sizeof(buffer), stream) != NULL) { 582 | if (buffer[0] == 'v') { 583 | if (buffer[1] == ' ') { 584 | vertexCounter+= ObjReadVertex(&buffer[2], &vertexPoints); 585 | } else if (buffer[1] == 't') { 586 | uvCounter+= ObjReadUv(&buffer[2], &uvs); 587 | } 588 | } else if (buffer[0] == 'f') { 589 | halfedgeCounter+= ObjReadFace(&buffer[2], &halfedges); 590 | cbf_SetBit(faceIterator, halfedgeCounter, 1u); 591 | } 592 | } 593 | 594 | if (halfedgeCounter != ccm_HalfedgeCount(mesh) 595 | || vertexCounter != ccm_VertexCount(mesh) 596 | || uvCounter != ccm_UvCount(mesh)) { 597 | return false; 598 | } 599 | 600 | cbf_Reduce(faceIterator); 601 | LoadFaceMappings(mesh, faceIterator); 602 | 603 | return true; 604 | } 605 | 606 | 607 | /******************************************************************************* 608 | * ObjLoadCreaseData -- Loads an OBJ crease mesh data into a halfedge mesh 609 | * 610 | */ 611 | static bool 612 | ObjLoadCreaseData(FILE *stream, cc_Mesh *mesh) 613 | { 614 | char buffer[1024]; 615 | 616 | while(fgets(buffer, sizeof(buffer), stream) != NULL) { 617 | ObjReadCrease(mesh, buffer); 618 | } 619 | 620 | return true; 621 | } 622 | 623 | 624 | /******************************************************************************* 625 | * ObjReadMeshSize -- Retrieves the amount of memory suitable for loading an OBJ mesh 626 | * 627 | * This routine returns the number of indexes and vertices stored in the file. 628 | * Returns false if the size is invalid. 629 | * 630 | */ 631 | static bool 632 | ObjReadMeshSize( 633 | FILE *stream, 634 | int32_t *halfedgeCount, 635 | int32_t *vertexCount, 636 | int32_t *uvCount 637 | ) { 638 | char buffer[1024]; 639 | 640 | (*halfedgeCount) = 0; 641 | (*vertexCount) = 0; 642 | (*uvCount) = 0; 643 | 644 | while(fgets(buffer, sizeof(buffer), stream) != NULL) { 645 | if (buffer[0] == 'v') { 646 | if (buffer[1] == ' ') { 647 | ++(*vertexCount); 648 | } else if (buffer[1] == 't') { 649 | ++(*uvCount); 650 | } 651 | } else if (buffer[0] == 'f') { 652 | (*halfedgeCount)+= ObjReadFace(&buffer[1], NULL); 653 | } 654 | } 655 | 656 | return ((*halfedgeCount) > 0 && (*vertexCount) >= 3); 657 | } 658 | 659 | 660 | /******************************************************************************* 661 | * ObjReadVertex -- Reads an OBJ file 662 | * 663 | * Returns NULL on failure. 664 | * 665 | */ 666 | CCDEF cc_Mesh *LoadObj(const char *filename) 667 | { 668 | int32_t halfedgeCount, vertexCount, uvCount; 669 | cbf_BitField *faceIterator; 670 | cc_Mesh *mesh; 671 | FILE *stream; 672 | 673 | stream = fopen(filename, "r"); 674 | if (!stream) { 675 | CC_LOG("cc: fopen failed"); 676 | 677 | return NULL; 678 | } 679 | 680 | CC_LOG("Parsing OBJ..."); 681 | if (!ObjReadMeshSize(stream, &halfedgeCount, &vertexCount, &uvCount)) { 682 | CC_LOG("cc: invalid OBJ file"); 683 | fclose(stream); 684 | 685 | return NULL; 686 | } 687 | 688 | CC_LOG("Allocating mesh..."); 689 | mesh = (cc_Mesh *)CC_MALLOC(sizeof(*mesh)); 690 | mesh->halfedgeCount = halfedgeCount; 691 | mesh->halfedges = (cc_Halfedge *)CC_MALLOC(sizeof(cc_Halfedge) * halfedgeCount); 692 | mesh->vertexCount = vertexCount; 693 | mesh->vertexPoints = (cc_VertexPoint *)CC_MALLOC(sizeof(cc_VertexPoint) * vertexCount); 694 | mesh->uvCount = uvCount; 695 | mesh->uvs = (cc_VertexUv *)CC_MALLOC(sizeof(cc_VertexUv) * uvCount); 696 | faceIterator = cbf_Create(halfedgeCount + 1); 697 | rewind(stream); 698 | 699 | CC_LOG("Loading mesh data..."); 700 | if (!ObjLoadMeshData(stream, mesh, faceIterator)) { 701 | CC_LOG("cc: failed to read OBJ data"); 702 | CC_FREE(mesh->halfedges); 703 | CC_FREE(mesh->vertexPoints); 704 | CC_FREE(mesh->uvs); 705 | CC_FREE(mesh); 706 | 707 | fclose(stream); 708 | 709 | return NULL; 710 | } 711 | 712 | CC_LOG("Computing twins..."); 713 | ComputeTwins(mesh); 714 | CC_LOG("Computing edge mappings..."); 715 | LoadEdgeMappings(mesh); 716 | CC_LOG("Computing vertex mappings..."); 717 | LoadVertexHalfedges(mesh); 718 | 719 | CC_LOG("Loading creases..."); 720 | if (true) { 721 | const int32_t creaseCount = ccm_EdgeCount(mesh); 722 | 723 | mesh->creases = (cc_Crease *)CC_MALLOC(sizeof(cc_Crease) * creaseCount); 724 | rewind(stream); 725 | 726 | CC_PARALLEL_FOR 727 | for (int32_t creaseID = 0; creaseID < creaseCount; ++creaseID) { 728 | mesh->creases[creaseID].nextID = creaseID; 729 | mesh->creases[creaseID].prevID = creaseID; 730 | mesh->creases[creaseID].sharpness = 0.0f; 731 | } 732 | CC_BARRIER 733 | 734 | if (!ObjLoadCreaseData(stream, mesh)) { 735 | CC_LOG("cc: failed to read OBJ crease data"); 736 | ccm_Release(mesh); 737 | fclose(stream); 738 | 739 | return NULL; 740 | } 741 | 742 | MakeBoundariesSharp(mesh); 743 | } 744 | 745 | fclose(stream); 746 | cbf_Release(faceIterator); 747 | 748 | CC_LOG("Computing crease neighbors..."); 749 | ComputeCreaseNeighbors(mesh); 750 | 751 | return mesh; 752 | } 753 | 754 | static void Usage(const char *appname) 755 | { 756 | CC_LOG("usage -- %s file1 file2 ...", appname); 757 | } 758 | 759 | 760 | int main(int argc, char **argv) 761 | { 762 | const int32_t meshCount = argc - 1; 763 | char buffer[1024]; 764 | 765 | if (meshCount == 0) { 766 | Usage(argv[0]); 767 | } 768 | 769 | for (int32_t meshID = 0; meshID < meshCount; ++meshID) { 770 | const char *file = argv[meshID + 1]; 771 | CC_LOG("Loading: %s", file); 772 | cc_Mesh *mesh = LoadObj(file); 773 | char *preFix, *postFix; 774 | 775 | memset(buffer, 0, sizeof(buffer)); 776 | memcpy(buffer, file, strlen(file)); 777 | preFix = strrchr(buffer, '/'); 778 | postFix = strrchr(buffer, '.'); 779 | 780 | if (postFix != NULL) { 781 | *postFix = '\0'; 782 | } 783 | 784 | if (preFix != NULL) { 785 | sprintf(buffer, "%s.ccm", preFix + 1); 786 | } else { 787 | sprintf(buffer, "%s.ccm", buffer); 788 | } 789 | CC_LOG("Output file: %s", buffer); 790 | 791 | ccm_Save(mesh, buffer); 792 | ccm_Release(mesh); 793 | 794 | mesh = ccm_Load(buffer); 795 | CC_LOG("V: %i", ccm_VertexCount(mesh)); 796 | CC_LOG("U: %i", ccm_UvCount(mesh)); 797 | CC_LOG("H: %i", ccm_HalfedgeCount(mesh)); 798 | CC_LOG("C: %i", ccm_CreaseCount(mesh)); 799 | CC_LOG("E: %i", ccm_EdgeCount(mesh)); 800 | CC_LOG("F: %i", ccm_FaceCount(mesh)); 801 | ccm_Release(mesh); 802 | } 803 | } 804 | -------------------------------------------------------------------------------- /examples/subd_cpu.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef _WIN32 8 | #include 9 | #endif 10 | 11 | #define LOG(fmt, ...) fprintf(stdout, fmt "\n", ##__VA_ARGS__); fflush(stdout); 12 | 13 | //#define CC_DISABLE_UV 14 | #define CC_IMPLEMENTATION 15 | #include "CatmullClark.h" 16 | 17 | 18 | /******************************************************************************* 19 | * ExportToObj -- Exports subd to the OBJ file format 20 | * 21 | */ 22 | static void 23 | ExportToObj( 24 | const cc_Subd *subd, 25 | int32_t depth, 26 | const char *filename 27 | ) { 28 | const cc_Mesh *cage = subd->cage; 29 | const int32_t vertexPointCount = ccm_VertexCountAtDepth(cage, depth); 30 | const int32_t faceCount = ccm_FaceCountAtDepth(cage, depth); 31 | FILE *pf = fopen(filename, "w"); 32 | 33 | // write vertices 34 | fprintf(pf, "# Vertices\n"); 35 | if (depth == 0) { 36 | const int32_t vertexUvCount = ccm_UvCount(cage); 37 | 38 | for (int32_t vertexID = 0; vertexID < vertexPointCount; ++vertexID) { 39 | const float *v = ccm_VertexPoint(cage, vertexID).array; 40 | 41 | fprintf(pf, "v %f %f %f\n", v[0], v[1], v[2]); 42 | } 43 | 44 | for (int32_t vertexID = 0; vertexID < vertexUvCount; ++vertexID) { 45 | const float *v = ccm_Uv(cage, vertexID).array; 46 | 47 | fprintf(pf, "vt %f %f\n", v[0], v[1]); 48 | } 49 | } else { 50 | const int32_t halfedgeCount = ccm_HalfedgeCountAtDepth(cage, depth); 51 | 52 | for (int32_t vertexID = 0; vertexID < vertexPointCount; ++vertexID) { 53 | const float *v = ccs_VertexPoint(subd, vertexID, depth).array; 54 | 55 | fprintf(pf, "v %f %f %f\n", v[0], v[1], v[2]); 56 | } 57 | 58 | #ifndef CC_DISABLE_UV 59 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 60 | const float *uv = ccs_HalfedgeVertexUv(subd, halfedgeID, depth).array; 61 | 62 | fprintf(pf, "vt %f %f\n", uv[0], uv[1]); 63 | } 64 | #endif 65 | } 66 | fprintf(pf, "\n"); 67 | 68 | // write topology 69 | fprintf(pf, "# Topology\n"); 70 | if (depth == 0) { 71 | for (int32_t faceID = 0; faceID < faceCount; ++faceID) { 72 | const int32_t halfEdgeID = ccm_FaceToHalfedgeID(cage, faceID); 73 | 74 | fprintf(pf, 75 | "f %i/%i", 76 | ccm_HalfedgeVertexID(cage, halfEdgeID) + 1, 77 | ccm_HalfedgeUvID(cage, halfEdgeID) + 1); 78 | 79 | for (int32_t halfEdgeIt = ccm_HalfedgeNextID(cage, halfEdgeID); 80 | halfEdgeIt != halfEdgeID; 81 | halfEdgeIt = ccm_HalfedgeNextID(cage, halfEdgeIt)) { 82 | fprintf(pf, 83 | " %i/%i", 84 | ccm_HalfedgeVertexID(cage, halfEdgeIt) + 1, 85 | ccm_HalfedgeUvID(cage, halfEdgeIt) + 1); 86 | } 87 | fprintf(pf, "\n"); 88 | } 89 | } else { 90 | for (int32_t faceID = 0; faceID < faceCount; ++faceID) { 91 | #ifndef CC_DISABLE_UV 92 | fprintf(pf, 93 | "f %i/%i %i/%i %i/%i %i/%i\n", 94 | ccs_HalfedgeVertexID(subd, 4 * faceID + 0, depth) + 1, 95 | 4 * faceID + 1, 96 | ccs_HalfedgeVertexID(subd, 4 * faceID + 1, depth) + 1, 97 | 4 * faceID + 2, 98 | ccs_HalfedgeVertexID(subd, 4 * faceID + 2, depth) + 1, 99 | 4 * faceID + 3, 100 | ccs_HalfedgeVertexID(subd, 4 * faceID + 3, depth) + 1, 101 | 4 * faceID + 4); 102 | #else 103 | fprintf(pf, 104 | "f %i %i %i %i\n", 105 | ccs_HalfedgeVertexID(subd, 4 * faceID + 0, depth) + 1, 106 | ccs_HalfedgeVertexID(subd, 4 * faceID + 1, depth) + 1, 107 | ccs_HalfedgeVertexID(subd, 4 * faceID + 2, depth) + 1, 108 | ccs_HalfedgeVertexID(subd, 4 * faceID + 3, depth) + 1); 109 | #endif 110 | } 111 | fprintf(pf, "\n"); 112 | } 113 | 114 | fclose(pf); 115 | } 116 | 117 | typedef struct { 118 | double min, max, median, mean; 119 | } BenchStats; 120 | 121 | static int CompareCallback(const void * a, const void * b) 122 | { 123 | if (*(double*)a > *(double*)b) { 124 | return 1; 125 | } else if (*(double*)a < *(double*)b) { 126 | return -1; 127 | } else { 128 | return 0; 129 | } 130 | } 131 | 132 | BenchStats Bench(void (*SubdCallback)(cc_Subd *subd), cc_Subd *subd) 133 | { 134 | #ifdef FLAG_BENCH 135 | const int32_t runCount = 100; 136 | #else 137 | const int32_t runCount = 1; 138 | #endif 139 | #ifdef _WIN32 140 | DWORD startTime, stopTime; 141 | #else 142 | struct timespec startTime, stopTime; 143 | #endif 144 | double *times = (double *)malloc(sizeof(*times) * runCount); 145 | double timesTotal = 0.0; 146 | BenchStats stats = {0.0, 0.0, 0.0, 0.0}; 147 | 148 | for (int32_t runID = 0; runID < runCount; ++runID) { 149 | double time = 0.0; 150 | 151 | #ifdef _WIN32 152 | startTime = GetTickCount(); 153 | #else 154 | clock_gettime(CLOCK_MONOTONIC, &startTime); 155 | #endif 156 | (*SubdCallback)(subd); 157 | #ifdef _WIN32 158 | stopTime = GetTickCount(); 159 | time = (stopTime - startTime) / 1e3; 160 | #else 161 | clock_gettime(CLOCK_MONOTONIC, &stopTime); 162 | 163 | time = (stopTime.tv_sec - startTime.tv_sec); 164 | time+= (stopTime.tv_nsec - startTime.tv_nsec) / 1000000000.0; 165 | #endif 166 | times[runID] = time; 167 | timesTotal+= time; 168 | } 169 | 170 | qsort(times, runCount, sizeof(times[0]), &CompareCallback); 171 | 172 | stats.min = times[0]; 173 | stats.max = times[runCount - 1]; 174 | stats.median = times[runCount / 2]; 175 | stats.mean = timesTotal / runCount; 176 | 177 | free(times); 178 | 179 | return stats; 180 | } 181 | 182 | int main(int argc, char **argv) 183 | { 184 | const char *filename = "./Kitchen_PUP.ccm"; 185 | int32_t maxDepth = 4; 186 | #ifdef FLAG_BENCH 187 | int32_t exportToObj = 0; 188 | #else 189 | int32_t exportToObj = 1; 190 | #endif 191 | cc_Mesh *cage = NULL; 192 | cc_Subd *subd = NULL; 193 | 194 | if (argc > 1) { 195 | filename = argv[1]; 196 | } 197 | 198 | if (argc > 2) { 199 | maxDepth = atoi(argv[2]); 200 | } 201 | 202 | if (argc > 3) { 203 | exportToObj = atoi(argv[3]); 204 | } 205 | 206 | cage = ccm_Load(filename); 207 | 208 | if (!cage) { 209 | return -1; 210 | } 211 | 212 | subd = ccs_Create(cage, maxDepth); 213 | 214 | if (!subd) { 215 | ccm_Release(cage); 216 | 217 | return -1; 218 | } 219 | 220 | LOG("Refining..."); 221 | { 222 | const BenchStats stats = Bench(&ccs_RefineCreases, subd); 223 | 224 | LOG("Creases -- median/mean/min/max (ms): %f / %f / %f / %f", 225 | stats.median * 1e3, 226 | stats.mean * 1e3, 227 | stats.min * 1e3, 228 | stats.max * 1e3); 229 | } 230 | 231 | { 232 | const BenchStats stats = Bench(&ccs_RefineHalfedges, subd); 233 | 234 | LOG("Halfedges -- median/mean/min/max (ms): %f / %f / %f / %f", 235 | stats.median * 1e3, 236 | stats.mean * 1e3, 237 | stats.min * 1e3, 238 | stats.max * 1e3); 239 | } 240 | 241 | { 242 | const BenchStats stats = Bench(&ccs_RefineVertexPoints_Scatter, subd); 243 | 244 | LOG("VertexPoints -- median/mean/min/max (ms): %f / %f / %f / %f", 245 | stats.median * 1e3, 246 | stats.mean * 1e3, 247 | stats.min * 1e3, 248 | stats.max * 1e3); 249 | } 250 | 251 | #ifndef CC_DISABLE_UV 252 | { 253 | const BenchStats stats = Bench(&ccs_RefineVertexUvs, subd); 254 | 255 | LOG("VertexUvs -- median/mean/min/max (ms): %f / %f / %f / %f", 256 | stats.median * 1e3, 257 | stats.mean * 1e3, 258 | stats.min * 1e3, 259 | stats.max * 1e3); 260 | } 261 | #endif 262 | 263 | if (exportToObj > 0) { 264 | char buffer[64]; 265 | 266 | LOG("Exporting..."); 267 | for (int32_t depth = 0; depth <= maxDepth; ++depth) { 268 | sprintf(buffer, "subd_%01i.obj", depth); 269 | 270 | ExportToObj(subd, depth, buffer); 271 | LOG("Level %i: done.", depth); 272 | } 273 | } 274 | 275 | LOG("All done!"); 276 | 277 | ccm_Release(cage); 278 | ccs_Release(subd); 279 | 280 | return 0; 281 | } 282 | -------------------------------------------------------------------------------- /examples/subd_gpu.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "glad/glad.h" 7 | #include "GLFW/glfw3.h" 8 | 9 | #define LOG(fmt, ...) fprintf(stdout, fmt "\n", ##__VA_ARGS__); fflush(stdout); 10 | 11 | #define CC_DISABLE_UV 12 | #define CC_IMPLEMENTATION 13 | #include "CatmullClark.h" 14 | 15 | #define DJ_OPENGL_IMPLEMENTATION 16 | #include "dj_opengl.h" 17 | 18 | #ifndef PATH_TO_SRC_DIRECTORY 19 | # define PATH_TO_SRC_DIRECTORY "./" 20 | #endif 21 | 22 | struct Window { 23 | const char *name; 24 | int32_t width, height; 25 | struct { 26 | int32_t major, minor; 27 | } glversion; 28 | GLFWwindow* handle; 29 | } g_window = { 30 | "Catmull Clark", 31 | 720, 720, 32 | {4, 5}, 33 | NULL 34 | }; 35 | 36 | enum { 37 | BUFFER_CAGE_VERTEX_TO_HALFEDGE, 38 | BUFFER_CAGE_EDGE_TO_HALFEDGE, 39 | BUFFER_CAGE_FACE_TO_HALFEDGE, 40 | BUFFER_CAGE_HALFEDGES, 41 | BUFFER_CAGE_CREASES, 42 | BUFFER_CAGE_VERTEX_POINTS, 43 | BUFFER_CAGE_VERTEX_UVS, 44 | BUFFER_CAGE_COUNTERS, 45 | BUFFER_SUBD_MAXDEPTH, 46 | BUFFER_SUBD_HALFEDGES, 47 | BUFFER_SUBD_CREASES, 48 | BUFFER_SUBD_VERTEX_POINTS, 49 | 50 | BUFFER_COUNT 51 | }; 52 | 53 | enum { 54 | PROGRAM_SUBD_CAGE_HALFEDGES, 55 | PROGRAM_SUBD_CAGE_CREASES, 56 | PROGRAM_SUBD_CAGE_FACE_POINTS, 57 | PROGRAM_SUBD_CAGE_EDGE_POINTS, 58 | PROGRAM_SUBD_CAGE_VERTEX_POINTS, 59 | PROGRAM_SUBD_CAGE_VERTEX_UVS, 60 | PROGRAM_SUBD_HALFEDGES, 61 | PROGRAM_SUBD_CREASES, 62 | PROGRAM_SUBD_FACE_POINTS, 63 | PROGRAM_SUBD_EDGE_POINTS, 64 | PROGRAM_SUBD_VERTEX_POINTS, 65 | PROGRAM_SUBD_VERTEX_UVS, 66 | 67 | PROGRAM_COUNT 68 | }; 69 | 70 | struct OpenGL { 71 | GLuint programs[PROGRAM_COUNT]; 72 | GLuint buffers[BUFFER_COUNT]; 73 | } g_gl = { 74 | {0}, 75 | {0} 76 | }; 77 | 78 | struct SubdManager { 79 | int32_t computeShaderLocalSize; 80 | } g_subd = { 81 | 5 82 | }; 83 | 84 | #define PATH_TO_SHADER_DIRECTORY PATH_TO_SRC_DIRECTORY "../glsl/" 85 | 86 | static void 87 | LoadCatmullClarkLibrary( 88 | djg_program *djgp, 89 | bool halfedgeWrite, 90 | bool creaseWrite, 91 | bool vertexWrite 92 | ) { 93 | djgp_push_string(djgp, "#extension GL_NV_shader_atomic_float: require\n"); 94 | djgp_push_string(djgp, "#extension GL_NV_shader_thread_shuffle: require\n"); 95 | #ifdef CC_DISABLE_UV 96 | djgp_push_string(djgp, "#define CC_DISABLE_UV\n"); 97 | #endif 98 | 99 | if (halfedgeWrite) { 100 | djgp_push_string(djgp, "#define CCS_HALFEDGE_WRITE\n"); 101 | } 102 | if (creaseWrite) { 103 | djgp_push_string(djgp, "#define CCS_CREASE_WRITE\n"); 104 | } 105 | if (vertexWrite) { 106 | djgp_push_string(djgp, "#define CCS_VERTEX_WRITE\n"); 107 | } 108 | 109 | djgp_push_string(djgp, 110 | "#define CC_LOCAL_SIZE_X %i\n", 111 | 1 << g_subd.computeShaderLocalSize); 112 | djgp_push_string(djgp, 113 | "#define CC_BUFFER_BINDING_CAGE_VERTEX_TO_HALFEDGE %i\n", 114 | BUFFER_CAGE_VERTEX_TO_HALFEDGE); 115 | djgp_push_string(djgp, 116 | "#define CC_BUFFER_BINDING_CAGE_EDGE_TO_HALFEDGE %i\n", 117 | BUFFER_CAGE_EDGE_TO_HALFEDGE); 118 | djgp_push_string(djgp, 119 | "#define CC_BUFFER_BINDING_CAGE_FACE_TO_HALFEDGE %i\n", 120 | BUFFER_CAGE_FACE_TO_HALFEDGE); 121 | djgp_push_string(djgp, 122 | "#define CC_BUFFER_BINDING_CAGE_HALFEDGE %i\n", 123 | BUFFER_CAGE_HALFEDGES); 124 | djgp_push_string(djgp, 125 | "#define CC_BUFFER_BINDING_CAGE_CREASE %i\n", 126 | BUFFER_CAGE_CREASES); 127 | djgp_push_string(djgp, 128 | "#define CC_BUFFER_BINDING_CAGE_VERTEX_POINT %i\n", 129 | BUFFER_CAGE_VERTEX_POINTS); 130 | djgp_push_string(djgp, 131 | "#define CC_BUFFER_BINDING_CAGE_UV %i\n", 132 | BUFFER_CAGE_VERTEX_UVS); 133 | djgp_push_string(djgp, 134 | "#define CC_BUFFER_BINDING_CAGE_COUNTERS %i\n", 135 | BUFFER_CAGE_COUNTERS); 136 | djgp_push_string(djgp, 137 | "#define CC_BUFFER_BINDING_SUBD_MAXDEPTH %i\n", 138 | BUFFER_SUBD_MAXDEPTH); 139 | djgp_push_string(djgp, 140 | "#define CC_BUFFER_BINDING_SUBD_HALFEDGE %i\n", 141 | BUFFER_SUBD_HALFEDGES); 142 | djgp_push_string(djgp, 143 | "#define CC_BUFFER_BINDING_SUBD_CREASE %i\n", 144 | BUFFER_SUBD_CREASES); 145 | djgp_push_string(djgp, 146 | "#define CC_BUFFER_BINDING_SUBD_VERTEX_POINT %i\n", 147 | BUFFER_SUBD_VERTEX_POINTS); 148 | 149 | djgp_push_file(djgp, PATH_TO_SHADER_DIRECTORY "CatmullClark_Scatter.glsl"); 150 | } 151 | 152 | static bool 153 | LoadCatmullClarkProgram( 154 | int32_t programID, 155 | const char *sourceFile, 156 | bool halfEdgeWrite, 157 | bool creaseWrite, 158 | bool vertexWrite 159 | ) { 160 | djg_program *djgp = djgp_create(); 161 | GLuint *glp = &g_gl.programs[programID]; 162 | 163 | LoadCatmullClarkLibrary(djgp, halfEdgeWrite, creaseWrite, vertexWrite); 164 | djgp_push_file(djgp, sourceFile); 165 | djgp_push_string(djgp, "#ifdef COMPUTE_SHADER\n#endif"); 166 | if (!djgp_to_gl(djgp, 450, false, true, glp)) { 167 | djgp_release(djgp); 168 | 169 | return false; 170 | } 171 | 172 | djgp_release(djgp); 173 | 174 | return glGetError() == GL_NO_ERROR; 175 | } 176 | 177 | bool LoadCageFacePointsProgram() 178 | { 179 | LOG("Loading {Program-Cage-Face-Points}"); 180 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_CageFacePoints_Scatter.glsl"; 181 | 182 | return LoadCatmullClarkProgram(PROGRAM_SUBD_CAGE_FACE_POINTS, srcFile, false, false, true); 183 | } 184 | 185 | bool LoadCageEdgePointsProgram() 186 | { 187 | LOG("Loading {Program-Cage-Edge-Points}"); 188 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_CageEdgePoints_Scatter.glsl"; 189 | 190 | return LoadCatmullClarkProgram(PROGRAM_SUBD_CAGE_EDGE_POINTS, srcFile, false, false, true); 191 | } 192 | 193 | bool LoadCageVertexPointsProgram() 194 | { 195 | LOG("Loading {Program-Cage-Vertex-Points}"); 196 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_CageVertexPoints_Scatter.glsl"; 197 | 198 | return LoadCatmullClarkProgram(PROGRAM_SUBD_CAGE_VERTEX_POINTS, srcFile, false, false, true); 199 | } 200 | 201 | bool LoadCageHalfedgeRefinementProgram() 202 | { 203 | LOG("Loading {Program-Refine-Cage-Halfedges}"); 204 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_RefineCageHalfedges.glsl"; 205 | 206 | return LoadCatmullClarkProgram(PROGRAM_SUBD_CAGE_HALFEDGES, srcFile, true, false, false); 207 | } 208 | 209 | bool LoadCageCreaseRefinementProgram() 210 | { 211 | LOG("Loading {Program-Refine-Cage-Creases}"); 212 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_RefineCageCreases.glsl"; 213 | 214 | return LoadCatmullClarkProgram(PROGRAM_SUBD_CAGE_CREASES, srcFile, false, true, false); 215 | } 216 | 217 | bool LoadFacePointsProgram() 218 | { 219 | LOG("Loading {Program-Face-Points}"); 220 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_FacePoints_Scatter.glsl"; 221 | 222 | return LoadCatmullClarkProgram(PROGRAM_SUBD_FACE_POINTS, srcFile, false, false, true); 223 | } 224 | 225 | bool LoadEdgeRefinementProgram() 226 | { 227 | LOG("Loading {Program-Edge-Points}"); 228 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_EdgePoints_Scatter.glsl"; 229 | 230 | return LoadCatmullClarkProgram(PROGRAM_SUBD_EDGE_POINTS, srcFile, false, false, true); 231 | } 232 | 233 | bool LoadVertexRefinementProgram() 234 | { 235 | LOG("Loading {Program-Vertex-Points}"); 236 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_VertexPoints_Scatter.glsl"; 237 | 238 | return LoadCatmullClarkProgram(PROGRAM_SUBD_VERTEX_POINTS, srcFile, false, false, true); 239 | } 240 | 241 | bool LoadHalfedgeRefinementProgram() 242 | { 243 | LOG("Loading {Program-Refine-Halfedges}"); 244 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_RefineHalfedges.glsl"; 245 | 246 | return LoadCatmullClarkProgram(PROGRAM_SUBD_HALFEDGES, srcFile, true, false, false); 247 | } 248 | 249 | bool LoadCreaseRefinementProgram() 250 | { 251 | LOG("Loading {Program-Refine-Creases}"); 252 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_RefineCreases.glsl"; 253 | 254 | return LoadCatmullClarkProgram(PROGRAM_SUBD_CREASES, srcFile, false, true, false); 255 | } 256 | 257 | #ifndef CC_DISABLE_UV 258 | bool LoadCageVertexUvRefinementProgram() 259 | { 260 | LOG("Loading {Program-Refine-Cage-Uvs}"); 261 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_RefineCageVertexUvs.glsl"; 262 | 263 | return LoadCatmullClarkProgram(PROGRAM_SUBD_CAGE_VERTEX_UVS, srcFile, true, false, false); 264 | } 265 | 266 | bool LoadVertexUvRefinementProgram() 267 | { 268 | LOG("Loading {Program-Refine-Vertex-Uvs}"); 269 | const char *srcFile = PATH_TO_SHADER_DIRECTORY "cc_RefineVertexUvs.glsl"; 270 | 271 | return LoadCatmullClarkProgram(PROGRAM_SUBD_VERTEX_UVS, srcFile, true, false, false); 272 | } 273 | #endif 274 | 275 | bool LoadPrograms() 276 | { 277 | bool success = true; 278 | 279 | if (success) success = LoadCageHalfedgeRefinementProgram(); 280 | if (success) success = LoadHalfedgeRefinementProgram(); 281 | if (success) success = LoadCageCreaseRefinementProgram(); 282 | if (success) success = LoadCreaseRefinementProgram(); 283 | if (success) success = LoadCageFacePointsProgram(); 284 | if (success) success = LoadCageEdgePointsProgram(); 285 | if (success) success = LoadCageVertexPointsProgram(); 286 | if (success) success = LoadFacePointsProgram(); 287 | if (success) success = LoadEdgeRefinementProgram(); 288 | if (success) success = LoadVertexRefinementProgram(); 289 | #ifndef CC_DISABLE_UV 290 | if (success) success = LoadCageVertexUvRefinementProgram(); 291 | if (success) success = LoadVertexUvRefinementProgram(); 292 | #endif 293 | 294 | return success; 295 | } 296 | #undef PATH_TO_SHADER_DIRECTORY 297 | 298 | GLuint 299 | LoadCatmullClarkBuffer( 300 | int32_t bufferID, 301 | GLsizeiptr bufferByteSize, 302 | const void *data, 303 | GLbitfield flags 304 | ) { 305 | GLuint *buffer = &g_gl.buffers[bufferID]; 306 | 307 | glGenBuffers(1, buffer); 308 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, *buffer); 309 | glBufferStorage(GL_SHADER_STORAGE_BUFFER, 310 | bufferByteSize, 311 | data, 312 | flags); 313 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 314 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bufferID, *buffer); 315 | 316 | return glGetError() == GL_NO_ERROR; 317 | } 318 | 319 | bool LoadCageVertexToHalfedgeIDsBuffer(const cc_Mesh *cage) 320 | { 321 | return LoadCatmullClarkBuffer(BUFFER_CAGE_VERTEX_TO_HALFEDGE, 322 | ccm_VertexCount(cage) * sizeof(int32_t), 323 | cage->vertexToHalfedgeIDs, 324 | 0); 325 | } 326 | 327 | bool LoadCageEdgeToHalfedgeIDsBuffer(const cc_Mesh *cage) 328 | { 329 | return LoadCatmullClarkBuffer(BUFFER_CAGE_EDGE_TO_HALFEDGE, 330 | ccm_EdgeCount(cage) * sizeof(int32_t), 331 | cage->edgeToHalfedgeIDs, 332 | 0); 333 | } 334 | 335 | bool LoadCageFaceToHalfedgeIDsBuffer(const cc_Mesh *cage) 336 | { 337 | return LoadCatmullClarkBuffer(BUFFER_CAGE_FACE_TO_HALFEDGE, 338 | ccm_FaceCount(cage) * sizeof(int32_t), 339 | cage->faceToHalfedgeIDs, 340 | 0); 341 | } 342 | 343 | bool LoadCageHalfedgeBuffer(const cc_Mesh *cage) 344 | { 345 | return LoadCatmullClarkBuffer(BUFFER_CAGE_HALFEDGES, 346 | sizeof(cc_Halfedge) * ccm_HalfedgeCount(cage), 347 | cage->halfedges, 348 | 0); 349 | } 350 | 351 | bool LoadCageCreaseBuffer(const cc_Mesh *cage) 352 | { 353 | return LoadCatmullClarkBuffer(BUFFER_CAGE_CREASES, 354 | sizeof(cc_Crease) * ccm_CreaseCount(cage), 355 | cage->creases, 356 | 0); 357 | } 358 | 359 | bool LoadCageVertexPointBuffer(const cc_Mesh *cage) 360 | { 361 | return LoadCatmullClarkBuffer(BUFFER_CAGE_VERTEX_POINTS, 362 | sizeof(cc_VertexPoint) * ccm_VertexCount(cage), 363 | cage->vertexPoints, 364 | 0); 365 | } 366 | 367 | bool LoadCageVertexUvBuffer(const cc_Mesh *cage) 368 | { 369 | if (ccm_UvCount(cage) > 0) { 370 | return LoadCatmullClarkBuffer(BUFFER_CAGE_VERTEX_UVS, 371 | sizeof(cc_VertexUv) * ccm_UvCount(cage), 372 | cage->uvs, 373 | 0); 374 | } else { 375 | return true; 376 | } 377 | } 378 | 379 | bool LoadSubdVertexPointBuffer(const cc_Subd *subd) 380 | { 381 | return LoadCatmullClarkBuffer(BUFFER_SUBD_VERTEX_POINTS, 382 | sizeof(cc_VertexPoint) * ccs_CumulativeVertexCount(subd), 383 | NULL, 384 | GL_MAP_READ_BIT); 385 | } 386 | 387 | bool LoadSubdHalfedgeBuffer(const cc_Subd *subd) 388 | { 389 | return LoadCatmullClarkBuffer(BUFFER_SUBD_HALFEDGES, 390 | sizeof(cc_Halfedge_SemiRegular) * ccs_CumulativeHalfedgeCount(subd), 391 | NULL, 392 | GL_MAP_READ_BIT); 393 | } 394 | 395 | bool LoadSubdCreaseBuffer(const cc_Subd *subd) 396 | { 397 | return LoadCatmullClarkBuffer(BUFFER_SUBD_CREASES, 398 | sizeof(cc_Crease) * ccs_CumulativeCreaseCount(subd), 399 | NULL, 400 | GL_MAP_READ_BIT); 401 | } 402 | 403 | bool LoadCageCounterBuffer(const cc_Mesh *cage) 404 | { 405 | const struct { 406 | int32_t vertexCount; 407 | int32_t halfedgeCount; 408 | int32_t edgeCount; 409 | int32_t faceCount; 410 | int32_t uvCount; 411 | } counters = { 412 | ccm_VertexCount(cage), 413 | ccm_HalfedgeCount(cage), 414 | ccm_EdgeCount(cage), 415 | ccm_FaceCount(cage), 416 | ccm_UvCount(cage) 417 | }; 418 | 419 | return LoadCatmullClarkBuffer(BUFFER_CAGE_COUNTERS, 420 | sizeof(counters), 421 | &counters, 422 | 0); 423 | } 424 | 425 | bool LoadSubdMaxDepthBuffer(const cc_Subd *subd) 426 | { 427 | const int32_t maxDepth = ccs_MaxDepth(subd); 428 | 429 | return LoadCatmullClarkBuffer(BUFFER_SUBD_MAXDEPTH, 430 | sizeof(maxDepth), 431 | &maxDepth, 432 | 0); 433 | } 434 | 435 | bool LoadBuffers(const cc_Subd *subd) 436 | { 437 | bool success = true; 438 | 439 | if (success) success = LoadCageVertexToHalfedgeIDsBuffer(subd->cage); 440 | if (success) success = LoadCageEdgeToHalfedgeIDsBuffer(subd->cage); 441 | if (success) success = LoadCageFaceToHalfedgeIDsBuffer(subd->cage); 442 | if (success) success = LoadCageHalfedgeBuffer(subd->cage); 443 | if (success) success = LoadCageCreaseBuffer(subd->cage); 444 | if (success) success = LoadCageVertexPointBuffer(subd->cage); 445 | if (success) success = LoadCageVertexUvBuffer(subd->cage); 446 | if (success) success = LoadCageCounterBuffer(subd->cage); 447 | if (success) success = LoadSubdVertexPointBuffer(subd); 448 | if (success) success = LoadSubdHalfedgeBuffer(subd); 449 | if (success) success = LoadSubdCreaseBuffer(subd); 450 | if (success) success = LoadSubdMaxDepthBuffer(subd); 451 | 452 | return success; 453 | } 454 | 455 | 456 | bool Load(const cc_Subd *subd) 457 | { 458 | bool success = true; 459 | 460 | if (success) success = LoadPrograms(); 461 | if (success) success = LoadBuffers(subd); 462 | 463 | return success; 464 | } 465 | 466 | void Release() 467 | { 468 | glDeleteBuffers(BUFFER_COUNT, g_gl.buffers); 469 | 470 | for (int i = 0; i < PROGRAM_COUNT; ++i) 471 | glDeleteProgram(g_gl.programs[i]); 472 | } 473 | 474 | 475 | void CageSubdivisionCommand(int32_t programID, int32_t threadCount) 476 | { 477 | const int32_t count = (threadCount >> g_subd.computeShaderLocalSize) + 1; 478 | 479 | glUseProgram(g_gl.programs[programID]); 480 | glDispatchCompute(count, 1, 1); 481 | glMemoryBarrier(GL_ALL_BARRIER_BITS); 482 | glUseProgram(0); 483 | } 484 | 485 | void SubdivisionCommand(int32_t programID, int32_t threadCount, int32_t depth) 486 | { 487 | const int32_t count = (threadCount >> g_subd.computeShaderLocalSize) + 1; 488 | const int32_t uniformLocation = 489 | glGetUniformLocation(g_gl.programs[programID], "u_Depth"); 490 | 491 | glUseProgram(g_gl.programs[programID]); 492 | glUniform1i(uniformLocation, depth); 493 | glDispatchCompute(count, 1, 1); 494 | glMemoryBarrier(GL_ALL_BARRIER_BITS); 495 | glUseProgram(0); 496 | } 497 | 498 | void RefineCageHalfedgesCommand(const cc_Subd *subd) 499 | { 500 | CageSubdivisionCommand(PROGRAM_SUBD_CAGE_HALFEDGES, 501 | ccm_HalfedgeCount(subd->cage)); 502 | } 503 | 504 | void RefineCageCreasesCommand(const cc_Subd *subd) 505 | { 506 | CageSubdivisionCommand(PROGRAM_SUBD_CAGE_CREASES, 507 | ccm_CreaseCount(subd->cage)); 508 | } 509 | 510 | #ifndef CC_DISABLE_UV 511 | void RefineCageVertexUvsCommand(const cc_Subd *subd) 512 | { 513 | CageSubdivisionCommand(PROGRAM_SUBD_CAGE_VERTEX_UVS, 514 | ccm_HalfedgeCount(subd->cage)); 515 | } 516 | #endif 517 | 518 | void RefineCageFacesCommand(const cc_Subd *subd) 519 | { 520 | CageSubdivisionCommand(PROGRAM_SUBD_CAGE_FACE_POINTS, 521 | ccm_HalfedgeCount(subd->cage)); 522 | } 523 | 524 | void RefineCageEdgesCommand(const cc_Subd *subd) 525 | { 526 | CageSubdivisionCommand(PROGRAM_SUBD_CAGE_EDGE_POINTS, 527 | ccm_HalfedgeCount(subd->cage)); 528 | } 529 | 530 | void RefineCageVerticesCommand(const cc_Subd *subd) 531 | { 532 | CageSubdivisionCommand(PROGRAM_SUBD_CAGE_VERTEX_POINTS, 533 | ccm_HalfedgeCount(subd->cage)); 534 | } 535 | 536 | void RefineHalfedgesCommand(const cc_Subd *subd, int32_t depth) 537 | { 538 | SubdivisionCommand(PROGRAM_SUBD_HALFEDGES, 539 | ccm_HalfedgeCountAtDepth(subd->cage, depth), 540 | depth); 541 | } 542 | 543 | void RefineCreasesCommand(const cc_Subd *subd, int32_t depth) 544 | { 545 | SubdivisionCommand(PROGRAM_SUBD_CREASES, 546 | ccm_CreaseCountAtDepth(subd->cage, depth), 547 | depth); 548 | } 549 | 550 | #ifndef CC_DISABLE_UV 551 | void RefineVertexUvsCommand(const cc_Subd *subd, int32_t depth) 552 | { 553 | SubdivisionCommand(PROGRAM_SUBD_VERTEX_UVS, 554 | ccm_HalfedgeCountAtDepth(subd->cage, depth), 555 | depth); 556 | } 557 | #endif 558 | 559 | void RefineFacesCommand(const cc_Subd *subd, int32_t depth) 560 | { 561 | SubdivisionCommand(PROGRAM_SUBD_FACE_POINTS, 562 | ccm_HalfedgeCountAtDepth(subd->cage, depth), 563 | depth); 564 | } 565 | 566 | void RefineEdgesCommand(const cc_Subd *subd, int32_t depth) 567 | { 568 | SubdivisionCommand(PROGRAM_SUBD_EDGE_POINTS, 569 | ccm_HalfedgeCountAtDepth(subd->cage, depth), 570 | depth); 571 | } 572 | 573 | void RefineVertexPointsCommand(const cc_Subd *subd, int32_t depth) 574 | { 575 | SubdivisionCommand(PROGRAM_SUBD_VERTEX_POINTS, 576 | ccm_HalfedgeCountAtDepth(subd->cage, depth), 577 | depth); 578 | } 579 | 580 | void RefineVertexPoints(const cc_Subd *subd) 581 | { 582 | glClearNamedBufferData(g_gl.buffers[BUFFER_SUBD_VERTEX_POINTS], 583 | GL_R32F, 584 | GL_RED, 585 | GL_FLOAT, 586 | NULL); 587 | RefineCageFacesCommand(subd); 588 | RefineCageEdgesCommand(subd); 589 | RefineCageVerticesCommand(subd); 590 | 591 | for (int32_t depth = 1; depth < ccs_MaxDepth(subd); ++depth) { 592 | RefineFacesCommand(subd, depth); 593 | RefineEdgesCommand(subd, depth); 594 | RefineVertexPointsCommand(subd, depth); 595 | } 596 | } 597 | 598 | void RefineHalfedges(const cc_Subd *subd) 599 | { 600 | RefineCageHalfedgesCommand(subd); 601 | 602 | for (int32_t depth = 1; depth < ccs_MaxDepth(subd); ++depth) { 603 | RefineHalfedgesCommand(subd, depth); 604 | } 605 | } 606 | 607 | void RefineCreases(const cc_Subd *subd) 608 | { 609 | RefineCageCreasesCommand(subd); 610 | 611 | for (int32_t depth = 1; depth < ccs_MaxDepth(subd); ++depth) { 612 | RefineCreasesCommand(subd, depth); 613 | } 614 | } 615 | 616 | #ifndef CC_DISABLE_UV 617 | void RefineVertexUvs(const cc_Subd *subd) 618 | { 619 | RefineCageVertexUvsCommand(subd); 620 | 621 | for (int32_t depth = 1; depth < ccs_MaxDepth(subd); ++depth) { 622 | RefineVertexUvsCommand(subd, depth); 623 | } 624 | } 625 | #endif 626 | 627 | typedef struct { 628 | double min, max, median, mean; 629 | } BenchStats; 630 | 631 | static int CompareCallback(const void * a, const void * b) 632 | { 633 | if (*(double*)a > *(double*)b) { 634 | return 1; 635 | } else if (*(double*)a < *(double*)b) { 636 | return -1; 637 | } else { 638 | return 0; 639 | } 640 | } 641 | 642 | BenchStats Bench(void (*SubdCallback)(const cc_Subd *subd), const cc_Subd *subd) 643 | { 644 | #ifdef FLAG_BENCH 645 | const int32_t runCount = 100; 646 | #else 647 | const int32_t runCount = 1; 648 | #endif 649 | double *times = (double *)malloc(sizeof(*times) * runCount); 650 | double timesTotal = 0.0; 651 | BenchStats stats = {0.0, 0.0, 0.0, 0.0}; 652 | djg_clock *clock = djgc_create(); 653 | 654 | for (int32_t runID = 0; runID < runCount; ++runID) { 655 | double cpuTime = 0.0, gpuTime = 0.0; 656 | 657 | glFinish(); 658 | djgc_start(clock); 659 | (*SubdCallback)(subd); 660 | djgc_stop(clock); 661 | glFinish(); 662 | djgc_ticks(clock, &cpuTime, &gpuTime); 663 | 664 | times[runID] = gpuTime; 665 | timesTotal+= gpuTime; 666 | } 667 | 668 | qsort(times, runCount, sizeof(times[0]), &CompareCallback); 669 | 670 | stats.min = times[0]; 671 | stats.max = times[runCount - 1]; 672 | stats.median = times[runCount / 2]; 673 | stats.mean = timesTotal / runCount; 674 | 675 | free(times); 676 | djgc_release(clock); 677 | 678 | return stats; 679 | } 680 | 681 | void GetHalfedges(cc_Subd *subd) 682 | { 683 | const GLuint *buffer = &g_gl.buffers[BUFFER_SUBD_HALFEDGES]; 684 | const cc_Halfedge_SemiRegular *halfedges = 685 | (cc_Halfedge_SemiRegular *)glMapNamedBuffer(*buffer, GL_READ_ONLY); 686 | 687 | memcpy(subd->halfedges, 688 | halfedges, 689 | ccs_CumulativeHalfedgeCount(subd) * sizeof(cc_Halfedge_SemiRegular)); 690 | 691 | glUnmapNamedBuffer(*buffer); 692 | } 693 | 694 | void GetVertices(cc_Subd *subd) 695 | { 696 | const GLuint *buffer = &g_gl.buffers[BUFFER_SUBD_VERTEX_POINTS]; 697 | const cc_VertexPoint *vertices = 698 | (cc_VertexPoint *)glMapNamedBuffer(*buffer, GL_READ_ONLY); 699 | 700 | memcpy(subd->vertexPoints, 701 | vertices, 702 | ccs_CumulativeVertexCount(subd) * sizeof(cc_VertexPoint)); 703 | 704 | glUnmapNamedBuffer(*buffer); 705 | } 706 | 707 | void GetCreases(cc_Subd *subd) 708 | { 709 | const GLuint *buffer = &g_gl.buffers[BUFFER_SUBD_CREASES]; 710 | const cc_Crease *creases = 711 | (cc_Crease *)glMapNamedBuffer(*buffer, GL_READ_ONLY); 712 | 713 | memcpy(subd->creases, 714 | creases, 715 | ccs_CumulativeCreaseCount(subd) * sizeof(cc_Crease)); 716 | 717 | glUnmapNamedBuffer(*buffer); 718 | } 719 | 720 | 721 | /******************************************************************************* 722 | * ExportToObj -- Exports subd to the OBJ file format 723 | * 724 | */ 725 | static void 726 | ExportToObj( 727 | const cc_Subd *subd, 728 | int32_t depth, 729 | const char *filename 730 | ) { 731 | const cc_Mesh *cage = subd->cage; 732 | const int32_t vertexPointCount = ccm_VertexCountAtDepth(cage, depth); 733 | const int32_t faceCount = ccm_FaceCountAtDepth(cage, depth); 734 | FILE *pf = fopen(filename, "w"); 735 | 736 | // write vertices 737 | fprintf(pf, "# Vertices\n"); 738 | if (depth == 0) { 739 | const int32_t vertexUvCount = ccm_UvCount(cage); 740 | 741 | for (int32_t vertexID = 0; vertexID < vertexPointCount; ++vertexID) { 742 | const float *v = ccm_VertexPoint(cage, vertexID).array; 743 | 744 | fprintf(pf, "v %f %f %f\n", v[0], v[1], v[2]); 745 | } 746 | 747 | for (int32_t vertexID = 0; vertexID < vertexUvCount; ++vertexID) { 748 | const float *v = ccm_Uv(cage, vertexID).array; 749 | 750 | fprintf(pf, "vt %f %f\n", v[0], v[1]); 751 | } 752 | } else { 753 | const int32_t halfedgeCount = ccm_HalfedgeCountAtDepth(cage, depth); 754 | 755 | for (int32_t vertexID = 0; vertexID < vertexPointCount; ++vertexID) { 756 | const float *v = ccs_VertexPoint(subd, vertexID, depth).array; 757 | 758 | fprintf(pf, "v %f %f %f\n", v[0], v[1], v[2]); 759 | } 760 | 761 | #ifndef CC_DISABLE_UV 762 | for (int32_t halfedgeID = 0; halfedgeID < halfedgeCount; ++halfedgeID) { 763 | const float *uv = ccs_HalfedgeVertexUv(subd, halfedgeID, depth).array; 764 | 765 | fprintf(pf, "vt %f %f\n", uv[0], uv[1]); 766 | } 767 | #endif 768 | } 769 | fprintf(pf, "\n"); 770 | 771 | // write topology 772 | fprintf(pf, "# Topology\n"); 773 | if (depth == 0) { 774 | for (int32_t faceID = 0; faceID < faceCount; ++faceID) { 775 | const int32_t halfEdgeID = ccm_FaceToHalfedgeID(cage, faceID); 776 | 777 | fprintf(pf, 778 | "f %i/%i", 779 | ccm_HalfedgeVertexID(cage, halfEdgeID) + 1, 780 | ccm_HalfedgeUvID(cage, halfEdgeID) + 1); 781 | 782 | for (int32_t halfEdgeIt = ccm_HalfedgeNextID(cage, halfEdgeID); 783 | halfEdgeIt != halfEdgeID; 784 | halfEdgeIt = ccm_HalfedgeNextID(cage, halfEdgeIt)) { 785 | fprintf(pf, 786 | " %i/%i", 787 | ccm_HalfedgeVertexID(cage, halfEdgeIt) + 1, 788 | ccm_HalfedgeUvID(cage, halfEdgeIt) + 1); 789 | } 790 | fprintf(pf, "\n"); 791 | } 792 | } else { 793 | for (int32_t faceID = 0; faceID < faceCount; ++faceID) { 794 | #ifndef CC_DISABLE_UV 795 | fprintf(pf, 796 | "f %i/%i %i/%i %i/%i %i/%i\n", 797 | ccs_HalfedgeVertexID(subd, 4 * faceID + 0, depth) + 1, 798 | 4 * faceID + 1, 799 | ccs_HalfedgeVertexID(subd, 4 * faceID + 1, depth) + 1, 800 | 4 * faceID + 2, 801 | ccs_HalfedgeVertexID(subd, 4 * faceID + 2, depth) + 1, 802 | 4 * faceID + 3, 803 | ccs_HalfedgeVertexID(subd, 4 * faceID + 3, depth) + 1, 804 | 4 * faceID + 4); 805 | #else 806 | fprintf(pf, 807 | "f %i %i %i %i\n", 808 | ccs_HalfedgeVertexID(subd, 4 * faceID + 0, depth) + 1, 809 | ccs_HalfedgeVertexID(subd, 4 * faceID + 1, depth) + 1, 810 | ccs_HalfedgeVertexID(subd, 4 * faceID + 2, depth) + 1, 811 | ccs_HalfedgeVertexID(subd, 4 * faceID + 3, depth) + 1); 812 | #endif 813 | } 814 | fprintf(pf, "\n"); 815 | } 816 | 817 | fclose(pf); 818 | } 819 | 820 | 821 | int main(int argc, char **argv) 822 | { 823 | const char *filename = "./ArmorGuy.ccm"; 824 | int32_t maxDepth = 4; 825 | #ifdef FLAG_BENCH 826 | int32_t exportToObj = 0; 827 | #else 828 | int32_t exportToObj = 1; 829 | #endif 830 | cc_Mesh *cage = NULL; 831 | cc_Subd *subd = NULL; 832 | 833 | if (argc > 1) { 834 | filename = argv[1]; 835 | } 836 | 837 | if (argc > 2) { 838 | maxDepth = atoi(argv[2]); 839 | } 840 | 841 | if (argc > 3) { 842 | exportToObj = atoi(argv[3]); 843 | } 844 | 845 | cage = ccm_Load(filename); 846 | 847 | if (!cage) { 848 | return -1; 849 | } 850 | 851 | subd = ccs_Create(cage, maxDepth); 852 | 853 | if (!subd) { 854 | ccm_Release(cage); 855 | 856 | return -1; 857 | } 858 | 859 | LOG("Loading {OpenGL Window}"); 860 | glfwInit(); 861 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, g_window.glversion.major); 862 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, g_window.glversion.minor); 863 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 864 | glfwWindowHint(GLFW_VISIBLE, GL_FALSE); 865 | #ifndef NDEBUG 866 | glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); 867 | #endif 868 | g_window.handle = glfwCreateWindow(g_window.width, 869 | g_window.height, 870 | g_window.name, 871 | NULL, NULL); 872 | if (g_window.handle == NULL) { 873 | LOG("=> Failure <="); 874 | glfwTerminate(); 875 | 876 | return -1; 877 | } 878 | glfwMakeContextCurrent(g_window.handle); 879 | 880 | // load OpenGL functions 881 | LOG("Loading {OpenGL Functions}"); 882 | if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { 883 | LOG("=> Failure <="); 884 | glfwTerminate(); 885 | 886 | return -1; 887 | } 888 | 889 | // initialize 890 | LOG("Loading {Demo}"); 891 | if (!Load(subd)) { 892 | LOG("=> Failure <="); 893 | glfwTerminate(); 894 | 895 | return -1; 896 | } 897 | 898 | // execute GPU kernels 899 | { 900 | const BenchStats stats = Bench(&RefineCreases, subd); 901 | 902 | LOG("Creases -- median/mean/min/max (ms): %f / %f / %f / %f", 903 | stats.median * 1e3, 904 | stats.mean * 1e3, 905 | stats.min * 1e3, 906 | stats.max * 1e3); 907 | } 908 | 909 | { 910 | const BenchStats stats = Bench(&RefineHalfedges, subd); 911 | 912 | LOG("Halfedges -- median/mean/min/max (ms): %f / %f / %f / %f", 913 | stats.median * 1e3, 914 | stats.mean * 1e3, 915 | stats.min * 1e3, 916 | stats.max * 1e3); 917 | } 918 | 919 | { 920 | const BenchStats stats = Bench(&RefineVertexPoints, subd); 921 | 922 | LOG("VertexPoints -- median/mean/min/max (ms): %f / %f / %f / %f", 923 | stats.median * 1e3, 924 | stats.mean * 1e3, 925 | stats.min * 1e3, 926 | stats.max * 1e3); 927 | } 928 | 929 | #ifndef CC_DISABLE_UV 930 | { 931 | const BenchStats stats = Bench(&RefineVertexUvs, subd); 932 | 933 | LOG("VertexUvs -- median/mean/min/max (ms): %f / %f / %f / %f", 934 | stats.median * 1e3, 935 | stats.mean * 1e3, 936 | stats.min * 1e3, 937 | stats.max * 1e3); 938 | } 939 | #endif 940 | 941 | if (exportToObj > 0) { 942 | LOG("Exporting..."); 943 | GetHalfedges(subd); 944 | GetVertices(subd); 945 | GetCreases(subd); 946 | 947 | for (int32_t depth = 0; depth <= ccs_MaxDepth(subd); ++depth) { 948 | char buf[64]; 949 | 950 | sprintf(buf, "subd_%01i_gpu.obj", depth); 951 | ExportToObj(subd, depth, buf); 952 | LOG("Level %i: done.", depth); 953 | } 954 | } 955 | 956 | LOG("All done!"); 957 | 958 | Release(); 959 | glfwTerminate(); 960 | ccm_Release(cage); 961 | ccs_Release(subd); 962 | 963 | return 0; 964 | } 965 | -------------------------------------------------------------------------------- /glsl/CatmullClark_Scatter.glsl: -------------------------------------------------------------------------------- 1 | struct cc_Halfedge { 2 | int twinID; 3 | int nextID; 4 | int prevID; 5 | int faceID; 6 | int edgeID; 7 | int vertexID; 8 | int uvID; 9 | }; 10 | 11 | struct cc_Halfedge_SemiRegular { 12 | int twinID; 13 | int edgeID; 14 | int vertexID; 15 | #ifndef CC_DISABLE_UV 16 | int uvID; 17 | #endif 18 | }; 19 | 20 | struct cc_Crease { 21 | int nextID; 22 | int prevID; 23 | float sharpness; 24 | }; 25 | 26 | 27 | // ----------------------------------------------------------------------------- 28 | // Buffers 29 | #ifndef CC_BUFFER_BINDING_CAGE_VERTEX_TO_HALFEDGE 30 | # error Unspecified Buffer Binding 31 | #endif 32 | #ifndef CC_BUFFER_BINDING_CAGE_EDGE_TO_HALFEDGE 33 | # error Unspecified Buffer Binding 34 | #endif 35 | #ifndef CC_BUFFER_BINDING_CAGE_FACE_TO_HALFEDGE 36 | # error Unspecified Buffer Binding 37 | #endif 38 | #ifndef CC_BUFFER_BINDING_CAGE_HALFEDGE 39 | # error User must specify the binding of the cage halfedge buffer 40 | #endif 41 | #ifndef CC_BUFFER_BINDING_CAGE_CREASE 42 | # error User must specify the binding of the cage crease buffer 43 | #endif 44 | #ifndef CC_BUFFER_BINDING_CAGE_VERTEX_POINT 45 | # error User must specify the binding of the cage vertex buffer 46 | #endif 47 | #ifndef CC_BUFFER_BINDING_CAGE_UV 48 | # error User must specify the binding of the cage uv buffer 49 | #endif 50 | #ifndef CC_BUFFER_BINDING_CAGE_COUNTERS 51 | # error User must specify the binding of the cage counter 52 | #endif 53 | #ifndef CC_BUFFER_BINDING_SUBD_MAXDEPTH 54 | # error User must specify the binding of the subd maxDepth buffer 55 | #endif 56 | #ifndef CC_BUFFER_BINDING_SUBD_HALFEDGE 57 | # error User must specify the binding of the subd halfedge buffer 58 | #endif 59 | #ifndef CC_BUFFER_BINDING_SUBD_VERTEX_POINT 60 | # error User must specify the binding of the subd vertex buffer 61 | #endif 62 | #ifndef CC_BUFFER_BINDING_SUBD_CREASE 63 | # error User must specify the binding of the subd crease buffer 64 | #endif 65 | 66 | layout(std430, binding = CC_BUFFER_BINDING_CAGE_VERTEX_TO_HALFEDGE) 67 | readonly buffer ccm_HalfedgeToVertexBuffer { 68 | int ccmu_VertexToHalfedgeIDs[]; 69 | }; 70 | 71 | layout(std430, binding = CC_BUFFER_BINDING_CAGE_EDGE_TO_HALFEDGE) 72 | readonly buffer ccm_EdgeToHalfedgeBuffer { 73 | int ccmu_EdgeToHalfedgeIDs[]; 74 | }; 75 | 76 | layout(std430, binding = CC_BUFFER_BINDING_CAGE_FACE_TO_HALFEDGE) 77 | readonly buffer ccm_FaceToHalfedgeBuffer { 78 | int ccmu_FaceToHalfedgeIDs[]; 79 | }; 80 | 81 | layout(std430, binding = CC_BUFFER_BINDING_CAGE_HALFEDGE) 82 | readonly buffer ccm_HalfedgeBuffer { 83 | cc_Halfedge ccmu_Halfedges[]; 84 | }; 85 | 86 | layout(std430, binding = CC_BUFFER_BINDING_CAGE_CREASE) 87 | readonly buffer ccm_CreaseBuffer { 88 | cc_Crease ccmu_Creases[]; 89 | }; 90 | 91 | layout(std430, binding = CC_BUFFER_BINDING_CAGE_VERTEX_POINT) 92 | readonly buffer ccm_VertexPointBuffer { 93 | float ccmu_VertexPoints[]; 94 | }; 95 | 96 | layout(std430, binding = CC_BUFFER_BINDING_CAGE_UV) 97 | readonly buffer ccm_UvBuffer { 98 | float ccmu_Uvs[]; 99 | }; 100 | 101 | layout(std430, binding = CC_BUFFER_BINDING_CAGE_COUNTERS) 102 | readonly buffer ccm_CounterBuffer { 103 | int ccmu_VertexCount; 104 | int ccmu_HalfedgeCount; 105 | int ccmu_EdgeCount; 106 | int ccmu_FaceCount; 107 | int ccmu_UvCount; 108 | }; 109 | 110 | layout(std430, binding = CC_BUFFER_BINDING_SUBD_MAXDEPTH) 111 | readonly buffer ccs_MaxDepthBuffer { 112 | int ccsu_MaxDepth; 113 | }; 114 | 115 | layout(std430, binding = CC_BUFFER_BINDING_SUBD_VERTEX_POINT) 116 | #ifndef CCS_VERTEX_WRITE 117 | readonly 118 | #endif 119 | buffer ccs_VertexPointBuffer { 120 | float ccsu_VertexPoints[]; 121 | }; 122 | 123 | layout(std430, binding = CC_BUFFER_BINDING_SUBD_HALFEDGE) 124 | #ifndef CCS_HALFEDGE_WRITE 125 | readonly 126 | #endif 127 | buffer ccs_HalfedgeBuffer { 128 | cc_Halfedge_SemiRegular ccsu_Halfedges[]; 129 | }; 130 | 131 | layout(std430, binding = CC_BUFFER_BINDING_SUBD_CREASE) 132 | #ifndef CCS_CREASE_WRITE 133 | readonly 134 | #endif 135 | buffer ccs_CreaseBuffer { 136 | cc_Crease ccsu_Creases[]; 137 | }; 138 | 139 | 140 | // ----------------------------------------------------------------------------- 141 | 142 | // mesh queries 143 | int ccm_FaceCount(); 144 | int ccm_EdgeCount(); 145 | int ccm_HalfedgeCount(); 146 | int ccm_CreaseCount(); 147 | int ccm_VertexCount(); 148 | int ccm_UvCount(); 149 | 150 | // counts at a given Catmull-Clark subdivision depth 151 | int ccm_HalfedgeCountAtDepth(int depth); 152 | int ccm_FaceCountAtDepth (int depth); 153 | int ccm_FaceCountAtDepth_Fast(int depth); 154 | int ccm_EdgeCountAtDepth (int depth); 155 | int ccm_EdgeCountAtDepth_Fast(int depth); 156 | int ccm_VertexCountAtDepth (int depth); 157 | int ccm_VertexCountAtDepth_Fast(int depth); 158 | 159 | // data-access (O(1)) 160 | int ccm_HalfedgeTwinID(int halfedgeID); 161 | int ccm_HalfedgePrevID(int halfedgeID); 162 | int ccm_HalfedgeNextID(int halfedgeID); 163 | int ccm_HalfedgeFaceID(int halfedgeID); 164 | int ccm_HalfedgeEdgeID(int halfedgeID); 165 | int ccm_HalfedgeVertexID(int halfedgeID); 166 | int ccm_HalfedgeUvID(int halfedgeID); 167 | float ccm_HalfedgeSharpnnes(int halfedgeID); 168 | vec3 ccm_HalfedgeVertexPoint(int halfedgeID); 169 | vec2 ccm_HalfedgeVertexUv(int halfedgeID); 170 | int ccm_CreaseNextID(int edgeID); 171 | int ccm_CreasePrevID(int edgeID); 172 | float ccm_CreaseSharpness(int edgeID); 173 | vec3 ccm_VertexPoint(int vertexID); 174 | vec2 ccm_Uv(int uvID); 175 | int ccm_HalfedgeNextID_Quad(int halfedgeID); 176 | int ccm_HalfedgePrevID_Quad(int halfedgeID); 177 | int ccm_HalfedgeFaceID_Quad(int halfedgeID); 178 | 179 | // (vertex, edge, face) -> halfedge mappings (O(1)) 180 | int ccm_VertexToHalfedgeID(int vertexID); 181 | int ccm_EdgeToHalfedgeID(int edgeID); 182 | int ccm_FaceToHalfedgeID(int faceID); 183 | int ccm_FaceToHalfedgeID_Quad(int faceID); 184 | 185 | // halfedge remappings (O(1)) 186 | int ccm_NextVertexHalfedgeID(int halfedgeID); 187 | int ccm_PrevVertexHalfedgeID(int halfedgeID); 188 | 189 | // subd queries 190 | int ccs_MaxDepth(); 191 | int ccs_CumulativeFaceCount(); 192 | int ccs_CumulativeEdgeCount(); 193 | int ccs_CumulativeCreaseCount(); 194 | int ccs_CumulativeVertexCount(); 195 | int ccs_CumulativeHalfedgeCount(); 196 | int ccs_CumulativeFaceCountAtDepth(int depth); 197 | int ccs_CumulativeEdgeCountAtDepth(int depth); 198 | int ccs_CumulativeCreaseCountAtDepth(int depth); 199 | int ccs_CumulativeVertexCountAtDepth(int depth); 200 | int ccs_CumulativeHalfedgeCountAtDepth(int depth); 201 | 202 | // O(1) data-access 203 | int ccs_HalfedgeTwinID(int halfedgeID, int depth); 204 | int ccs_HalfedgeNextID(int halfedgeID, int depth); 205 | int ccs_HalfedgePrevID(int halfedgeID, int depth); 206 | int ccs_HalfedgeFaceID(int halfedgeID, int depth); 207 | int ccs_HalfedgeEdgeID(int halfedgeID, int depth); 208 | int ccs_HalfedgeVertexID(int halfedgeID, int depth); 209 | vec3 ccs_HalfedgeVertexPoint(int halfedgeID, int depth); 210 | #ifndef CC_DISABLE_UV 211 | vec2 ccs_HalfedgeVertexUv(int halfedgeID, int depth); 212 | #endif 213 | int ccs_CreaseNextID_Fast(int edgeID, int depth); 214 | int ccs_CreaseNextID (int edgeID, int depth); 215 | int ccs_CreasePrevID_Fast(int edgeID, int depth); 216 | int ccs_CreasePrevID (int edgeID, int depth); 217 | float ccs_CreaseSharpness_Fast(int edgeID, int depth); 218 | float ccs_CreaseSharpness (int edgeID, int depth); 219 | float ccs_HalfedgeSharpness(int halfedgeID, int depth); 220 | vec3 ccs_VertexPoint(int vertexID, int depth); 221 | 222 | // halfedge remapping 223 | int ccs_NextVertexHalfedgeID(int halfedgeID, int depth); 224 | int ccs_PrevVertexHalfedgeID(int halfedgeID, int depth); 225 | 226 | // (vertex, edge) -> halfedge mappings 227 | int ccs_VertexToHalfedgeID(int vertexID, int depth); 228 | int ccs_EdgeToHalfedgeID(int edgeID, int depth); 229 | int ccs_FaceToHalfedgeID(int edgeID, int depth); 230 | 231 | // halfedge normal 232 | vec3 ccs_HalfedgeNormal_Fast(int halfedgeID); 233 | vec3 ccs_HalfedgeNormal(int halfedgeID); 234 | 235 | 236 | 237 | // ----------------------------------------------------------------------------- 238 | // ----------------------------------------------------------------------------- 239 | // ----------------------------------------------------------------------------- 240 | 241 | 242 | /******************************************************************************* 243 | * UV Encoding / Decoding routines 244 | * 245 | */ 246 | vec2 cc__DecodeUv(int uvEncoded) 247 | { 248 | const uint tmp = uint(uvEncoded); 249 | const vec2 uv = vec2( 250 | ((tmp >> 0) & 0xFFFFu) / 65535.0f, 251 | ((tmp >> 16) & 0xFFFFu) / 65535.0f 252 | ); 253 | 254 | return uv; 255 | } 256 | 257 | int cc__EncodeUv(vec2 uv) 258 | { 259 | const uint u = uint(round(uv[0] * 65535.0f)); 260 | const uint v = uint(round(uv[1] * 65535.0f)); 261 | const uint tmp = ((u & 0xFFFFu) | ((v & 0xFFFFu) << 16)); 262 | 263 | return int(tmp); 264 | } 265 | 266 | 267 | /******************************************************************************* 268 | * FaceCount -- Returns the number of faces 269 | * 270 | */ 271 | int ccm_FaceCount() 272 | { 273 | return ccmu_FaceCount; 274 | } 275 | 276 | 277 | /******************************************************************************* 278 | * EdgeCount -- Returns the number of edges 279 | * 280 | */ 281 | int ccm_EdgeCount() 282 | { 283 | return ccmu_EdgeCount; 284 | } 285 | 286 | 287 | /******************************************************************************* 288 | * HalfedgeCount -- Returns the number of half edges 289 | * 290 | */ 291 | int ccm_HalfedgeCount() 292 | { 293 | return ccmu_HalfedgeCount; 294 | } 295 | 296 | 297 | /******************************************************************************* 298 | * CreaseCount -- Returns the number of creases 299 | * 300 | */ 301 | int ccm_CreaseCount() 302 | { 303 | return ccm_EdgeCount(); 304 | } 305 | 306 | 307 | /******************************************************************************* 308 | * VertexCount -- Returns the number of vertices 309 | * 310 | */ 311 | int ccm_VertexCount() 312 | { 313 | return ccmu_VertexCount; 314 | } 315 | 316 | 317 | /******************************************************************************* 318 | * UvCount -- Returns the number of uvs 319 | * 320 | */ 321 | int ccm_UvCount() 322 | { 323 | return ccmu_UvCount; 324 | } 325 | 326 | 327 | /******************************************************************************* 328 | * FaceCountAtDepth -- Returns the number of faces at a given subdivision depth 329 | * 330 | * The number of faces follows the rule 331 | * F^{d+1} = H^d 332 | * Therefore, the number of half edges at a given subdivision depth d>= 0 is 333 | * F^d = 4^{d - 1} H^0, 334 | * where H0 denotes the number of half-edges of the control cage. 335 | * 336 | */ 337 | int ccm_FaceCountAtDepth_Fast(int depth) 338 | { 339 | const int H0 = ccm_HalfedgeCount(); 340 | 341 | return (H0 << (2 * (depth - 1))); 342 | } 343 | 344 | int ccm_FaceCountAtDepth(int depth) 345 | { 346 | if (depth == 0) { 347 | return ccm_FaceCount(); 348 | } else { 349 | return ccm_FaceCountAtDepth_Fast(depth); 350 | } 351 | } 352 | 353 | 354 | /******************************************************************************* 355 | * EdgeCountAtDepth -- Returns the number of edges at a given subdivision depth 356 | * 357 | * The number of edges follows the rule 358 | * E^{d+1} = 2 E^d + H^d 359 | * Therefore, the number of edges at a given subdivision depth d>= 0 is 360 | * E^d = 2^{d - 1} (2 E^0 + (2^d - 1) H^0), 361 | * where H0 and E0 respectively denote the number of half-edges and edges 362 | * of the control cage. 363 | * 364 | */ 365 | int ccm_EdgeCountAtDepth_Fast(int depth) 366 | { 367 | const int E0 = ccm_EdgeCount(); 368 | const int H0 = ccm_HalfedgeCount(); 369 | const int tmp = ~(0xFFFFFFFF << depth); // (2^d - 1) 370 | 371 | return ((E0 << 1) + (tmp * H0)) << (depth - 1); 372 | } 373 | 374 | int ccm_EdgeCountAtDepth(int depth) 375 | { 376 | if (depth == 0) { 377 | return ccm_EdgeCount(); 378 | } else { 379 | return ccm_EdgeCountAtDepth_Fast(depth); 380 | } 381 | } 382 | 383 | 384 | /******************************************************************************* 385 | * HalfedgeCountAtDepth -- Returns the number of half edges at a given subd depth 386 | * 387 | * The number of half edges is multiplied by 4 at each subdivision step. 388 | * Therefore, the number of half edges at a given subdivision depth d>= 0 is 389 | * 4^d H0, 390 | * where H0 denotes the number of half-edges of the control cage. 391 | * 392 | */ 393 | int ccm_HalfedgeCountAtDepth(int depth) 394 | { 395 | const int H0 = ccm_HalfedgeCount(); 396 | 397 | return H0 << (depth << 1); 398 | } 399 | 400 | 401 | /******************************************************************************* 402 | * CreaseCountAtDepth -- Returns the number of creases at a given subd depth 403 | * 404 | * The number of creases is multiplied by 2 at each subdivision step. 405 | * Therefore, the number of halfedges at a given subdivision depth d>= 0 is 406 | * 2^d C0, 407 | * where C0 denotes the number of creases of the control cage. 408 | * 409 | */ 410 | int ccm_CreaseCountAtDepth(int depth) 411 | { 412 | const int C0 = ccm_CreaseCount(); 413 | 414 | return C0 << depth; 415 | } 416 | 417 | 418 | /******************************************************************************* 419 | * VertexCountAtDepth -- Returns the number of vertices at a given subd depth 420 | * 421 | * The number of vertices follows the rule 422 | * V^{d+1} = V^d + E^d + F^d 423 | * For a quad mesh, the number of vertices at a given subdivision depth d>= 0 is 424 | * V^d = V0 + (2^{d-1} - 1)E0 + (2^{d-1} - 1)^2F0, 425 | * where: 426 | * - V0 denotes the number of vertices of the control cage 427 | * - E0 denotes the number of edges of the control cage 428 | * - F0 denotes the number of faces of the control cage 429 | * Note that since the input mesh may contain non-quad faces, we compute 430 | * the first subdivision step by hand and then apply the formula. 431 | * 432 | */ 433 | int ccm_VertexCountAtDepth_Fast(int depth) 434 | { 435 | const int V0 = ccm_VertexCount(); 436 | const int F0 = ccm_FaceCount(); 437 | const int E0 = ccm_EdgeCount(); 438 | const int H0 = ccm_HalfedgeCount(); 439 | const int F1 = H0; 440 | const int E1 = 2 * E0 + H0; 441 | const int V1 = V0 + E0 + F0; 442 | const int tmp = ~(0xFFFFFFFF << (depth - 1)); // 2^{d-1} - 1 443 | 444 | return V1 + tmp * (E1 + tmp * F1); 445 | } 446 | 447 | int ccm_VertexCountAtDepth(int depth) 448 | { 449 | if (depth == 0) { 450 | return ccm_VertexCount(); 451 | } else { 452 | return ccm_VertexCountAtDepth_Fast(depth); 453 | } 454 | } 455 | 456 | 457 | /******************************************************************************* 458 | * Halfedge data accessors 459 | * 460 | */ 461 | cc_Halfedge ccm__Halfedge(int halfedgeID) 462 | { 463 | return ccmu_Halfedges[halfedgeID]; 464 | } 465 | 466 | int ccm_HalfedgeTwinID(int halfedgeID) 467 | { 468 | return ccm__Halfedge(halfedgeID).twinID; 469 | } 470 | 471 | int ccm_HalfedgeNextID(int halfedgeID) 472 | { 473 | return ccm__Halfedge(halfedgeID).nextID; 474 | } 475 | 476 | int ccm_HalfedgePrevID(int halfedgeID) 477 | { 478 | return ccm__Halfedge(halfedgeID).prevID; 479 | } 480 | 481 | int ccm_HalfedgeVertexID(int halfedgeID) 482 | { 483 | return ccm__Halfedge(halfedgeID).vertexID; 484 | } 485 | 486 | int ccm_HalfedgeUvID(int halfedgeID) 487 | { 488 | return ccm__Halfedge(halfedgeID).uvID; 489 | } 490 | 491 | int ccm_HalfedgeEdgeID(int halfedgeID) 492 | { 493 | return ccm__Halfedge(halfedgeID).edgeID; 494 | } 495 | 496 | int ccm_HalfedgeFaceID(int halfedgeID) 497 | { 498 | return ccm__Halfedge(halfedgeID).faceID; 499 | } 500 | 501 | float ccm_HalfedgeSharpness(int halfedgeID) 502 | { 503 | return ccm_CreaseSharpness(ccm_HalfedgeEdgeID(halfedgeID)); 504 | } 505 | 506 | vec3 ccm_HalfedgeVertexPoint(int halfedgeID) 507 | { 508 | return ccm_VertexPoint(ccm_HalfedgeVertexID(halfedgeID)); 509 | } 510 | 511 | vec2 ccm_HalfedgeVertexUv(int halfedgeID) 512 | { 513 | return ccm_Uv(ccm_HalfedgeUvID(halfedgeID)); 514 | } 515 | 516 | cc_Crease ccm__Crease(int edgeID) 517 | { 518 | return ccmu_Creases[edgeID]; 519 | } 520 | 521 | int ccm_CreaseNextID(int edgeID) 522 | { 523 | return ccm__Crease(edgeID).nextID; 524 | } 525 | 526 | int ccm_CreasePrevID(int edgeID) 527 | { 528 | return ccm__Crease(edgeID).prevID; 529 | } 530 | 531 | float ccm_CreaseSharpness(int edgeID) 532 | { 533 | return ccm__Crease(edgeID).sharpness; 534 | } 535 | 536 | int ccm_HalfedgeFaceID_Quad(int halfedgeID) 537 | { 538 | return halfedgeID >> 2; 539 | } 540 | 541 | 542 | /******************************************************************************* 543 | * Halfedge Iteration (Quad-only special case) 544 | * 545 | */ 546 | int ccm__ScrollFaceHalfedgeID_Quad(int halfedgeID, int dir) 547 | { 548 | const int base = 3; 549 | const int localID = (halfedgeID & base) + dir; 550 | 551 | return (halfedgeID & ~base) | (localID & base); 552 | } 553 | 554 | int ccm_HalfedgeNextID_Quad(int halfedgeID) 555 | { 556 | return ccm__ScrollFaceHalfedgeID_Quad(halfedgeID, +1); 557 | } 558 | 559 | int ccm_HalfedgePrevID_Quad(int halfedgeID) 560 | { 561 | return ccm__ScrollFaceHalfedgeID_Quad(halfedgeID, -1); 562 | } 563 | 564 | 565 | /******************************************************************************* 566 | * Vertex queries 567 | * 568 | */ 569 | vec3 ccm_VertexPoint(int vertexID) 570 | { 571 | #define vertexPoints ccmu_VertexPoints 572 | const float x = vertexPoints[3 * vertexID + 0]; 573 | const float y = vertexPoints[3 * vertexID + 1]; 574 | const float z = vertexPoints[3 * vertexID + 2]; 575 | #undef vertexPoints 576 | 577 | return vec3(x, y, z); 578 | } 579 | 580 | vec2 ccm_Uv(int uvID) 581 | { 582 | #define uvs ccmu_Uvs 583 | const float x = uvs[2 * uvID + 0]; 584 | const float y = uvs[2 * uvID + 1]; 585 | #undef uvs 586 | 587 | return vec2(x, y); 588 | } 589 | 590 | /******************************************************************************* 591 | * VertexToHalfedgeID -- Returns a half edge ID that carries a given vertex 592 | * 593 | */ 594 | int ccm_VertexToHalfedgeID(int vertexID) 595 | { 596 | return ccmu_VertexToHalfedgeIDs[vertexID]; 597 | } 598 | 599 | 600 | /******************************************************************************* 601 | * EdgeToHalfedgeID -- Returns a halfedge associated with a given edge 602 | * 603 | */ 604 | int ccm_EdgeToHalfedgeID(int edgeID) 605 | { 606 | return ccmu_EdgeToHalfedgeIDs[edgeID]; 607 | } 608 | 609 | 610 | /******************************************************************************* 611 | * FaceToHalfedgeID -- Returns a halfedge associated with a given face 612 | * 613 | */ 614 | int ccm_FaceToHalfedgeID(int faceID) 615 | { 616 | return ccmu_FaceToHalfedgeIDs[faceID]; 617 | } 618 | 619 | int ccm_FaceToHalfedgeID_Quad(int faceID) 620 | { 621 | return faceID << 2; 622 | } 623 | 624 | 625 | /******************************************************************************* 626 | * Vertex Halfedge Iteration 627 | * 628 | */ 629 | int ccm_NextVertexHalfedgeID(int halfedgeID) 630 | { 631 | const int twinID = ccm_HalfedgeTwinID(halfedgeID); 632 | 633 | return twinID >= 0 ? ccm_HalfedgeNextID(twinID) : -1; 634 | } 635 | 636 | int ccm_PrevVertexHalfedgeID(int halfedgeID) 637 | { 638 | const int prevID = ccm_HalfedgePrevID(halfedgeID); 639 | 640 | return ccm_HalfedgeTwinID(prevID); 641 | } 642 | 643 | 644 | /******************************************************************************* 645 | * FaceCountAtDepth -- Returns the accumulated number of faces up to a given subdivision depth 646 | * 647 | */ 648 | int ccs_CumulativeFaceCountAtDepth(int depth) 649 | { 650 | return ccs_CumulativeHalfedgeCountAtDepth(depth) >> 2; 651 | } 652 | 653 | int ccs_CumulativeFaceCount() 654 | { 655 | return ccs_CumulativeFaceCountAtDepth(ccs_MaxDepth()); 656 | } 657 | 658 | 659 | /******************************************************************************* 660 | * EdgeCountAtDepth -- Returns the accumulated number of edges up to a given subdivision depth 661 | * 662 | */ 663 | int ccs_CumulativeEdgeCountAtDepth(int depth) 664 | { 665 | const int H0 = ccm_HalfedgeCount(); 666 | const int E0 = ccm_EdgeCount(); 667 | const int H1 = H0 << 2; 668 | const int E1 = (E0 << 1) + H0; 669 | const int D = depth; 670 | const int A = ~(0xFFFFFFFF << D); // 2^{d} - 1 671 | 672 | return (A * (6 * E1 + A * H1 - H1)) / 6; 673 | } 674 | 675 | int ccs_CumulativeEdgeCount() 676 | { 677 | return ccs_CumulativeEdgeCountAtDepth(ccs_MaxDepth()); 678 | } 679 | 680 | 681 | /******************************************************************************* 682 | * HalfedgeCount -- Returns the total number of half edges stored by the subd 683 | * 684 | * The number of half edges is multiplied by 4 at each subdivision step. 685 | * It follows that the number of half-edges is given by the formula 686 | * H = H0 x sum_{d=0}^{D} 4^d 687 | * = H0 (4^{D+1} - 1) / 3 688 | * where D denotes the maximum subdivision depth and H0 the number of 689 | * half edges in the control mesh. 690 | * 691 | */ 692 | int ccs_CumulativeHalfedgeCountAtDepth(int maxDepth) 693 | { 694 | const int D = maxDepth; 695 | const int H0 = ccm_HalfedgeCount(); 696 | const int H1 = H0 << 2; 697 | const int tmp = ~(0xFFFFFFFF << (D << 1)); // (4^D - 1) 698 | 699 | return (H1 * tmp) / 3; 700 | } 701 | 702 | int ccs_CumulativeHalfedgeCount() 703 | { 704 | return ccs_CumulativeHalfedgeCountAtDepth(ccs_MaxDepth()); 705 | } 706 | 707 | 708 | /******************************************************************************* 709 | * CreaseCount -- Returns the total number of creases stored by the subd 710 | * 711 | * The number of creases is multiplied by 2 at each subdivision step. 712 | * It follows that the number of half-edges is given by the formula 713 | * C = C0 x sum_{d=0}^{D} 2^d 714 | * = C0 (2^{D+1} - 1) 715 | * where D denotes the maximum subdivision depth and C0 the number of 716 | * creases in the control mesh. 717 | * 718 | */ 719 | int ccs_CumulativeCreaseCountAtDepth(int maxDepth) 720 | { 721 | const int D = maxDepth; 722 | const int C0 = ccm_CreaseCount(); 723 | const int C1 = C0 << 1; 724 | const int tmp = ~(0xFFFFFFFF << D); // (2^D - 1) 725 | 726 | return (C1 * tmp); 727 | } 728 | 729 | int ccs_CumulativeCreaseCount() 730 | { 731 | return ccs_CumulativeCreaseCountAtDepth(ccs_MaxDepth()); 732 | } 733 | 734 | 735 | /******************************************************************************* 736 | * VertexCount -- Returns the total number of vertices stored by the subd 737 | * 738 | * The number of vertices increases according to the following formula at 739 | * each subdivision step: 740 | * Vd+1 = Fd + Ed + Vd 741 | * It follows that the number of vertices is given by the formula 742 | * Vd = d F0 + (2^(d+1) - 1) E0 + 743 | * = 4 H0 (4^D - 1) / 3 744 | * where D denotes the maximum subdivition depth and H0 the number of 745 | * half edges in the control mesh 746 | * 747 | */ 748 | int ccs_CumulativeVertexCountAtDepth(int depth) 749 | { 750 | const int V0 = ccm_VertexCount(); 751 | const int F0 = ccm_FaceCount(); 752 | const int E0 = ccm_EdgeCount(); 753 | const int H0 = ccm_HalfedgeCount(); 754 | const int F1 = H0; 755 | const int E1 = 2 * E0 + H0; 756 | const int V1 = V0 + E0 + F0; 757 | const int D = depth; 758 | const int A = ~(0xFFFFFFFF << (D )); // 2^{d} - 1 759 | const int B = ~(0xFFFFFFFF << (D << 1)) / 3; // (4^{d} - 1) / 3 760 | 761 | return A * (E1 - (F1 << 1)) + B * F1 + D * (F1 - E1 + V1); 762 | } 763 | 764 | int ccs_CumulativeVertexCount() 765 | { 766 | return ccs_CumulativeVertexCountAtDepth(ccs_MaxDepth()); 767 | } 768 | 769 | 770 | /******************************************************************************* 771 | * Max Depth Query 772 | * 773 | */ 774 | int ccs_MaxDepth() 775 | { 776 | return ccsu_MaxDepth; 777 | } 778 | 779 | 780 | /******************************************************************************* 781 | * Crease queries 782 | * 783 | */ 784 | cc_Crease ccs__Crease(int edgeID, int depth) 785 | { 786 | const int stride = ccs_CumulativeCreaseCountAtDepth(depth - 1); 787 | 788 | return ccsu_Creases[stride + edgeID]; 789 | } 790 | 791 | int ccs_CreaseNextID_Fast(int edgeID, int depth) 792 | { 793 | return ccs__Crease(edgeID, depth).nextID; 794 | } 795 | 796 | int ccs_CreaseNextID(int edgeID, int depth) 797 | { 798 | const int creaseCount = ccm_CreaseCountAtDepth(depth); 799 | 800 | if (edgeID < creaseCount) { 801 | return ccs_CreaseNextID_Fast(edgeID, depth); 802 | } else { 803 | return edgeID; 804 | } 805 | } 806 | 807 | int ccs_CreasePrevID_Fast(int edgeID, int depth) 808 | { 809 | return ccs__Crease(edgeID, depth).prevID; 810 | } 811 | 812 | int ccs_CreasePrevID(int edgeID, int depth) 813 | { 814 | const int creaseCount = ccm_CreaseCountAtDepth(depth); 815 | 816 | if (edgeID < creaseCount) { 817 | return ccs_CreasePrevID_Fast(edgeID, depth); 818 | } else { 819 | return edgeID; 820 | } 821 | } 822 | 823 | float ccs_CreaseSharpness_Fast(int edgeID, int depth) 824 | { 825 | return ccs__Crease(edgeID, depth).sharpness; 826 | } 827 | 828 | float ccs_CreaseSharpness(int edgeID, int depth) 829 | { 830 | const int creaseCount = ccm_CreaseCountAtDepth(depth); 831 | 832 | if (edgeID < creaseCount) { 833 | return ccs_CreaseSharpness_Fast(edgeID, depth); 834 | } else { 835 | return 0.0f; 836 | } 837 | } 838 | 839 | 840 | /******************************************************************************* 841 | * Halfedge queries 842 | * 843 | */ 844 | cc_Halfedge_SemiRegular ccs__Halfedge(int halfedgeID, int depth) 845 | { 846 | const int stride = ccs_CumulativeHalfedgeCountAtDepth(depth - 1); 847 | 848 | return ccsu_Halfedges[stride + halfedgeID]; 849 | } 850 | 851 | int ccs_HalfedgeTwinID(int halfedgeID, int depth) 852 | { 853 | return ccs__Halfedge(halfedgeID, depth).twinID; 854 | } 855 | 856 | int ccs_HalfedgeNextID(int halfedgeID, int depth) 857 | { 858 | return ccm_HalfedgeNextID_Quad(halfedgeID); 859 | } 860 | 861 | int ccs_HalfedgePrevID(int halfedgeID, int depth) 862 | { 863 | return ccm_HalfedgePrevID_Quad(halfedgeID); 864 | } 865 | 866 | int ccs_HalfedgeFaceID(int halfedgeID, int depth) 867 | { 868 | return ccm_HalfedgeFaceID_Quad(halfedgeID); 869 | } 870 | 871 | int ccs_HalfedgeEdgeID(int halfedgeID, int depth) 872 | { 873 | return ccs__Halfedge(halfedgeID, depth).edgeID; 874 | } 875 | 876 | int ccs_HalfedgeVertexID(int halfedgeID, int depth) 877 | { 878 | return ccs__Halfedge(halfedgeID, depth).vertexID; 879 | } 880 | 881 | float ccs_HalfedgeSharpness(int halfedgeID, int depth) 882 | { 883 | const int edgeID = ccs_HalfedgeEdgeID(halfedgeID, depth); 884 | 885 | return ccs_CreaseSharpness(edgeID, depth); 886 | } 887 | 888 | vec3 ccs_HalfedgeVertexPoint(int halfedgeID, int depth) 889 | { 890 | const int vertexID = ccs_HalfedgeVertexID(halfedgeID, depth); 891 | 892 | return ccs_VertexPoint(vertexID, depth); 893 | } 894 | 895 | #ifndef CC_DISABLE_UV 896 | int ccs__HalfedgeUvID(int halfedgeID, int depth) 897 | { 898 | return ccs__Halfedge(halfedgeID, depth).uvID; 899 | } 900 | 901 | vec2 ccs_HalfedgeVertexUv(int halfedgeID, int depth) 902 | { 903 | return cc__DecodeUv(ccs__HalfedgeUvID(halfedgeID, depth)); 904 | } 905 | #endif 906 | 907 | 908 | /******************************************************************************* 909 | * Vertex queries 910 | * 911 | */ 912 | vec3 ccs_VertexPoint(int vertexID, int depth) 913 | { 914 | const int stride = ccs_CumulativeVertexCountAtDepth(depth - 1); 915 | const int tmp = 3 * (stride + vertexID); 916 | 917 | #define vertexPoints ccsu_VertexPoints 918 | const float x = vertexPoints[tmp + 0]; 919 | const float y = vertexPoints[tmp + 1]; 920 | const float z = vertexPoints[tmp + 2]; 921 | #undef vertexPoints 922 | 923 | return vec3(x, y, z); 924 | } 925 | 926 | 927 | /******************************************************************************* 928 | * Normal computation 929 | * 930 | */ 931 | vec3 ccs_HalfedgeNormal_Fast(int halfedgeID) 932 | { 933 | const int maxDepth = ccs_MaxDepth(); 934 | const int nextID = ccm_HalfedgeNextID_Quad(halfedgeID); 935 | const int prevID = ccm_HalfedgePrevID_Quad(halfedgeID); 936 | const vec3 v0 = ccs_HalfedgeVertexPoint(halfedgeID, maxDepth); 937 | const vec3 v1 = ccs_HalfedgeVertexPoint(prevID , maxDepth); 938 | const vec3 v2 = ccs_HalfedgeVertexPoint(nextID , maxDepth); 939 | 940 | return normalize(cross(v2 - v0, v1 - v0)); 941 | } 942 | 943 | vec3 ccs_HalfedgeNormal(int halfedgeID) 944 | { 945 | const int maxDepth = ccs_MaxDepth(); 946 | const vec3 halfedgeNormal = ccs_HalfedgeNormal_Fast(halfedgeID); 947 | vec3 averageNormal = vec3(0.0f); 948 | int halfedgeIterator; 949 | 950 | for (halfedgeIterator = ccs_PrevVertexHalfedgeID(halfedgeID, maxDepth); 951 | halfedgeIterator >= 0 && halfedgeIterator != halfedgeID; 952 | halfedgeIterator = ccs_PrevVertexHalfedgeID(halfedgeIterator, maxDepth)) { 953 | averageNormal+= ccs_HalfedgeNormal_Fast(halfedgeIterator); 954 | } 955 | 956 | if (halfedgeIterator < 0) 957 | return halfedgeNormal; 958 | else 959 | return normalize(halfedgeNormal + averageNormal); 960 | } 961 | 962 | 963 | /******************************************************************************* 964 | * VertexHalfedge Iteraion 965 | * 966 | */ 967 | int ccs_PrevVertexHalfedgeID(int halfedgeID, int depth) 968 | { 969 | const int prevID = ccm_HalfedgePrevID_Quad(halfedgeID); 970 | 971 | return ccs_HalfedgeTwinID(prevID, depth); 972 | } 973 | 974 | int ccs_NextVertexHalfedgeID(int halfedgeID, int depth) 975 | { 976 | const int twinID = ccs_HalfedgeTwinID(halfedgeID, depth); 977 | 978 | return ccm_HalfedgeNextID_Quad(twinID); 979 | } 980 | 981 | 982 | /******************************************************************************* 983 | * Edge to Halfedge Mapping 984 | * 985 | * This procedure returns one of the ID of one of the half edge that constitutes 986 | * the edge. This routine has O(depth) complexity. 987 | * 988 | */ 989 | int ccs__EdgeToHalfedgeID_First(int edgeID) 990 | { 991 | const int edgeCount = ccm_EdgeCount(); 992 | 993 | if /* [2E, 2E + H) */ (edgeID >= 2 * edgeCount) { 994 | const int halfedgeID = edgeID - 2 * edgeCount; 995 | const int nextID = ccm_HalfedgeNextID(halfedgeID); 996 | 997 | return max(4 * halfedgeID + 1, 4 * nextID + 2); 998 | 999 | } else if /* */ ((edgeID & 1) == 1) { 1000 | const int halfedgeID = ccm_EdgeToHalfedgeID(edgeID >> 1); 1001 | const int nextID = ccm_HalfedgeNextID(halfedgeID); 1002 | 1003 | return 4 * nextID + 3; 1004 | 1005 | } else /* */ { 1006 | const int halfedgeID = ccm_EdgeToHalfedgeID(edgeID >> 1); 1007 | 1008 | return 4 * halfedgeID + 0; 1009 | } 1010 | } 1011 | 1012 | int ccs_EdgeToHalfedgeID(int edgeID, int depth) 1013 | { 1014 | uint heap = 1u; 1015 | int edgeHalfedgeID = 0; 1016 | int heapDepth = depth; 1017 | 1018 | // build heap 1019 | for (; heapDepth > 1; --heapDepth) { 1020 | const int edgeCount = ccm_EdgeCountAtDepth_Fast(heapDepth - 1); 1021 | 1022 | if /* [2E, 2E + H) */ (edgeID >= 2 * edgeCount) { 1023 | const int halfedgeID = edgeID - 2 * edgeCount; 1024 | const int nextID = ccm_HalfedgeNextID_Quad(halfedgeID); 1025 | 1026 | edgeHalfedgeID = max(4 * halfedgeID + 1, 4 * nextID + 2); 1027 | break; 1028 | } else { 1029 | heap = (heap << 1) | (edgeID & 1); 1030 | edgeID>>= 1; 1031 | } 1032 | } 1033 | 1034 | // initialize root cfg 1035 | if (heapDepth == 1) { 1036 | edgeHalfedgeID = ccs__EdgeToHalfedgeID_First(edgeID); 1037 | } 1038 | 1039 | // read heap 1040 | while (heap > 1u) { 1041 | if ((heap & 1u) == 1u) { 1042 | const int nextID = ccm_HalfedgeNextID_Quad(edgeHalfedgeID); 1043 | 1044 | edgeHalfedgeID = 4 * nextID + 3; 1045 | } else { 1046 | edgeHalfedgeID = 4 * edgeHalfedgeID + 0; 1047 | } 1048 | 1049 | heap>>= 1; 1050 | } 1051 | 1052 | return edgeHalfedgeID; 1053 | } 1054 | 1055 | 1056 | /******************************************************************************* 1057 | * Vertex to Halfedge Mapping 1058 | * 1059 | * This procedure returns the ID of one of the half edge that connects a 1060 | * given vertex. This routine has O(depth) complexity. 1061 | * 1062 | */ 1063 | int ccs__VertexToHalfedgeID_First(int vertexID) 1064 | { 1065 | const int vertexCount = ccm_VertexCount(); 1066 | const int faceCount = ccm_FaceCount(); 1067 | 1068 | if /* [V + F, V + F + E) */ (vertexID >= vertexCount + faceCount) { 1069 | const int edgeID = vertexID - vertexCount - faceCount; 1070 | 1071 | return 4 * ccm_EdgeToHalfedgeID(edgeID) + 1; 1072 | 1073 | } else if /* [V, V + F) */ (vertexID >= vertexCount) { 1074 | const int faceID = vertexID - vertexCount; 1075 | 1076 | return 4 * ccm_FaceToHalfedgeID(faceID) + 2; 1077 | 1078 | } else /* [0, V) */ { 1079 | 1080 | return 4 * ccm_VertexToHalfedgeID(vertexID) + 0; 1081 | } 1082 | } 1083 | 1084 | int ccs_VertexToHalfedgeID(int vertexID, int depth) 1085 | { 1086 | int stride = 0; 1087 | int halfedgeID = 0; 1088 | int heapDepth = depth; 1089 | 1090 | // build heap 1091 | for (; heapDepth > 1; --heapDepth) { 1092 | const int vertexCount = ccm_VertexCountAtDepth_Fast(heapDepth - 1); 1093 | const int faceCount = ccm_FaceCountAtDepth_Fast(heapDepth - 1); 1094 | 1095 | if /* [V + F, V + F + E) */ (vertexID >= vertexCount + faceCount) { 1096 | const int edgeID = vertexID - faceCount - vertexCount; 1097 | 1098 | halfedgeID = 4 * ccs_EdgeToHalfedgeID(edgeID, heapDepth - 1) + 1; 1099 | break; 1100 | } else if /* [V, V + F) */ (vertexID >= vertexCount) { 1101 | const int faceID = vertexID - vertexCount; 1102 | 1103 | halfedgeID = 4 * ccm_FaceToHalfedgeID_Quad(faceID) + 2; 1104 | break; 1105 | } else /* [0, V) */ { 1106 | stride+= 2; 1107 | } 1108 | } 1109 | 1110 | // initialize root cfg 1111 | if (heapDepth == 1) { 1112 | halfedgeID = ccs__VertexToHalfedgeID_First(vertexID); 1113 | } 1114 | 1115 | return (halfedgeID << stride); 1116 | } 1117 | -------------------------------------------------------------------------------- /glsl/cc_CageEdgePoints_Scatter.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | layout (local_size_x = CC_LOCAL_SIZE_X, 6 | local_size_y = 1, 7 | local_size_z = 1) in; 8 | 9 | void WriteVertex(int vertexID, in const vec3 vertexPoint) 10 | { 11 | #define vertexPoints ccsu_VertexPoints 12 | atomicAdd(vertexPoints[3 * vertexID + 0], vertexPoint.x); 13 | atomicAdd(vertexPoints[3 * vertexID + 1], vertexPoint.y); 14 | atomicAdd(vertexPoints[3 * vertexID + 2], vertexPoint.z); 15 | #undef vertexPoints 16 | } 17 | 18 | void main() 19 | { 20 | const uint threadID = gl_GlobalInvocationID.x; 21 | const int halfedgeCount = ccm_HalfedgeCount(); 22 | const int halfedgeID = int(threadID); 23 | const int vertexCount = ccm_VertexCount(); 24 | const int faceCount = ccm_FaceCount(); 25 | 26 | if (halfedgeID < halfedgeCount) { 27 | const int faceID = ccm_HalfedgeFaceID(halfedgeID); 28 | const int edgeID = ccm_HalfedgeEdgeID(halfedgeID); 29 | const int twinID = ccm_HalfedgeTwinID(halfedgeID); 30 | const int nextID = ccm_HalfedgeNextID(halfedgeID); 31 | const float sharp = ccm_CreaseSharpness(edgeID); 32 | const float edgeWeight = clamp(sharp, 0.0f, 1.0f); 33 | const vec3 newFacePoint = ccs_VertexPoint(vertexCount + faceID, 1); 34 | const vec3 oldEdgePoints[2] = vec3[2]( 35 | ccm_HalfedgeVertexPoint(halfedgeID), 36 | ccm_HalfedgeVertexPoint(nextID) 37 | ); 38 | const vec3 sharpPoint = mix(oldEdgePoints[0], 39 | oldEdgePoints[1], 40 | 0.5f) * (twinID < 0 ? 1.0f : 0.5f); 41 | const vec3 smoothPoint = mix(oldEdgePoints[0], newFacePoint, 0.5f) * 0.5f; 42 | const vec3 atomicWeight = mix(smoothPoint, sharpPoint, edgeWeight); 43 | 44 | WriteVertex(vertexCount + faceCount + edgeID, atomicWeight); 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /glsl/cc_CageFacePoints_Scatter.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | layout (local_size_x = CC_LOCAL_SIZE_X, 6 | local_size_y = 1, 7 | local_size_z = 1) in; 8 | 9 | void WriteVertex(int vertexID, in const vec3 vertexPoint) 10 | { 11 | #define vertexPoints ccsu_VertexPoints 12 | atomicAdd(vertexPoints[3 * vertexID + 0], vertexPoint.x); 13 | atomicAdd(vertexPoints[3 * vertexID + 1], vertexPoint.y); 14 | atomicAdd(vertexPoints[3 * vertexID + 2], vertexPoint.z); 15 | #undef vertexPoints 16 | } 17 | 18 | void main() 19 | { 20 | const uint threadID = gl_GlobalInvocationID.x; 21 | const int halfedgeCount = ccm_HalfedgeCount(); 22 | const int halfedgeID = int(threadID); 23 | 24 | if (halfedgeID < halfedgeCount) { 25 | const int vertexCount = ccm_VertexCount(); 26 | const int faceID = ccm_HalfedgeFaceID(halfedgeID); 27 | const vec3 vertexPoint = ccm_HalfedgeVertexPoint(halfedgeID); 28 | int halfedgeIt = ccm_HalfedgeNextID(halfedgeID); 29 | float n = 1.0f; 30 | 31 | while (halfedgeIt != halfedgeID) { 32 | halfedgeIt = ccm_HalfedgeNextID(halfedgeIt); 33 | ++n; 34 | } 35 | 36 | WriteVertex(vertexCount + faceID, vertexPoint / n); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /glsl/cc_CageVertexPoints_Scatter.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | layout (local_size_x = CC_LOCAL_SIZE_X, 6 | local_size_y = 1, 7 | local_size_z = 1) in; 8 | 9 | void WriteVertex(int vertexID, in const vec3 vertexPoint) 10 | { 11 | #define vertexPoints ccsu_VertexPoints 12 | atomicAdd(vertexPoints[3 * vertexID + 0], vertexPoint.x); 13 | atomicAdd(vertexPoints[3 * vertexID + 1], vertexPoint.y); 14 | atomicAdd(vertexPoints[3 * vertexID + 2], vertexPoint.z); 15 | #undef vertexPoints 16 | } 17 | 18 | void main() 19 | { 20 | const uint threadID = gl_GlobalInvocationID.x; 21 | const int halfedgeCount = ccm_HalfedgeCount(); 22 | const int halfedgeID = int(threadID); 23 | const int vertexCount = ccm_VertexCount(); 24 | const int faceCount = ccm_FaceCount(); 25 | 26 | if (halfedgeID < halfedgeCount) { 27 | const int vertexID = ccm_HalfedgeVertexID(halfedgeID); 28 | const int edgeID = ccm_HalfedgeEdgeID(halfedgeID); 29 | const int faceID = ccm_HalfedgeFaceID(halfedgeID); 30 | const int prevID = ccm_HalfedgePrevID(halfedgeID); 31 | const int prevEdgeID = ccm_HalfedgeEdgeID(prevID); 32 | const float thisS = ccm_HalfedgeSharpness(halfedgeID); 33 | const float prevS = ccm_HalfedgeSharpness(prevID); 34 | const float creaseWeight = sign(thisS); 35 | const float prevCreaseWeight = sign(prevS); 36 | const vec3 newPrevEdgePoint = ccs_VertexPoint(vertexCount + faceCount + prevEdgeID, 1); 37 | const vec3 newEdgePoint = ccs_VertexPoint(vertexCount + faceCount + edgeID, 1); 38 | const vec3 newFacePoint = ccs_VertexPoint(vertexCount + faceID, 1); 39 | const vec3 oldVertexPoint = ccm_VertexPoint(vertexID); 40 | vec3 cornerPoint = vec3(0.0f); 41 | vec3 smoothPoint = vec3(0.0f); 42 | vec3 creasePoint = vec3(0.0f); 43 | vec3 atomicWeight = vec3(0.0f); 44 | float avgS = prevS; 45 | float creaseCount = prevCreaseWeight; 46 | float valence = 1.0f; 47 | int forwardIterator, backwardIterator; 48 | 49 | for (forwardIterator = ccm_HalfedgeTwinID(prevID); 50 | forwardIterator >= 0 && forwardIterator != halfedgeID; 51 | forwardIterator = ccm_HalfedgeTwinID(forwardIterator)) { 52 | const int prevID = ccm_HalfedgePrevID(forwardIterator); 53 | const float prevS = ccm_HalfedgeSharpness(prevID); 54 | const float prevCreaseWeight = sign(prevS); 55 | 56 | // valence computation 57 | ++valence; 58 | 59 | // crease computation 60 | avgS+= prevS; 61 | creaseCount+= prevCreaseWeight; 62 | 63 | // next vertex halfedge 64 | forwardIterator = prevID; 65 | } 66 | 67 | for (backwardIterator = ccm_HalfedgeTwinID(halfedgeID); 68 | forwardIterator < 0 && backwardIterator >= 0 && backwardIterator != halfedgeID; 69 | backwardIterator = ccm_HalfedgeTwinID(backwardIterator)) { 70 | const int nextID = ccm_HalfedgeNextID(backwardIterator); 71 | const float nextS = ccm_HalfedgeSharpness(nextID); 72 | const float nextCreaseWeight = sign(nextS); 73 | 74 | // valence computation 75 | ++valence; 76 | 77 | // crease computation 78 | avgS+= nextS; 79 | creaseCount+= nextCreaseWeight; 80 | 81 | // next vertex halfedge 82 | backwardIterator = nextID; 83 | } 84 | 85 | // corner point 86 | cornerPoint = oldVertexPoint / valence; 87 | 88 | // crease computation: V / 4 89 | creasePoint = (oldVertexPoint + newEdgePoint) * 0.25f * creaseWeight; 90 | 91 | // smooth computation: (4E - F + (n - 3) V) / N 92 | const vec3 E = newEdgePoint; 93 | const vec3 F = newFacePoint; 94 | const vec3 V = oldVertexPoint; 95 | const float N = valence; 96 | smoothPoint = (4.0f * E - F + (N - 3.0f) * V) / (N * N); 97 | 98 | // boundary corrections 99 | if (forwardIterator < 0) { 100 | creaseCount+= creaseWeight; 101 | ++valence; 102 | 103 | creasePoint+= (oldVertexPoint + newPrevEdgePoint) * 0.25f * prevCreaseWeight; 104 | } 105 | 106 | // average sharpness 107 | avgS/= valence; 108 | 109 | // atomicWeight 110 | if (creaseCount >= 3.0f || valence == 2.0f) { 111 | atomicWeight = cornerPoint; 112 | } else if (creaseCount <= 1.0f) { 113 | atomicWeight = smoothPoint; 114 | } else { 115 | atomicWeight = mix(cornerPoint, creasePoint, clamp(avgS, 0.0f, 1.0f)); 116 | } 117 | 118 | WriteVertex(vertexID, atomicWeight); 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /glsl/cc_EdgePoints_Scatter.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | uniform int u_Depth; 6 | 7 | layout (local_size_x = CC_LOCAL_SIZE_X, 8 | local_size_y = 1, 9 | local_size_z = 1) in; 10 | 11 | void WriteVertex(int vertexID, in const vec3 vertexPoint, int depth) 12 | { 13 | const int stride = ccs_CumulativeVertexCountAtDepth(depth); 14 | const int tmp = stride + vertexID; 15 | 16 | #define vertexPoints ccsu_VertexPoints 17 | atomicAdd(vertexPoints[3 * tmp + 0], vertexPoint.x); 18 | atomicAdd(vertexPoints[3 * tmp + 1], vertexPoint.y); 19 | atomicAdd(vertexPoints[3 * tmp + 2], vertexPoint.z); 20 | #undef vertexPoints 21 | } 22 | 23 | void main() 24 | { 25 | const int cageID = 0; 26 | const int depth = u_Depth; 27 | const uint threadID = gl_GlobalInvocationID.x; 28 | const int halfedgeCount = ccm_HalfedgeCountAtDepth(depth); 29 | const int halfedgeID = int(threadID); 30 | const int vertexCount = ccm_VertexCountAtDepth_Fast(depth); 31 | const int faceCount = ccm_FaceCountAtDepth_Fast(depth); 32 | 33 | if (halfedgeID < halfedgeCount) { 34 | const int twinID = ccs_HalfedgeTwinID(halfedgeID, depth); 35 | const int edgeID = ccs_HalfedgeEdgeID(halfedgeID, depth); 36 | const int faceID = ccs_HalfedgeFaceID(halfedgeID, depth); 37 | const int nextID = ccs_HalfedgeNextID(halfedgeID, depth); 38 | const float sharp = ccs_CreaseSharpness(edgeID, depth); 39 | const float edgeWeight = clamp(sharp, 0.0f, 1.0f); 40 | const vec3 newFacePoint = ccs_VertexPoint(vertexCount + faceID, depth + 1); 41 | const vec3 oldEdgePoints[2] = { 42 | ccs_HalfedgeVertexPoint(halfedgeID, depth), 43 | ccs_HalfedgeVertexPoint( nextID, depth) 44 | }; 45 | const vec3 sharpPoint = mix(oldEdgePoints[0], 46 | oldEdgePoints[1], 47 | 0.5f) * (twinID < 0 ? 1.0f : 0.5f); 48 | const vec3 smoothPoint = mix(oldEdgePoints[0], newFacePoint, 0.5f) * 0.5f; 49 | const vec3 atomicWeight = mix(smoothPoint, sharpPoint, edgeWeight); 50 | 51 | WriteVertex(vertexCount + faceCount + edgeID, atomicWeight, depth); 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /glsl/cc_FacePoints_Scatter.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | uniform int u_Depth; 6 | 7 | layout (local_size_x = CC_LOCAL_SIZE_X, 8 | local_size_y = 1, 9 | local_size_z = 1) in; 10 | 11 | void WriteVertex(int vertexID, in const vec3 vertexPoint, int depth) 12 | { 13 | const int stride = ccs_CumulativeVertexCountAtDepth(depth); 14 | const int tmp = stride + vertexID; 15 | 16 | #define vertexPoints ccsu_VertexPoints 17 | atomicAdd(vertexPoints[3 * tmp + 0], vertexPoint.x); 18 | atomicAdd(vertexPoints[3 * tmp + 1], vertexPoint.y); 19 | atomicAdd(vertexPoints[3 * tmp + 2], vertexPoint.z); 20 | #undef vertexPoints 21 | } 22 | 23 | void main() 24 | { 25 | const int depth = u_Depth; 26 | const uint threadID = gl_GlobalInvocationID.x; 27 | const int halfedgeCount = ccm_HalfedgeCountAtDepth(depth); 28 | const int halfedgeID = int(threadID); 29 | 30 | if (halfedgeID < halfedgeCount) { 31 | const int vertexCount = ccm_VertexCountAtDepth_Fast(depth); 32 | const int faceID = ccm_HalfedgeFaceID_Quad(halfedgeID); 33 | const vec3 vertexPoint = ccs_HalfedgeVertexPoint(halfedgeID, depth); 34 | const vec3 facePoint = vertexPoint * 0.25f; 35 | 36 | WriteVertex(vertexCount + faceID, facePoint, depth); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /glsl/cc_RefineCageCreases.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | layout (local_size_x = CC_LOCAL_SIZE_X, 6 | local_size_y = 1, 7 | local_size_z = 1) in; 8 | 9 | void WriteCrease(int edgeID, in const cc_Crease crease) 10 | { 11 | ccsu_Creases[edgeID] = crease; 12 | } 13 | 14 | void main() 15 | { 16 | const uint threadID = gl_GlobalInvocationID.x; 17 | const int edgeCount = ccm_EdgeCount(); 18 | const int edgeID = int(threadID); 19 | 20 | if (edgeID < edgeCount) { 21 | const int nextID = ccm_CreaseNextID(edgeID); 22 | const int prevID = ccm_CreasePrevID(edgeID); 23 | const bool t1 = ccm_CreasePrevID(nextID) == edgeID && nextID != edgeID; 24 | const bool t2 = ccm_CreaseNextID(prevID) == edgeID && prevID != edgeID; 25 | const float thisS = 3.0f * ccm_CreaseSharpness(edgeID); 26 | const float nextS = ccm_CreaseSharpness(nextID); 27 | const float prevS = ccm_CreaseSharpness(prevID); 28 | cc_Crease newCreases[2]; 29 | 30 | // next rule 31 | newCreases[0].nextID = 2 * edgeID + 1; 32 | newCreases[1].nextID = 2 * nextID + (t1 ? 0 : 1); 33 | 34 | // prev rule 35 | newCreases[0].prevID = 2 * prevID + (t2 ? 1 : 0); 36 | newCreases[1].prevID = 2 * edgeID + 0; 37 | 38 | // sharpness rule 39 | newCreases[0].sharpness = max(0.0f, (prevS + thisS) / 4.0f - 1.0f); 40 | newCreases[1].sharpness = max(0.0f, (thisS + nextS) / 4.0f - 1.0f); 41 | 42 | // write data 43 | WriteCrease(2 * edgeID + 0, newCreases[0]); 44 | WriteCrease(2 * edgeID + 1, newCreases[1]); 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /glsl/cc_RefineCageHalfedges.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | layout (local_size_x = CC_LOCAL_SIZE_X, 6 | local_size_y = 1, 7 | local_size_z = 1) in; 8 | 9 | void 10 | WriteHalfedge(int halfedgeID, in const cc_Halfedge_SemiRegular halfedge) 11 | { 12 | ccsu_Halfedges[halfedgeID] = halfedge; 13 | } 14 | 15 | void main() 16 | { 17 | const uint threadID = gl_GlobalInvocationID.x; 18 | const int halfedgeCount = ccm_HalfedgeCount(); 19 | const int halfedgeID = int(threadID); 20 | 21 | if (halfedgeID < halfedgeCount) { 22 | const int vertexCount = ccm_VertexCount(); 23 | const int edgeCount = ccm_EdgeCount(); 24 | const int faceCount = ccm_FaceCount(); 25 | const int vertexID = ccm_HalfedgeVertexID(halfedgeID); 26 | const int twinID = ccm_HalfedgeTwinID(halfedgeID); 27 | const int prevID = ccm_HalfedgePrevID(halfedgeID); 28 | const int nextID = ccm_HalfedgeNextID(halfedgeID); 29 | const int faceID = ccm_HalfedgeFaceID(halfedgeID); 30 | const int edgeID = ccm_HalfedgeEdgeID(halfedgeID); 31 | const int prevEdgeID = ccm_HalfedgeEdgeID(prevID); 32 | const int prevTwinID = ccm_HalfedgeTwinID(prevID); 33 | const int twinNextID = 34 | twinID >= 0 ? ccm_HalfedgeNextID(twinID) : -1; 35 | cc_Halfedge_SemiRegular newHalfedges[4]; 36 | 37 | // twinIDs 38 | newHalfedges[0].twinID = 4 * twinNextID + 3; 39 | newHalfedges[1].twinID = 4 * nextID + 2; 40 | newHalfedges[2].twinID = 4 * prevID + 1; 41 | newHalfedges[3].twinID = 4 * prevTwinID + 0; 42 | 43 | // edgeIDs 44 | newHalfedges[0].edgeID = 2 * edgeID + (halfedgeID > twinID ? 0 : 1); 45 | newHalfedges[1].edgeID = 2 * edgeCount + halfedgeID; 46 | newHalfedges[2].edgeID = 2 * edgeCount + prevID; 47 | newHalfedges[3].edgeID = 2 * prevEdgeID + (prevID > prevTwinID ? 1 : 0); 48 | 49 | // vertexIDs 50 | newHalfedges[0].vertexID = vertexID; 51 | newHalfedges[1].vertexID = vertexCount + faceCount + edgeID; 52 | newHalfedges[2].vertexID = vertexCount + faceID; 53 | newHalfedges[3].vertexID = vertexCount + faceCount + prevEdgeID; 54 | 55 | // write data 56 | WriteHalfedge(4 * halfedgeID + 0, newHalfedges[0]); 57 | WriteHalfedge(4 * halfedgeID + 1, newHalfedges[1]); 58 | WriteHalfedge(4 * halfedgeID + 2, newHalfedges[2]); 59 | WriteHalfedge(4 * halfedgeID + 3, newHalfedges[3]); 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /glsl/cc_RefineCageVertexUvs.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | layout (local_size_x = CC_LOCAL_SIZE_X, 6 | local_size_y = 1, 7 | local_size_z = 1) in; 8 | 9 | void WriteHalfedgeUv(int halfedgeID, vec2 uv) 10 | { 11 | ccsu_Halfedges[halfedgeID].uvID = cc__EncodeUv(uv); 12 | } 13 | 14 | void main() 15 | { 16 | const uint threadID = gl_GlobalInvocationID.x; 17 | const int halfedgeCount = ccm_HalfedgeCount(); 18 | const int halfedgeID = int(threadID); 19 | 20 | if (halfedgeID < halfedgeCount) { 21 | const int prevID = ccm_HalfedgePrevID(halfedgeID); 22 | const int nextID = ccm_HalfedgeNextID(halfedgeID); 23 | const vec2 uv = ccm_HalfedgeVertexUv(halfedgeID); 24 | const vec2 nextUv = ccm_HalfedgeVertexUv(nextID); 25 | const vec2 prevUv = ccm_HalfedgeVertexUv(prevID); 26 | const vec2 edgeUv = (uv + nextUv) * 0.5f; 27 | const vec2 prevEdgeUv = (uv + prevUv) * 0.5f; 28 | vec2 faceUv = uv; 29 | float m = 1.0f; 30 | 31 | for (int halfedgeIt = ccm_HalfedgeNextID(halfedgeID); 32 | halfedgeIt != halfedgeID; 33 | halfedgeIt = ccm_HalfedgeNextID(halfedgeIt)) { 34 | const vec2 uv = ccm_HalfedgeVertexUv(halfedgeIt); 35 | 36 | faceUv+= uv; 37 | ++m; 38 | } 39 | 40 | faceUv/= m; 41 | 42 | WriteHalfedgeUv(4 * halfedgeID + 0, uv); 43 | WriteHalfedgeUv(4 * halfedgeID + 1, edgeUv); 44 | WriteHalfedgeUv(4 * halfedgeID + 2, faceUv); 45 | WriteHalfedgeUv(4 * halfedgeID + 3, prevEdgeUv); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /glsl/cc_RefineCreases.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | uniform int u_Depth; 6 | 7 | layout (local_size_x = CC_LOCAL_SIZE_X, 8 | local_size_y = 1, 9 | local_size_z = 1) in; 10 | 11 | void WriteCrease(int edgeID, in const cc_Crease crease, int depth) 12 | { 13 | const int stride = ccs_CumulativeCreaseCountAtDepth(depth); 14 | 15 | ccsu_Creases[stride + edgeID] = crease; 16 | } 17 | 18 | void main() 19 | { 20 | const int depth = u_Depth; 21 | const uint threadID = gl_GlobalInvocationID.x; 22 | const int creaseCount = ccm_CreaseCountAtDepth(depth); 23 | const int edgeID = int(threadID); 24 | 25 | if (edgeID < creaseCount) { 26 | const int nextID = ccs_CreaseNextID_Fast(edgeID, depth); 27 | const int prevID = ccs_CreasePrevID_Fast(edgeID, depth); 28 | const bool t1 = ccs_CreasePrevID_Fast(nextID, depth) == edgeID && nextID != edgeID; 29 | const bool t2 = ccs_CreaseNextID_Fast(prevID, depth) == edgeID && prevID != edgeID; 30 | const float thisS = 3.0f * ccs_CreaseSharpness_Fast(edgeID, depth); 31 | const float nextS = ccs_CreaseSharpness_Fast(nextID, depth); 32 | const float prevS = ccs_CreaseSharpness_Fast(prevID, depth); 33 | cc_Crease newCreases[2]; 34 | 35 | // next rule 36 | newCreases[0].nextID = 2 * edgeID + 1; 37 | newCreases[1].nextID = 2 * nextID + (t1 ? 0 : 1); 38 | 39 | // prev rule 40 | newCreases[0].prevID = 2 * prevID + (t2 ? 1 : 0); 41 | newCreases[1].prevID = 2 * edgeID + 0; 42 | 43 | // sharpness rule 44 | newCreases[0].sharpness = max(0.0f, (prevS + thisS) / 4.0f - 1.0f); 45 | newCreases[1].sharpness = max(0.0f, (thisS + nextS) / 4.0f - 1.0f); 46 | 47 | // write data 48 | WriteCrease(2 * edgeID + 0, newCreases[0], depth); 49 | WriteCrease(2 * edgeID + 1, newCreases[1], depth); 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /glsl/cc_RefineHalfedges.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | uniform int u_Depth; 6 | 7 | layout (local_size_x = CC_LOCAL_SIZE_X, 8 | local_size_y = 1, 9 | local_size_z = 1) in; 10 | 11 | void 12 | WriteHalfedge(int halfedgeID, in const cc_Halfedge_SemiRegular halfedge, int depth) 13 | { 14 | const int stride = ccs_CumulativeHalfedgeCountAtDepth(depth); 15 | 16 | ccsu_Halfedges[stride + halfedgeID] = halfedge; 17 | } 18 | 19 | void main() 20 | { 21 | const int cageID = 0; 22 | const int depth = u_Depth; 23 | const uint threadID = gl_GlobalInvocationID.x; 24 | const int halfedgeCount = ccm_HalfedgeCountAtDepth(depth); 25 | const int halfedgeID = int(threadID); 26 | 27 | if (halfedgeID < halfedgeCount) { 28 | const int vertexCount = ccm_VertexCountAtDepth_Fast(depth); 29 | const int edgeCount = ccm_EdgeCountAtDepth_Fast(depth); 30 | const int faceCount = ccm_FaceCountAtDepth_Fast(depth); 31 | const int vertexID = ccs_HalfedgeVertexID(halfedgeID, depth); 32 | const int twinID = ccs_HalfedgeTwinID(halfedgeID, depth); 33 | const int prevID = ccs_HalfedgePrevID(halfedgeID, depth); 34 | const int nextID = ccs_HalfedgeNextID(halfedgeID, depth); 35 | const int faceID = ccs_HalfedgeFaceID(halfedgeID, depth); 36 | const int edgeID = ccs_HalfedgeEdgeID(halfedgeID, depth); 37 | const int prevEdgeID = ccs_HalfedgeEdgeID(prevID, depth); 38 | const int prevTwinID = ccs_HalfedgeTwinID(prevID, depth); 39 | const int twinNextID = ccs_HalfedgeNextID(twinID, depth); 40 | cc_Halfedge_SemiRegular halfedges[4]; 41 | 42 | // twinIDs 43 | halfedges[0].twinID = 4 * twinNextID + 3; 44 | halfedges[1].twinID = 4 * nextID + 2; 45 | halfedges[2].twinID = 4 * prevID + 1; 46 | halfedges[3].twinID = 4 * prevTwinID + 0; 47 | 48 | // edgeIDs 49 | halfedges[0].edgeID = 2 * edgeID + (halfedgeID > twinID ? 0 : 1); 50 | halfedges[1].edgeID = 2 * edgeCount + halfedgeID; 51 | halfedges[2].edgeID = 2 * edgeCount + prevID; 52 | halfedges[3].edgeID = 2 * prevEdgeID + (prevID > prevTwinID ? 1 : 0); 53 | 54 | // vertexIDs 55 | halfedges[0].vertexID = vertexID; 56 | halfedges[1].vertexID = vertexCount + faceCount + edgeID; 57 | halfedges[2].vertexID = vertexCount + faceID; 58 | halfedges[3].vertexID = vertexCount + faceCount + prevEdgeID; 59 | 60 | WriteHalfedge(4 * halfedgeID + 0, halfedges[0], depth); 61 | WriteHalfedge(4 * halfedgeID + 1, halfedges[1], depth); 62 | WriteHalfedge(4 * halfedgeID + 2, halfedges[2], depth); 63 | WriteHalfedge(4 * halfedgeID + 3, halfedges[3], depth); 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /glsl/cc_RefineVertexUvs.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | uniform int u_Depth; 6 | 7 | layout (local_size_x = CC_LOCAL_SIZE_X, 8 | local_size_y = 1, 9 | local_size_z = 1) in; 10 | 11 | void WriteHalfedgeUv(int halfedgeID, vec2 uv, int depth) 12 | { 13 | const int stride = ccs_CumulativeHalfedgeCountAtDepth(depth); 14 | 15 | ccsu_Halfedges[stride + halfedgeID].uvID = cc__EncodeUv(uv); 16 | } 17 | 18 | void main() 19 | { 20 | const int depth = u_Depth; 21 | const uint threadID = gl_GlobalInvocationID.x; 22 | const int halfedgeCount = ccm_HalfedgeCountAtDepth(depth); 23 | const int halfedgeID = int(threadID); 24 | 25 | if (halfedgeID < halfedgeCount) { 26 | const int prevID = ccs_HalfedgePrevID(halfedgeID, depth); 27 | const int nextID = ccs_HalfedgeNextID(halfedgeID, depth); 28 | const vec2 uv = ccs_HalfedgeVertexUv(halfedgeID, depth); 29 | const vec2 nextUv = ccs_HalfedgeVertexUv(nextID, depth); 30 | const vec2 prevUv = ccs_HalfedgeVertexUv(prevID, depth); 31 | const vec2 edgeUv = (uv + nextUv) * 0.5f; 32 | const vec2 prevEdgeUv = (uv + prevUv) * 0.5f; 33 | vec2 faceUv = uv; 34 | 35 | for (int halfedgeIt = ccm_HalfedgeNextID_Quad(halfedgeID); 36 | halfedgeIt != halfedgeID; 37 | halfedgeIt = ccm_HalfedgeNextID_Quad(halfedgeIt)) { 38 | const vec2 uv = ccs_HalfedgeVertexUv(halfedgeIt, depth); 39 | 40 | faceUv+= uv; 41 | } 42 | 43 | faceUv*= 0.25f; 44 | 45 | WriteHalfedgeUv(4 * halfedgeID + 0, uv , depth); 46 | WriteHalfedgeUv(4 * halfedgeID + 1, edgeUv , depth); 47 | WriteHalfedgeUv(4 * halfedgeID + 2, faceUv , depth); 48 | WriteHalfedgeUv(4 * halfedgeID + 3, prevEdgeUv, depth); 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /glsl/cc_VertexPoints_Scatter.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CC_LOCAL_SIZE_X 2 | # define CC_LOCAL_SIZE_X 256 3 | #endif 4 | 5 | uniform int u_Depth; 6 | 7 | layout (local_size_x = CC_LOCAL_SIZE_X, 8 | local_size_y = 1, 9 | local_size_z = 1) in; 10 | 11 | void WriteVertex(int vertexID, in const vec3 vertexPoint, int depth) 12 | { 13 | const int stride = ccs_CumulativeVertexCountAtDepth(depth); 14 | const int tmp = stride + vertexID; 15 | 16 | #define vertexPoints ccsu_VertexPoints 17 | atomicAdd(vertexPoints[3 * tmp + 0], vertexPoint.x); 18 | atomicAdd(vertexPoints[3 * tmp + 1], vertexPoint.y); 19 | atomicAdd(vertexPoints[3 * tmp + 2], vertexPoint.z); 20 | #undef vertexPoints 21 | } 22 | 23 | void main() 24 | { 25 | const int depth = u_Depth; 26 | const uint threadID = gl_GlobalInvocationID.x; 27 | const int halfedgeCount = ccm_HalfedgeCountAtDepth(depth); 28 | const int halfedgeID = int(threadID); 29 | const int faceCount = ccm_FaceCountAtDepth_Fast(depth); 30 | const int vertexCount = ccm_VertexCountAtDepth_Fast(depth); 31 | 32 | if (halfedgeID < halfedgeCount) { 33 | const int vertexID = ccs_HalfedgeVertexID(halfedgeID, depth); 34 | const int edgeID = ccs_HalfedgeEdgeID(halfedgeID, depth); 35 | const int faceID = ccs_HalfedgeFaceID(halfedgeID, depth); 36 | const int prevID = ccs_HalfedgePrevID(halfedgeID, depth); 37 | const int prevEdgeID = ccs_HalfedgeEdgeID(prevID, depth); 38 | const float thisS = ccs_HalfedgeSharpness(halfedgeID, depth); 39 | const float prevS = ccs_HalfedgeSharpness( prevID, depth); 40 | const float creaseWeight = sign(thisS); 41 | const float prevCreaseWeight = sign(prevS); 42 | const vec3 newPrevEdgePoint = ccs_VertexPoint(vertexCount + faceCount + prevEdgeID, depth + 1); 43 | const vec3 newEdgePoint = ccs_VertexPoint(vertexCount + faceCount + edgeID, depth + 1); 44 | const vec3 newFacePoint = ccs_VertexPoint(vertexCount + faceID, depth + 1); 45 | const vec3 oldVertexPoint = ccs_VertexPoint(vertexID, depth); 46 | vec3 cornerPoint = vec3(0.0f); 47 | vec3 smoothPoint = vec3(0.0f); 48 | vec3 creasePoint = vec3(0.0f); 49 | vec3 atomicWeight = vec3(0.0f); 50 | float avgS = prevS; 51 | float creaseCount = prevCreaseWeight; 52 | float valence = 1.0f; 53 | int forwardIterator, backwardIterator; 54 | 55 | for (forwardIterator = ccs_HalfedgeTwinID(prevID, depth); 56 | forwardIterator >= 0 && forwardIterator != halfedgeID; 57 | forwardIterator = ccs_HalfedgeTwinID(forwardIterator, depth)) { 58 | const int prevID = ccs_HalfedgePrevID(forwardIterator, depth); 59 | const float prevS = ccs_HalfedgeSharpness(prevID, depth); 60 | const float prevCreaseWeight = sign(prevS); 61 | 62 | // valence computation 63 | ++valence; 64 | 65 | // crease computation 66 | avgS+= prevS; 67 | creaseCount+= prevCreaseWeight; 68 | 69 | // next vertex halfedge 70 | forwardIterator = prevID; 71 | } 72 | 73 | for (backwardIterator = ccs_HalfedgeTwinID(halfedgeID, depth); 74 | forwardIterator < 0 && backwardIterator >= 0 && backwardIterator != halfedgeID; 75 | backwardIterator = ccs_HalfedgeTwinID(backwardIterator, depth)) { 76 | const int nextID = ccs_HalfedgeNextID(backwardIterator, depth); 77 | const float nextS = ccs_HalfedgeSharpness(nextID, depth); 78 | const float nextCreaseWeight = sign(nextS); 79 | 80 | // valence computation 81 | ++valence; 82 | 83 | // crease computation 84 | avgS+= nextS; 85 | creaseCount+= nextCreaseWeight; 86 | 87 | // next vertex halfedge 88 | backwardIterator = nextID; 89 | } 90 | 91 | // corner point 92 | cornerPoint = oldVertexPoint / valence; 93 | 94 | // crease computation: V / 4 95 | creasePoint = (oldVertexPoint + newEdgePoint) * 0.25f * creaseWeight; 96 | 97 | // smooth computation: (4E - F + (n - 3) V) / N 98 | const vec3 E = newEdgePoint; 99 | const vec3 F = newFacePoint; 100 | const vec3 V = oldVertexPoint; 101 | const float N = valence; 102 | smoothPoint = (4.0f * E - F + (N - 3.0f) * V) / (N * N); 103 | 104 | // boundary corrections 105 | if (forwardIterator < 0) { 106 | creaseCount+= creaseWeight; 107 | ++valence; 108 | 109 | creasePoint+= (oldVertexPoint + newPrevEdgePoint) * 0.25f * prevCreaseWeight; 110 | } 111 | 112 | // average sharpness 113 | avgS/= valence; 114 | 115 | // atomicWeight 116 | if (creaseCount >= 3.0f || valence == 2.0f) { 117 | atomicWeight = cornerPoint; 118 | } else if (creaseCount <= 1.0f) { 119 | atomicWeight = smoothPoint; 120 | } else { 121 | atomicWeight = mix(cornerPoint, creasePoint, clamp(avgS, 0.0f, 1.0f)); 122 | } 123 | 124 | WriteVertex(vertexID, atomicWeight, depth); 125 | } 126 | } 127 | 128 | 129 | 130 | --------------------------------------------------------------------------------