diff options
author | Mai Lavelle <mai.lavelle@gmail.com> | 2016-07-17 05:57:06 +0300 |
---|---|---|
committer | Mai Lavelle <mai.lavelle@gmail.com> | 2016-08-07 18:13:11 +0300 |
commit | 0b68c68006578bb5c2aec3f5fb698087c4fd0f4c (patch) | |
tree | 114d95cd59ed3a60cc994c1723717dd8e969cb7b /intern/cycles/subd/subd_patch_table.cpp | |
parent | fc9747fa89152251cf50b48db311622916d14f8f (diff) |
Cycles microdisplacement: Support for Catmull-Clark subdivision via OpenSubdiv
Enables Catmull-Clark subdivision meshes with support for creases and attribute
subdivision. Still waiting on OpenSubdiv to fully support face varying
interpolation for subdividing uv coordinates tho. Also there may be some
inconsistencies with Blender's subdivision which will be resolved at a
later time.
Code for reading patch tables and creating patch maps is borrowed
from OpenSubdiv.
Reviewed By: brecht
Differential Revision: https://developer.blender.org/D2111
Diffstat (limited to 'intern/cycles/subd/subd_patch_table.cpp')
-rw-r--r-- | intern/cycles/subd/subd_patch_table.cpp | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/intern/cycles/subd/subd_patch_table.cpp b/intern/cycles/subd/subd_patch_table.cpp new file mode 100644 index 00000000000..a32533bbf3e --- /dev/null +++ b/intern/cycles/subd/subd_patch_table.cpp @@ -0,0 +1,294 @@ +/* + * Based on code from OpenSubdiv released under this license: + * + * Copyright 2014 DreamWorks Animation LLC. + * + * Licensed under the Apache License, Version 2.0 (the "Apache License") + * with the following modification; you may not use this file except in + * compliance with the Apache License and the following modification to it: + * Section 6. Trademarks. is deleted and replaced with: + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor + * and its affiliates, except as required to comply with Section 4(c) of + * the License and to reproduce the content of the NOTICE file. + * + * You may obtain a copy of the Apache License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Apache License with the above modification is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the Apache License for the specific + * language governing permissions and limitations under the Apache License. + * + */ + +#include "subd_patch_table.h" +#include "kernel_types.h" + +#include "util_math.h" + +#ifdef WITH_OPENSUBDIV +#include <opensubdiv/far/patchTable.h> +#endif + +CCL_NAMESPACE_BEGIN + +#ifdef WITH_OPENSUBDIV + +using namespace OpenSubdiv; + +/* functions for building patch maps */ + +struct PatchMapQuadNode { + /* sets all the children to point to the patch of index */ + void set_child(int index) + { + for (int i = 0; i < 4; i++) { + children[i] = index | PATCH_MAP_NODE_IS_SET | PATCH_MAP_NODE_IS_LEAF; + } + } + + /* sets the child in quadrant to point to the node or patch of the given index */ + void set_child(unsigned char quadrant, int index, bool is_leaf=true) + { + assert(quadrant < 4); + children[quadrant] = index | PATCH_MAP_NODE_IS_SET | (is_leaf ? PATCH_MAP_NODE_IS_LEAF : 0); + } + + uint children[4]; +}; + +template<class T> +static int resolve_quadrant(T& median, T& u, T& v) +{ + int quadrant = -1; + + if(u < median) { + if(v < median) { + quadrant = 0; + } + else { + quadrant = 1; + v -= median; + } + } + else { + if(v < median) { + quadrant = 3; + } + else { + quadrant = 2; + v -= median; + } + u -= median; + } + + return quadrant; +} + +static void build_patch_map(PackedPatchTable& table, OpenSubdiv::Far::PatchTable* patch_table, int offset) +{ + int num_faces = 0; + + for(int array = 0; array < table.num_arrays; array++) { + Far::ConstPatchParamArray params = patch_table->GetPatchParams(array); + + for(int j = 0; j < patch_table->GetNumPatches(array); j++) { + num_faces = max(num_faces, (int)params[j].GetFaceId()); + } + } + num_faces++; + + vector<PatchMapQuadNode> quadtree; + quadtree.reserve(num_faces + table.num_patches); + quadtree.resize(num_faces); + + /* adjust offsets to make indices relative to the table */ + int handle_index = -(table.num_patches * PATCH_HANDLE_SIZE); + offset += table.total_size(); + + /* populate the quadtree from the FarPatchArrays sub-patches */ + for(int array = 0; array < table.num_arrays; array++) { + Far::ConstPatchParamArray params = patch_table->GetPatchParams(array); + + for(int i = 0; i < patch_table->GetNumPatches(array); i++, handle_index += PATCH_HANDLE_SIZE) { + const Far::PatchParam& param = params[i]; + unsigned short depth = param.GetDepth(); + + PatchMapQuadNode* node = &quadtree[params[i].GetFaceId()]; + + if(depth == (param.NonQuadRoot() ? 1 : 0)) { + /* special case : regular BSpline face w/ no sub-patches */ + node->set_child(handle_index + offset); + continue; + } + + int u = param.GetU(); + int v = param.GetV(); + int pdepth = param.NonQuadRoot() ? depth-2 : depth-1; + int half = 1 << pdepth; + + for(int j = 0; j < depth; j++) { + int delta = half >> 1; + + int quadrant = resolve_quadrant(half, u, v); + assert(quadrant >= 0); + + half = delta; + + if(j == pdepth) { + /* we have reached the depth of the sub-patch : add a leaf */ + assert(!(node->children[quadrant] & PATCH_MAP_NODE_IS_SET)); + node->set_child(quadrant, handle_index + offset, true); + break; + } + else { + /* travel down the child node of the corresponding quadrant */ + if(!(node->children[quadrant] & PATCH_MAP_NODE_IS_SET)) { + /* create a new branch in the quadrant */ + quadtree.push_back(PatchMapQuadNode()); + + int idx = (int)quadtree.size() - 1; + node->set_child(quadrant, idx*4 + offset, false); + + node = &quadtree[idx]; + } + else { + /* travel down an existing branch */ + uint idx = node->children[quadrant] & PATCH_MAP_NODE_INDEX_MASK; + node = &(quadtree[(idx - offset)/4]); + } + } + } + } + } + + /* copy into table */ + assert(table.table.size() == table.total_size()); + uint map_offset = table.total_size(); + + table.num_nodes = quadtree.size() * 4; + table.table.resize(table.total_size()); + + uint* data = &table.table[map_offset]; + + for(int i = 0; i < quadtree.size(); i++) { + for(int j = 0; j < 4; j++) { + assert(quadtree[i].children[j] & PATCH_MAP_NODE_IS_SET); + *(data++) = quadtree[i].children[j]; + } + } +} + +#endif + +/* packed patch table functions */ + +size_t PackedPatchTable::total_size() +{ + return num_arrays * PATCH_ARRAY_SIZE + + num_indices + + num_patches * (PATCH_PARAM_SIZE + PATCH_HANDLE_SIZE) + + num_nodes * PATCH_NODE_SIZE; +} + +void PackedPatchTable::pack(Far::PatchTable* patch_table, int offset) +{ + num_arrays = 0; + num_patches = 0; + num_indices = 0; + num_nodes = 0; + +#ifdef WITH_OPENSUBDIV + num_arrays = patch_table->GetNumPatchArrays(); + + for(int i = 0; i < num_arrays; i++) { + int patches = patch_table->GetNumPatches(i); + int num_control = patch_table->GetPatchArrayDescriptor(i).GetNumControlVertices(); + + num_patches += patches; + num_indices += patches * num_control; + } + + table.resize(total_size()); + uint* data = &table[0]; + + uint* array = data; + uint* index = array + num_arrays * PATCH_ARRAY_SIZE; + uint* param = index + num_indices; + uint* handle = param + num_patches * PATCH_PARAM_SIZE; + + uint current_param = 0; + + for(int i = 0; i < num_arrays; i++) { + *(array++) = patch_table->GetPatchArrayDescriptor(i).GetType(); + *(array++) = patch_table->GetNumPatches(i); + *(array++) = (index - data) + offset; + *(array++) = (param - data) + offset; + + Far::ConstIndexArray indices = patch_table->GetPatchArrayVertices(i); + + for(int j = 0; j < indices.size(); j++) { + *(index++) = indices[j]; + } + + const Far::PatchParamTable& param_table = patch_table->GetPatchParamTable(); + + int num_control = patch_table->GetPatchArrayDescriptor(i).GetNumControlVertices(); + int patches = patch_table->GetNumPatches(i); + + for(int j = 0; j < patches; j++, current_param++) { + *(param++) = param_table[current_param].field0; + *(param++) = param_table[current_param].field1; + + *(handle++) = (array - data) - PATCH_ARRAY_SIZE + offset; + *(handle++) = (param - data) - PATCH_PARAM_SIZE + offset; + *(handle++) = j * num_control; + } + } + + build_patch_map(*this, patch_table, offset); +#endif +} + +void PackedPatchTable::copy_adjusting_offsets(uint* dest, int doffset) +{ + uint* src = &table[0]; + + /* arrays */ + for(int i = 0; i < num_arrays; i++) { + *(dest++) = *(src++); + *(dest++) = *(src++); + *(dest++) = *(src++) + doffset; + *(dest++) = *(src++) + doffset; + } + + /* indices */ + for(int i = 0; i < num_indices; i++) { + *(dest++) = *(src++); + } + + /* params */ + for(int i = 0; i < num_patches; i++) { + *(dest++) = *(src++); + *(dest++) = *(src++); + } + + /* handles */ + for(int i = 0; i < num_patches; i++) { + *(dest++) = *(src++) + doffset; + *(dest++) = *(src++) + doffset; + *(dest++) = *(src++); + } + + /* nodes */ + for(int i = 0; i < num_nodes; i++) { + *(dest++) = *(src++) + doffset; + } +} + +CCL_NAMESPACE_END + |