295 lines
7.6 KiB
C++
295 lines
7.6 KiB
C++
|
/*
|
||
|
* 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
|
||
|
|