/* * 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/subd_patch_table.h" #include "kernel/kernel_types.h" #include "util/util_math.h" #ifdef WITH_OPENSUBDIV # include #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 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 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 ¶m = 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.data(); 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 ¶m_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); #else (void)patch_table; (void)offset; #endif } void PackedPatchTable::copy_adjusting_offsets(uint *dest, int doffset) { uint *src = table.data(); /* 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