Cycles microdisplacement: Support for Catmull-Clark subdivision via OpenSubdiv
authorMai Lavelle <mai.lavelle@gmail.com>
Sun, 17 Jul 2016 02:57:06 +0000 (22:57 -0400)
committerMai Lavelle <mai.lavelle@gmail.com>
Sun, 7 Aug 2016 15:13:11 +0000 (11:13 -0400)
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

21 files changed:
CMakeLists.txt
intern/cycles/CMakeLists.txt
intern/cycles/blender/blender_mesh.cpp
intern/cycles/kernel/CMakeLists.txt
intern/cycles/kernel/geom/geom.h
intern/cycles/kernel/geom/geom_attribute.h
intern/cycles/kernel/geom/geom_object.h
intern/cycles/kernel/geom/geom_patch.h [new file with mode: 0644]
intern/cycles/kernel/geom/geom_subd_triangle.h
intern/cycles/kernel/kernel_types.h
intern/cycles/render/attribute.cpp
intern/cycles/render/attribute.h
intern/cycles/render/mesh.cpp
intern/cycles/render/mesh.h
intern/cycles/render/mesh_subdivision.cpp
intern/cycles/render/object.cpp
intern/cycles/render/object.h
intern/cycles/subd/CMakeLists.txt
intern/cycles/subd/subd_patch_table.cpp [new file with mode: 0644]
intern/cycles/subd/subd_patch_table.h [new file with mode: 0644]
intern/cycles/util/util_vector.h

index d29162830910bf8dea5ace66d4ff519e727171f1..24b4cb3a3cc7efde2f2464580b8665a7bac169c0 100644 (file)
@@ -397,6 +397,7 @@ option(WITH_CYCLES                                  "Enable Cycles Render Engine" ON)
 option(WITH_CYCLES_STANDALONE          "Build Cycles standalone application" OFF)
 option(WITH_CYCLES_STANDALONE_GUI      "Build Cycles standalone with GUI" OFF)
 option(WITH_CYCLES_OSL                         "Build Cycles with OSL support" ${_init_CYCLES_OSL})
+option(WITH_CYCLES_OPENSUBDIV          "Build Cycles with OpenSubdiv support" ON)
 option(WITH_CYCLES_CUDA_BINARIES       "Build Cycles CUDA binaries" OFF)
 set(CYCLES_CUDA_BINARIES_ARCH sm_20 sm_21 sm_30 sm_35 sm_37 sm_50 sm_52 CACHE STRING "CUDA architectures to build binaries for")
 mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH)
@@ -2520,6 +2521,11 @@ if(WITH_CYCLES)
                        )
                endif()
        endif()
+
+       if(WITH_CYCLES_OPENSUBDIV AND NOT WITH_OPENSUBDIV)
+               message(STATUS "WITH_CYCLES_OPENSUBDIV requires WITH_OPENSUBDIV to be ON, turning OFF")
+               set(WITH_CYCLES_OPENSUBDIV OFF)
+       endif()
 endif()
 
 if(WITH_INTERNATIONAL)
index 3b410b2a1e1751e07304b84394f9212489c34ff4..97854a88e84f3bd91816c2cbdc12ea8aa6642614 100644 (file)
@@ -146,6 +146,14 @@ if(WITH_CYCLES_OSL)
        )
 endif()
 
+if(WITH_CYCLES_OPENSUBDIV)
+       add_definitions(-DWITH_OPENSUBDIV)
+       include_directories(
+               SYSTEM
+               ${OPENSUBDIV_INCLUDE_DIR}
+       )
+endif()
+
 set(WITH_CYCLES_DEVICE_OPENCL TRUE)
 set(WITH_CYCLES_DEVICE_CUDA TRUE)
 set(WITH_CYCLES_DEVICE_MULTI TRUE)
index 74fd4cb44a0c30529653f1ee04c53c3550921bc6..b4c490c0d330c24dbf9733b0efa3674d005efdda 100644 (file)
@@ -409,7 +409,8 @@ static void attr_create_uv_map(Scene *scene,
                                BL::Mesh& b_mesh,
                                const vector<int>& nverts,
                                const vector<int>& face_flags,
-                               bool subdivision)
+                               bool subdivision,
+                               bool subdivide_uvs)
 {
        if(subdivision) {
                BL::Mesh::uv_layers_iterator l;
@@ -429,6 +430,10 @@ static void attr_create_uv_map(Scene *scene,
                                else
                                        attr = mesh->subd_attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CORNER);
 
+                               if(subdivide_uvs) {
+                                       attr->flags |= ATTR_SUBDIVIDED;
+                               }
+
                                BL::Mesh::polygons_iterator p;
                                float3 *fdata = attr->data_float3();
 
@@ -592,7 +597,8 @@ static void create_mesh(Scene *scene,
                         Mesh *mesh,
                         BL::Mesh& b_mesh,
                         const vector<Shader*>& used_shaders,
-                        bool subdivision=false)
+                        bool subdivision=false,
+                        bool subdivide_uvs=true)
 {
        /* count vertices and faces */
        int numverts = b_mesh.vertices.length();
@@ -638,6 +644,7 @@ static void create_mesh(Scene *scene,
        /* create generated coordinates from undeformed coordinates */
        if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
                Attribute *attr = attributes.add(ATTR_STD_GENERATED);
+               attr->flags |= ATTR_SUBDIVIDED;
 
                float3 loc, size;
                mesh_texture_space(b_mesh, loc, size);
@@ -746,7 +753,7 @@ static void create_mesh(Scene *scene,
         * The calculate functions will check whether they're needed or not.
         */
        attr_create_vertex_color(scene, mesh, b_mesh, nverts, face_flags, subdivision);
-       attr_create_uv_map(scene, mesh, b_mesh, nverts, face_flags, subdivision);
+       attr_create_uv_map(scene, mesh, b_mesh, nverts, face_flags, subdivision, subdivide_uvs);
 
        /* for volume objects, create a matrix to transform from object space to
         * mesh texture space. this does not work with deformations but that can
@@ -770,8 +777,35 @@ static void create_subd_mesh(Scene *scene,
                              float dicing_rate,
                              int max_subdivisions)
 {
-       create_mesh(scene, mesh, b_mesh, used_shaders, true);
+       BL::SubsurfModifier subsurf_mod(b_ob.modifiers[b_ob.modifiers.length()-1]);
+       bool subdivide_uvs = subsurf_mod.use_subsurf_uv();
+
+       create_mesh(scene, mesh, b_mesh, used_shaders, true, subdivide_uvs);
+
+       /* export creases */
+       size_t num_creases = 0;
+       BL::Mesh::edges_iterator e;
+
+       for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) {
+               if(e->crease() != 0.0f) {
+                       num_creases++;
+               }
+       }
+
+       mesh->subd_creases.resize(num_creases);
+
+       Mesh::SubdEdgeCrease* crease = mesh->subd_creases.data();
+       for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) {
+               if(e->crease() != 0.0f) {
+                       crease->v[0] = e->vertices()[0];
+                       crease->v[1] = e->vertices()[1];
+                       crease->crease = e->crease();
+
+                       crease++;
+               }
+       }
 
+       /* set subd params */
        SubdParams sdparams(mesh);
 
        PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles");
index 42298a0811dbdacc759e6830eb9cdd487ee5e38e..7bef247d3bd667e6ce6169151f6cb7da7f4ec17f 100644 (file)
@@ -162,6 +162,7 @@ set(SRC_GEOM_HEADERS
        geom/geom_motion_curve.h
        geom/geom_motion_triangle.h
        geom/geom_object.h
+       geom/geom_patch.h
        geom/geom_primitive.h
        geom/geom_subd_triangle.h
        geom/geom_triangle.h
index 493afdc4f62fbd096b5462d7dcc2c10039ab0c0b..11548324e18fe44be3398eb8182068890fd06f74 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "geom_attribute.h"
 #include "geom_object.h"
+#include "geom_patch.h"
 #include "geom_triangle.h"
 #include "geom_subd_triangle.h"
 #include "geom_triangle_intersect.h"
index e036c2752d71ce7217449c81673c764f8a0ca6bb..8604d30ad346a43233eb4c034e64a27ac4e3ea65 100644 (file)
@@ -45,7 +45,7 @@ ccl_device_inline uint attribute_primitive_type(KernelGlobals *kg, const ShaderD
 
 ccl_device_inline AttributeDescriptor attribute_not_found()
 {
-       const AttributeDescriptor desc = {ATTR_ELEMENT_NONE, (NodeAttributeType)0, ATTR_STD_NOT_FOUND};
+       const AttributeDescriptor desc = {ATTR_ELEMENT_NONE, (NodeAttributeType)0, 0, ATTR_STD_NOT_FOUND};
        return desc;
 }
 
@@ -79,7 +79,8 @@ ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals *kg, const Sh
 
        /* return result */
        desc.offset = (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z;
-       desc.type = (NodeAttributeType)attr_map.w;
+       desc.type = (NodeAttributeType)(attr_map.w & 0xff);
+       desc.flags = (AttributeFlag)(attr_map.w >> 8);
 
        return desc;
 }
index c0d15a95954d4be54fc526b8f8ce2b605a5c887d..883c5dc100d39777bacdb0f3cfa769f6e75713e7 100644 (file)
@@ -292,6 +292,18 @@ ccl_device_inline void object_motion_info(KernelGlobals *kg, int object, int *nu
                *numverts = __float_as_int(f.w);
 }
 
+/* Offset to an objects patch map */
+
+ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object)
+{
+       if(object == OBJECT_NONE)
+               return 0;
+
+       int offset = object*OBJECT_SIZE + 11;
+       float4 f = kernel_tex_fetch(__objects, offset);
+       return __float_as_uint(f.x);
+}
+
 /* Pass ID for shader */
 
 ccl_device int shader_pass_id(KernelGlobals *kg, const ShaderData *sd)
diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/geom_patch.h
new file mode 100644 (file)
index 0000000..6a0ff5a
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Based on code from OpenSubdiv released under this license:
+ *
+ * Copyright 2013 Pixar
+ *
+ * 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.
+ *
+ */
+
+CCL_NAMESPACE_BEGIN
+
+typedef struct PatchHandle {
+       int array_index, patch_index, vert_index;
+} PatchHandle;
+
+ccl_device_inline int patch_map_resolve_quadrant(float median, float *u, float *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;
+}
+
+/* retrieve PatchHandle from patch coords */
+
+ccl_device_inline PatchHandle patch_map_find_patch(KernelGlobals *kg, int object, int patch, float u, float v)
+{
+       PatchHandle handle;
+
+       kernel_assert((u >= 0.0f) && (u <= 1.0f) && (v >= 0.0f) && (v <= 1.0f));
+
+       int node = (object_patch_map_offset(kg, object) + patch)/2;
+       float median = 0.5f;
+
+       for(int depth = 0; depth < 0xff; depth++) {
+               float delta = median * 0.5f;
+
+               int quadrant = patch_map_resolve_quadrant(median, &u, &v);
+               kernel_assert(quadrant >= 0);
+
+               uint child = kernel_tex_fetch(__patches, node + quadrant);
+
+               /* is the quadrant a hole? */
+               if(!(child & PATCH_MAP_NODE_IS_SET)) {
+                       handle.array_index = -1;
+                       return handle;
+               }
+
+               uint index = child & PATCH_MAP_NODE_INDEX_MASK;
+
+               if(child & PATCH_MAP_NODE_IS_LEAF) {
+                       handle.array_index = kernel_tex_fetch(__patches, index + 0);
+                       handle.patch_index = kernel_tex_fetch(__patches, index + 1);
+                       handle.vert_index = kernel_tex_fetch(__patches, index + 2);
+
+                       return handle;
+               } else {
+                       node = index;
+               }
+
+               median = delta;
+       }
+
+       /* no leaf found */
+       kernel_assert(0);
+
+       handle.array_index = -1;
+       return handle;
+}
+
+ccl_device_inline void patch_eval_bspline_weights(float t, float *point, float *deriv)
+{
+       /* The four uniform cubic B-Spline basis functions evaluated at t */
+       float inv_6 = 1.0f / 6.0f;
+
+       float t2 = t * t;
+       float t3 = t * t2;
+
+       point[0] = inv_6 * (1.0f - 3.0f*(t - t2) - t3);
+       point[1] = inv_6 * (4.0f - 6.0f*t2 + 3.0f*t3);
+       point[2] = inv_6 * (1.0f + 3.0f*(t + t2 - t3));
+       point[3] = inv_6 * t3;
+
+       /* Derivatives of the above four basis functions at t */
+       deriv[0] = -0.5f*t2 + t - 0.5f;
+       deriv[1] =  1.5f*t2 - 2.0f*t;
+       deriv[2] = -1.5f*t2 + t + 0.5f;
+       deriv[3] =  0.5f*t2;
+}
+
+ccl_device_inline void patch_eval_adjust_boundary_weights(uint bits, float *s, float *t)
+{
+       int boundary = ((bits >> 8) & 0xf);
+
+       if(boundary & 1) {
+               t[2] -= t[0];
+               t[1] += 2*t[0];
+               t[0] = 0;
+       }
+
+       if(boundary & 2) {
+               s[1] -= s[3];
+               s[2] += 2*s[3];
+               s[3] = 0;
+       }
+
+       if(boundary & 4) {
+               t[1] -= t[3];
+               t[2] += 2*t[3];
+               t[3] = 0;
+       }
+
+       if(boundary & 8) {
+               s[2] -= s[0];
+               s[1] += 2*s[0];
+               s[0] = 0;
+       }
+}
+
+ccl_device_inline int patch_eval_depth(uint patch_bits)
+{
+       return (patch_bits & 0xf);
+}
+
+ccl_device_inline float patch_eval_param_fraction(uint patch_bits)
+{
+       bool non_quad_root = (patch_bits >> 4) & 0x1;
+       int depth = patch_eval_depth(patch_bits);
+
+       if(non_quad_root) {
+               return 1.0f / (float)(1 << (depth-1));
+       }
+       else {
+               return 1.0f / (float)(1 << depth);
+       }
+}
+
+ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, float *u, float *v)
+{
+       float frac = patch_eval_param_fraction(patch_bits);
+
+       int iu = (patch_bits >> 22) & 0x3ff;
+       int iv = (patch_bits >> 12) & 0x3ff;
+
+       /* top left corner */
+       float pu = (float)iu*frac;
+       float pv = (float)iv*frac;
+
+       /* normalize uv coordinates */
+       *u = (*u - pu) / frac;
+       *v = (*v - pv) / frac;
+}
+
+/* retrieve patch control indices */
+
+ccl_device_inline int patch_eval_indices(KernelGlobals *kg, const PatchHandle *handle, int channel,
+                                         int indices[PATCH_MAX_CONTROL_VERTS])
+{
+       int index_base = kernel_tex_fetch(__patches, handle->array_index + 2) + handle->vert_index;
+
+       /* XXX: regular patches only */
+       for(int i = 0; i < 16; i++) {
+               indices[i] = kernel_tex_fetch(__patches, index_base + i);
+       }
+
+       return 16;
+}
+
+/* evaluate patch basis functions */
+
+ccl_device_inline void patch_eval_basis(KernelGlobals *kg, const PatchHandle *handle, float u, float v,
+                                float weights[PATCH_MAX_CONTROL_VERTS],
+                                float weights_du[PATCH_MAX_CONTROL_VERTS],
+                                float weights_dv[PATCH_MAX_CONTROL_VERTS])
+{
+       uint patch_bits = kernel_tex_fetch(__patches, handle->patch_index + 1); /* read patch param */
+       float d_scale = 1 << patch_eval_depth(patch_bits);
+
+       bool non_quad_root = (patch_bits >> 4) & 0x1;
+       if(non_quad_root) {
+               d_scale *= 0.5f;
+       }
+
+       patch_eval_normalize_coords(patch_bits, &u, &v);
+
+       /* XXX: regular patches only for now. */
+
+       float s[4], t[4], ds[4], dt[4];
+
+       patch_eval_bspline_weights(u, s, ds);
+       patch_eval_bspline_weights(v, t, dt);
+
+       patch_eval_adjust_boundary_weights(patch_bits, s, t);
+       patch_eval_adjust_boundary_weights(patch_bits, ds, dt);
+
+       for(int k = 0; k < 4; k++) {
+               for(int l = 0; l < 4; l++) {
+                       weights[4*k+l] = s[l] * t[k];
+                       weights_du[4*k+l] = ds[l] * t[k] * d_scale;
+                       weights_dv[4*k+l] = s[l] * dt[k] * d_scale;
+               }
+       }
+}
+
+/* generic function for evaluating indices and weights from patch coords */
+
+ccl_device_inline int patch_eval_control_verts(KernelGlobals *kg, int object, int patch, float u, float v, int channel,
+                                        int indices[PATCH_MAX_CONTROL_VERTS],
+                                        float weights[PATCH_MAX_CONTROL_VERTS],
+                                        float weights_du[PATCH_MAX_CONTROL_VERTS],
+                                        float weights_dv[PATCH_MAX_CONTROL_VERTS])
+{
+       PatchHandle handle = patch_map_find_patch(kg, object, patch, u, v);
+       kernel_assert(handle.array_index >= 0);
+
+       int num_control = patch_eval_indices(kg, &handle, channel, indices);
+       patch_eval_basis(kg, &handle, u, v, weights, weights_du, weights_dv);
+
+       return num_control;
+}
+
+/* functions for evaluating attributes on patches */
+
+ccl_device float patch_eval_float(KernelGlobals *kg, const ShaderData *sd, int offset,
+                                  int patch, float u, float v, int channel,
+                                  float *du, float* dv)
+{
+       int indices[PATCH_MAX_CONTROL_VERTS];
+       float weights[PATCH_MAX_CONTROL_VERTS];
+       float weights_du[PATCH_MAX_CONTROL_VERTS];
+       float weights_dv[PATCH_MAX_CONTROL_VERTS];
+
+       int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel,
+                                                  indices, weights, weights_du, weights_dv);
+
+       float val = 0.0f;
+       if(du) *du = 0.0f;
+       if(dv) *dv = 0.0f;
+
+       for(int i = 0; i < num_control; i++) {
+               float v = kernel_tex_fetch(__attributes_float, offset + indices[i]);
+
+               val += v * weights[i];
+               if(du) *du += v * weights_du[i];
+               if(dv) *dv += v * weights_dv[i];
+       }
+
+       return val;
+}
+
+ccl_device float3 patch_eval_float3(KernelGlobals *kg, const ShaderData *sd, int offset,
+                                    int patch, float u, float v, int channel,
+                                    float3 *du, float3 *dv)
+{
+       int indices[PATCH_MAX_CONTROL_VERTS];
+       float weights[PATCH_MAX_CONTROL_VERTS];
+       float weights_du[PATCH_MAX_CONTROL_VERTS];
+       float weights_dv[PATCH_MAX_CONTROL_VERTS];
+
+       int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel,
+                                                  indices, weights, weights_du, weights_dv);
+
+       float3 val = make_float3(0.0f, 0.0f, 0.0f);
+       if(du) *du = make_float3(0.0f, 0.0f, 0.0f);
+       if(dv) *dv = make_float3(0.0f, 0.0f, 0.0f);
+
+       for(int i = 0; i < num_control; i++) {
+               float3 v = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + indices[i]));
+
+               val += v * weights[i];
+               if(du) *du += v * weights_du[i];
+               if(dv) *dv += v * weights_dv[i];
+       }
+
+       return val;
+}
+
+ccl_device float3 patch_eval_uchar4(KernelGlobals *kg, const ShaderData *sd, int offset,
+                                    int patch, float u, float v, int channel,
+                                    float3 *du, float3 *dv)
+{
+       int indices[PATCH_MAX_CONTROL_VERTS];
+       float weights[PATCH_MAX_CONTROL_VERTS];
+       float weights_du[PATCH_MAX_CONTROL_VERTS];
+       float weights_dv[PATCH_MAX_CONTROL_VERTS];
+
+       int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel,
+                                                  indices, weights, weights_du, weights_dv);
+
+       float3 val = make_float3(0.0f, 0.0f, 0.0f);
+       if(du) *du = make_float3(0.0f, 0.0f, 0.0f);
+       if(dv) *dv = make_float3(0.0f, 0.0f, 0.0f);
+
+       for(int i = 0; i < num_control; i++) {
+               float3 v = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, offset + indices[i]));
+
+               val += v * weights[i];
+               if(du) *du += v * weights_du[i];
+               if(dv) *dv += v * weights_dv[i];
+       }
+
+       return val;
+}
+
+CCL_NAMESPACE_END
+
index 79a2ce5cc7028e9188f72405762bce0e5f1da15b..fccacf435f9852e3a35ed5e4bfbc0709f3a9d5e0 100644 (file)
@@ -97,11 +97,54 @@ ccl_device_inline void subd_triangle_patch_corners(KernelGlobals *kg, int patch,
 
 /* Reading attributes on various subdivision triangle elements */
 
-ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy)
+ccl_device_noinline float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy)
 {
        int patch = subd_triangle_patch(kg, sd);
 
-       if(desc.element == ATTR_ELEMENT_FACE) {
+       if(desc.flags & ATTR_SUBDIVIDED) {
+               float2 uv[3];
+               subd_triangle_patch_uv(kg, sd, uv);
+
+               float2 dpdu = uv[0] - uv[2];
+               float2 dpdv = uv[1] - uv[2];
+
+               /* p is [s, t] */
+               float2 p = dpdu * ccl_fetch(sd, u) + dpdv * ccl_fetch(sd, v) + uv[2];
+
+               float a, dads, dadt;
+               a = patch_eval_float(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
+
+#ifdef __RAY_DIFFERENTIALS__
+               if(dx || dy) {
+                       float dsdu = dpdu.x;
+                       float dtdu = dpdu.y;
+                       float dsdv = dpdv.x;
+                       float dtdv = dpdv.y;
+
+                       if(dx) {
+                               float dudx = ccl_fetch(sd, du).dx;
+                               float dvdx = ccl_fetch(sd, dv).dx;
+
+                               float dsdx = dsdu*dudx + dsdv*dvdx;
+                               float dtdx = dtdu*dudx + dtdv*dvdx;
+
+                               *dx = dads*dsdx + dadt*dtdx;
+                       }
+                       if(dy) {
+                               float dudy = ccl_fetch(sd, du).dy;
+                               float dvdy = ccl_fetch(sd, dv).dy;
+
+                               float dsdy = dsdu*dudy + dsdv*dvdy;
+                               float dtdy = dtdu*dudy + dtdv*dvdy;
+
+                               *dy = dads*dsdy + dadt*dtdy;
+                       }
+               }
+#endif
+
+               return a;
+       }
+       else if(desc.element == ATTR_ELEMENT_FACE) {
                if(dx) *dx = 0.0f;
                if(dy) *dy = 0.0f;
 
@@ -110,9 +153,8 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa
        else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
                float2 uv[3];
                subd_triangle_patch_uv(kg, sd, uv);
-               uint4 v = subd_triangle_patch_indices(kg, patch);
 
-               float a, b, c;
+               uint4 v = subd_triangle_patch_indices(kg, patch);
 
                float f0 = kernel_tex_fetch(__attributes_float, desc.offset + v.x);
                float f1 = kernel_tex_fetch(__attributes_float, desc.offset + v.y);
@@ -124,9 +166,9 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa
                        f3 = (f3+f0)*0.5f;
                }
 
-               a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
-               b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
-               c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+               float a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
+               float b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
+               float c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
 
 #ifdef __RAY_DIFFERENTIALS__
                if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c;
@@ -136,13 +178,11 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa
                return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c;
        }
        else if(desc.element == ATTR_ELEMENT_CORNER) {
-               int corners[4];
-               subd_triangle_patch_corners(kg, patch, corners);
-
                float2 uv[3];
                subd_triangle_patch_uv(kg, sd, uv);
 
-               float a, b, c;
+               int corners[4];
+               subd_triangle_patch_corners(kg, patch, corners);
 
                float f0 = kernel_tex_fetch(__attributes_float, corners[0] + desc.offset);
                float f1 = kernel_tex_fetch(__attributes_float, corners[1] + desc.offset);
@@ -154,9 +194,9 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa
                        f3 = (f3+f0)*0.5f;
                }
 
-               a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
-               b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
-               c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+               float a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
+               float b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
+               float c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
 
 #ifdef __RAY_DIFFERENTIALS__
                if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c;
@@ -173,11 +213,60 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa
        }
 }
 
-ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy)
+ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy)
 {
        int patch = subd_triangle_patch(kg, sd);
 
-       if(desc.element == ATTR_ELEMENT_FACE) {
+       if(desc.flags & ATTR_SUBDIVIDED) {
+               float2 uv[3];
+               subd_triangle_patch_uv(kg, sd, uv);
+
+               float2 dpdu = uv[0] - uv[2];
+               float2 dpdv = uv[1] - uv[2];
+
+               /* p is [s, t] */
+               float2 p = dpdu * ccl_fetch(sd, u) + dpdv * ccl_fetch(sd, v) + uv[2];
+
+               float3 a, dads, dadt;
+
+               if(desc.element == ATTR_ELEMENT_CORNER_BYTE) {
+                       a = patch_eval_uchar4(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
+               }
+               else {
+                       a = patch_eval_float3(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
+               }
+
+#ifdef __RAY_DIFFERENTIALS__
+               if(dx || dy) {
+                       float dsdu = dpdu.x;
+                       float dtdu = dpdu.y;
+                       float dsdv = dpdv.x;
+                       float dtdv = dpdv.y;
+
+                       if(dx) {
+                               float dudx = ccl_fetch(sd, du).dx;
+                               float dvdx = ccl_fetch(sd, dv).dx;
+
+                               float dsdx = dsdu*dudx + dsdv*dvdx;
+                               float dtdx = dtdu*dudx + dtdv*dvdx;
+
+                               *dx = dads*dsdx + dadt*dtdx;
+                       }
+                       if(dy) {
+                               float dudy = ccl_fetch(sd, du).dy;
+                               float dvdy = ccl_fetch(sd, dv).dy;
+
+                               float dsdy = dsdu*dudy + dsdv*dvdy;
+                               float dtdy = dtdu*dudy + dtdv*dvdy;
+
+                               *dy = dads*dsdy + dadt*dtdy;
+                       }
+               }
+#endif
+
+               return a;
+       }
+       else if(desc.element == ATTR_ELEMENT_FACE) {
                if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
                if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
 
@@ -186,9 +275,8 @@ ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const Shader
        else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
                float2 uv[3];
                subd_triangle_patch_uv(kg, sd, uv);
-               uint4 v = subd_triangle_patch_indices(kg, patch);
 
-               float3 a, b, c;
+               uint4 v = subd_triangle_patch_indices(kg, patch);
 
                float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.x));
                float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.y));
@@ -200,9 +288,9 @@ ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const Shader
                        f3 = (f3+f0)*0.5f;
                }
 
-               a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
-               b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
-               c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+               float3 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
+               float3 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
+               float3 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
 
 #ifdef __RAY_DIFFERENTIALS__
                if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c;
@@ -212,13 +300,12 @@ ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const Shader
                return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c;
        }
        else if(desc.element == ATTR_ELEMENT_CORNER || desc.element == ATTR_ELEMENT_CORNER_BYTE) {
-               int corners[4];
-               subd_triangle_patch_corners(kg, patch, corners);
-
                float2 uv[3];
                subd_triangle_patch_uv(kg, sd, uv);
 
-               float3 a, b, c;
+               int corners[4];
+               subd_triangle_patch_corners(kg, patch, corners);
+
                float3 f0, f1, f2, f3;
 
                if(desc.element == ATTR_ELEMENT_CORNER) {
@@ -239,9 +326,9 @@ ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const Shader
                        f3 = (f3+f0)*0.5f;
                }
 
-               a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
-               b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
-               c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+               float3 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
+               float3 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
+               float3 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
 
 #ifdef __RAY_DIFFERENTIALS__
                if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c;
index 57b116e1dd75f5a024b6e1b4c6aa2c8e83123275..2d4603c895d79e29f24237a0e781e5a7d0792599 100644 (file)
@@ -34,7 +34,7 @@
 CCL_NAMESPACE_BEGIN
 
 /* constants */
-#define OBJECT_SIZE            11
+#define OBJECT_SIZE            12
 #define OBJECT_VECTOR_SIZE     6
 #define LIGHT_SIZE                     5
 #define FILTER_TABLE_SIZE      1024
@@ -624,9 +624,15 @@ typedef enum AttributeStandard {
        ATTR_STD_NOT_FOUND = ~0
 } AttributeStandard;
 
+typedef enum AttributeFlag {
+       ATTR_FINAL_SIZE = (1 << 0),
+       ATTR_SUBDIVIDED = (1 << 1),
+} AttributeFlag;
+
 typedef struct AttributeDescriptor {
        AttributeElement element;
        NodeAttributeType type;
+       uint flags; /* see enum AttributeFlag */
        int offset;
 } AttributeDescriptor;
 
@@ -1245,6 +1251,16 @@ enum RayState {
 #define REMOVE_RAY_FLAG(ray_state, ray_index, flag) (ray_state[ray_index] = (ray_state[ray_index] & (~flag)))
 #define IS_FLAG(ray_state, ray_index, flag) (ray_state[ray_index] & flag)
 
+/* Patches */
+
+#define PATCH_MAX_CONTROL_VERTS 16
+
+/* Patch map node flags */
+
+#define PATCH_MAP_NODE_IS_SET (1 << 30)
+#define PATCH_MAP_NODE_IS_LEAF (1 << 31)
+#define PATCH_MAP_NODE_INDEX_MASK (~(PATCH_MAP_NODE_IS_SET | PATCH_MAP_NODE_IS_LEAF))
+
 CCL_NAMESPACE_END
 
 #endif /*  __KERNEL_TYPES_H__ */
index a77ae1121a17863de1048373f66105d707a20e22..c0d429a583c9b222e6c836d3fe5ad449d2676231 100644 (file)
@@ -44,6 +44,7 @@ void Attribute::set(ustring name_, TypeDesc type_, AttributeElement element_)
        type = type_;
        element = element_;
        std = ATTR_STD_NONE;
+       flags = 0;
 
        /* string and matrix not supported! */
        assert(type == TypeDesc::TypeFloat || type == TypeDesc::TypeColor ||
@@ -61,6 +62,11 @@ void Attribute::resize(Mesh *mesh, AttributePrimitive prim, bool reserve_only)
        }
 }
 
+void Attribute::resize(size_t num_elements)
+{
+       buffer.resize(num_elements * data_sizeof(), 0);
+}
+
 void Attribute::add(const float& f)
 {
        char *data = (char*)&f;
@@ -130,6 +136,10 @@ size_t Attribute::data_sizeof() const
 
 size_t Attribute::element_size(Mesh *mesh, AttributePrimitive prim) const
 {
+       if(flags & ATTR_FINAL_SIZE) {
+               return buffer.size() / data_sizeof();
+       }
+
        size_t size;
 
        switch(element) {
index 67d067d60c9123d02fb7839d753c22372b674e36..f4538c76369b2e17b16cd3e53a83beb965aca251 100644 (file)
@@ -54,11 +54,13 @@ public:
        TypeDesc type;
        vector<char> buffer;
        AttributeElement element;
+       uint flags; /* enum AttributeFlag */
 
        Attribute() {}
        ~Attribute();
        void set(ustring name, TypeDesc type, AttributeElement element);
        void resize(Mesh *mesh, AttributePrimitive prim, bool reserve_only);
+       void resize(size_t num_elements);
 
        size_t data_sizeof() const;
        size_t element_size(Mesh *mesh, AttributePrimitive prim) const;
index 2edec40b13105e690bd608b1c748c3a716dea0ac..0a8122199443d77c0bd3443ad2e34d882b8166f9 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "osl_globals.h"
 
+#include "subd_patch_table.h"
+
 #include "util_foreach.h"
 #include "util_logging.h"
 #include "util_progress.h"
@@ -112,7 +114,6 @@ float3 Mesh::SubdFace::normal(const Mesh *mesh) const
        return safe_normalize(cross(v1 - v0, v2 - v0));
 }
 
-
 /* Mesh */
 
 NODE_DEFINE(Mesh)
@@ -177,11 +178,14 @@ Mesh::Mesh()
        num_ngons = 0;
 
        subdivision_type = SUBDIVISION_NONE;
+
+       patch_table = NULL;
 }
 
 Mesh::~Mesh()
 {
        delete bvh;
+       delete patch_table;
 }
 
 void Mesh::resize_mesh(int numverts, int numtris)
@@ -274,6 +278,8 @@ void Mesh::clear()
 
        num_subd_verts = 0;
 
+       subd_creases.clear();
+
        attributes.clear();
        curve_attributes.clear();
        subd_attributes.clear();
@@ -283,6 +289,9 @@ void Mesh::clear()
        transform_negative_scaled = false;
        transform_normal = transform_identity();
        geometry_flags = GEOMETRY_NONE;
+
+       delete patch_table;
+       patch_table = NULL;
 }
 
 int Mesh::split_vertex(int vertex)
@@ -705,7 +714,6 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
        }
 }
 
-
 void Mesh::compute_bvh(DeviceScene *dscene,
                        SceneParams *params,
                        Progress *progress,
@@ -834,6 +842,7 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
                        osl_attr.desc.element = ATTR_ELEMENT_OBJECT;
                        osl_attr.value = attr;
                        osl_attr.desc.offset = 0;
+                       osl_attr.desc.flags = 0;
 
                        og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_TRIANGLE][attr.name()] = osl_attr;
                        og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_CURVE][attr.name()] = osl_attr;
@@ -977,6 +986,8 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
                                        attr_map[index].w = NODE_ATTR_MATRIX;
                                else
                                        attr_map[index].w = NODE_ATTR_FLOAT3;
+
+                               attr_map[index].w |= req.triangle_desc.flags << 8;
                        }
 
                        index++;
@@ -992,6 +1003,8 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
                                        attr_map[index].w = NODE_ATTR_MATRIX;
                                else
                                        attr_map[index].w = NODE_ATTR_FLOAT3;
+
+                               attr_map[index].w |= req.curve_desc.flags << 8;
                        }
 
                        index++;
@@ -1007,6 +1020,8 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
                                        attr_map[index].w = NODE_ATTR_MATRIX;
                                else
                                        attr_map[index].w = NODE_ATTR_FLOAT3;
+
+                               attr_map[index].w |= req.subd_desc.flags << 8;
                        }
 
                        index++;
@@ -1071,6 +1086,7 @@ static void update_attribute_element_offset(Mesh *mesh,
        if(mattr) {
                /* store element and type */
                desc.element = mattr->element;
+               desc.flags = mattr->flags;
                type = mattr->type;
 
                /* store attribute data in arrays */
@@ -1127,7 +1143,11 @@ static void update_attribute_element_offset(Mesh *mesh,
 
                /* mesh vertex/curve index is global, not per object, so we sneak
                 * a correction for that in here */
-               if(element == ATTR_ELEMENT_VERTEX)
+               if(mesh->subdivision_type == Mesh::SUBDIVISION_CATMULL_CLARK && desc.flags & ATTR_SUBDIVIDED) {
+                       /* indices for subdivided attributes are retrieved
+                        * from patch table so no need for correction here*/
+               }
+               else if(element == ATTR_ELEMENT_VERTEX)
                        offset -= mesh->vert_offset;
                else if(element == ATTR_ELEMENT_VERTEX_MOTION)
                        offset -= mesh->vert_offset;
@@ -1323,6 +1343,12 @@ void MeshManager::mesh_calc_offset(Scene *scene)
                if(mesh->subd_faces.size()) {
                        Mesh::SubdFace& last = mesh->subd_faces[mesh->subd_faces.size()-1];
                        patch_size += (last.ptex_offset + last.num_ptex_faces()) * 8;
+
+                       /* patch tables are stored in same array so include them in patch_size */
+                       if(mesh->patch_table) {
+                               mesh->patch_table_offset = patch_size;
+                               patch_size += mesh->patch_table->total_size();
+                       }
                }
                face_size += mesh->subd_faces.size();
                corner_size += mesh->subd_face_corners.size();
@@ -1354,6 +1380,12 @@ void MeshManager::device_update_mesh(Device *device,
                if(mesh->subd_faces.size()) {
                        Mesh::SubdFace& last = mesh->subd_faces[mesh->subd_faces.size()-1];
                        patch_size += (last.ptex_offset + last.num_ptex_faces()) * 8;
+
+                       /* patch tables are stored in same array so include them in patch_size */
+                       if(mesh->patch_table) {
+                               mesh->patch_table_offset = patch_size;
+                               patch_size += mesh->patch_table->total_size();
+                       }
                }
        }
 
@@ -1436,6 +1468,11 @@ void MeshManager::device_update_mesh(Device *device,
 
                foreach(Mesh *mesh, scene->meshes) {
                        mesh->pack_patches(&patch_data[mesh->patch_offset], mesh->vert_offset, mesh->face_offset, mesh->corner_offset);
+
+                       if(mesh->patch_table) {
+                               mesh->patch_table->copy_adjusting_offsets(&patch_data[mesh->patch_table_offset], mesh->patch_table_offset);
+                       }
+
                        if(progress.get_cancel()) return;
                }
 
@@ -1648,6 +1685,10 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
        }
        if(progress.get_cancel()) return;
 
+       /* after mesh data has been copied to device memory we need to update
+        * offsets for patch tables as this can't be known before hand */
+       scene->object_manager->device_update_patch_map_offsets(device, dscene, scene);
+
        device_update_attributes(device, dscene, scene, progress);
        if(progress.get_cancel()) return;
 
index c9ae9aab8889db215a5f704b2b922be32d91cdbc..2436d6aa23177182c45851d60d786b6a88624c6f 100644 (file)
@@ -40,6 +40,7 @@ class Scene;
 class SceneParams;
 class AttributeRequest;
 class DiagSplit;
+struct PackedPatchTable;
 
 /* Mesh */
 
@@ -110,6 +111,11 @@ public:
                int num_ptex_faces() const { return num_corners == 4 ? 1 : num_corners; }
        };
 
+       struct SubdEdgeCrease {
+               int v[2];
+               float crease;
+       };
+
        /* Displacement */
        enum DisplacementMethod {
                DISPLACE_BUMP = 0,
@@ -157,6 +163,8 @@ public:
        array<int> subd_face_corners;
        int num_ngons;
 
+       array<SubdEdgeCrease> subd_creases;
+
        vector<Shader*> used_shaders;
        AttributeSet attributes;
        AttributeSet curve_attributes;
@@ -168,6 +176,8 @@ public:
        Transform transform_normal;
        DisplacementMethod displacement_method;
 
+       PackedPatchTable *patch_table;
+
        uint motion_steps;
        bool use_motion_blur;
 
@@ -184,6 +194,7 @@ public:
        size_t curvekey_offset;
 
        size_t patch_offset;
+       size_t patch_table_offset;
        size_t face_offset;
        size_t corner_offset;
 
index fe8e41e8d35570d025a68fb642f01257aba4661c..efb40efbb79c12eaaefefb56dec8aabef9b9ddcc 100644 (file)
 
 #include "subd_split.h"
 #include "subd_patch.h"
+#include "subd_patch_table.h"
 
 #include "util_foreach.h"
 
 CCL_NAMESPACE_BEGIN
 
+#ifdef WITH_OPENSUBDIV
+
+CCL_NAMESPACE_END
+
+#include <opensubdiv/far/topologyRefinerFactory.h>
+#include <opensubdiv/far/primvarRefiner.h>
+#include <opensubdiv/far/patchTableFactory.h>
+#include <opensubdiv/far/patchMap.h>
+
+/* specializations of TopologyRefinerFactory for ccl::Mesh */
+
+namespace OpenSubdiv {
+namespace OPENSUBDIV_VERSION {
+namespace Far {
+       template<>
+       bool TopologyRefinerFactory<ccl::Mesh>::resizeComponentTopology(TopologyRefiner& refiner, ccl::Mesh const& mesh)
+       {
+               setNumBaseVertices(refiner, mesh.verts.size());
+               setNumBaseFaces(refiner, mesh.subd_faces.size());
+
+               ccl::Mesh::SubdFace* face = &mesh.subd_faces[0];
+
+               for(int i = 0; i < mesh.subd_faces.size(); i++, face++) {
+                       setNumBaseFaceVertices(refiner, i, face->num_corners);
+               }
+
+               return true;
+       }
+
+       template<>
+       bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTopology(TopologyRefiner& refiner, ccl::Mesh const& mesh)
+       {
+               ccl::Mesh::SubdFace* face = &mesh.subd_faces[0];
+
+               for(int i = 0; i < mesh.subd_faces.size(); i++, face++) {
+                       IndexArray face_verts = getBaseFaceVertices(refiner, i);
+
+                       int* corner = &mesh.subd_face_corners[face->start_corner];
+
+                       for(int j = 0; j < face->num_corners; j++, corner++) {
+                               face_verts[j] = *corner;
+                       }
+               }
+
+               return true;
+       }
+
+       template<>
+       bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTags(TopologyRefiner& refiner, ccl::Mesh const& mesh)
+       {
+               const ccl::Mesh::SubdEdgeCrease* crease = mesh.subd_creases.data();
+
+               for(int i = 0; i < mesh.subd_creases.size(); i++, crease++) {
+                       Index edge = findBaseEdge(refiner, crease->v[0], crease->v[1]);
+
+                       if(edge != INDEX_INVALID) {
+                               setBaseEdgeSharpness(refiner, edge, crease->crease * 10.0f);
+                       }
+               }
+
+               for(int i = 0; i < mesh.verts.size(); i++) {
+                       ConstIndexArray vert_edges = getBaseVertexEdges(refiner, i);
+
+                       if(vert_edges.size() == 2) {
+                               float sharpness = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]);
+                               sharpness = std::min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1]));
+
+                               setBaseVertexSharpness(refiner, i, sharpness);
+                       }
+               }
+
+               return true;
+       }
+
+       template<>
+       bool TopologyRefinerFactory<ccl::Mesh>::assignFaceVaryingTopology(TopologyRefiner& /*refiner*/, ccl::Mesh const& /*mesh*/)
+       {
+               return true;
+       }
+
+       template<>
+       void TopologyRefinerFactory<ccl::Mesh>::reportInvalidTopology(TopologyError /*err_code*/,
+               char const */*msg*/, ccl::Mesh const& /*mesh*/)
+       {
+       }
+} /* namespace Far */
+} /* namespace OPENSUBDIV_VERSION */
+} /* namespace OpenSubdiv */
+
+CCL_NAMESPACE_BEGIN
+
+using namespace OpenSubdiv;
+
+/* struct that implements OpenSubdiv's vertex interface */
+
+template<typename T>
+struct OsdValue {
+       T value;
+
+       OsdValue() {}
+
+       void Clear(void* = 0) {
+               memset(&value, 0, sizeof(T));
+       }
+
+       void AddWithWeight(OsdValue<T> const& src, float weight) {
+               value += src.value * weight;
+       }
+};
+
+template<>
+void OsdValue<uchar4>::AddWithWeight(OsdValue<uchar4> const& src, float weight)
+{
+       for(int i = 0; i < 4; i++) {
+               value[i] += (uchar)(src.value[i] * weight);
+       }
+}
+
+/* class for holding OpenSubdiv data used during tessellation */
+
+class OsdData {
+       Mesh* mesh;
+       vector<OsdValue<float3> > verts;
+       Far::TopologyRefiner* refiner;
+       Far::PatchTable* patch_table;
+       Far::PatchMap* patch_map;
+
+public:
+       OsdData() : mesh(NULL), refiner(NULL), patch_table(NULL), patch_map(NULL) {}
+
+       ~OsdData()
+       {
+               delete refiner;
+               delete patch_table;
+               delete patch_map;
+       }
+
+       void build_from_mesh(Mesh* mesh_)
+       {
+               mesh = mesh_;
+
+               /* type and options */
+               Sdc::SchemeType type = Sdc::SCHEME_CATMARK;
+
+               Sdc::Options options;
+               options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
+
+               /* create refiner */
+               refiner = Far::TopologyRefinerFactory<Mesh>::Create(*mesh,
+                               Far::TopologyRefinerFactory<Mesh>::Options(type, options));
+
+               /* adaptive refinement */
+               int max_isolation = 10;
+               refiner->RefineAdaptive(Far::TopologyRefiner::AdaptiveOptions(max_isolation));
+
+               /* create patch table */
+               Far::PatchTableFactory::Options patch_options;
+               patch_options.endCapType = Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
+
+               patch_table = Far::PatchTableFactory::Create(*refiner, patch_options);
+
+               /* interpolate verts */
+               int num_refiner_verts = refiner->GetNumVerticesTotal();
+               int num_local_points = patch_table->GetNumLocalPoints();
+
+               verts.resize(num_refiner_verts + num_local_points);
+               for(int i = 0; i < mesh->verts.size(); i++) {
+                       verts[i].value = mesh->verts[i];
+               }
+
+               OsdValue<float3>* src = &verts[0];
+               for(int i = 0; i < refiner->GetMaxLevel(); i++) {
+                       OsdValue<float3>* dest = src + refiner->GetLevel(i).GetNumVertices();
+                       Far::PrimvarRefiner(*refiner).Interpolate(i+1, src, dest);
+                       src = dest;
+               }
+
+               patch_table->ComputeLocalPointValues(&verts[0], &verts[num_refiner_verts]);
+
+               /* create patch map */
+               patch_map = new Far::PatchMap(*patch_table);
+       }
+
+       void subdivide_attribute(Attribute& attr)
+       {
+               Far::PrimvarRefiner primvar_refiner(*refiner);
+
+               if(attr.element == ATTR_ELEMENT_VERTEX) {
+                       int num_refiner_verts = refiner->GetNumVerticesTotal();
+                       int num_local_points = patch_table->GetNumLocalPoints();
+
+                       attr.resize(num_refiner_verts + num_local_points);
+                       attr.flags |= ATTR_FINAL_SIZE;
+
+                       char* src = &attr.buffer[0];
+
+                       for(int i = 0; i < refiner->GetMaxLevel(); i++) {
+                               char* dest = src + refiner->GetLevel(i).GetNumVertices() * attr.data_sizeof();
+
+                               if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) {
+                                       primvar_refiner.Interpolate(i+1, (OsdValue<float>*)src, (OsdValue<float>*&)dest);
+                               }
+                               else {
+                                       primvar_refiner.Interpolate(i+1, (OsdValue<float4>*)src, (OsdValue<float4>*&)dest);
+                               }
+
+                               src = dest;
+                       }
+
+                       if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) {
+                               patch_table->ComputeLocalPointValues((OsdValue<float>*)&attr.buffer[0],
+                                                                        (OsdValue<float>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
+                       }
+                       else {
+                               patch_table->ComputeLocalPointValues((OsdValue<float4>*)&attr.buffer[0],
+                                                                        (OsdValue<float4>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
+                       }
+               }
+               else if(attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) {
+                       // TODO(mai): fvar interpolation
+               }
+       }
+
+       friend struct OsdPatch;
+       friend class Mesh;
+};
+
+/* ccl::Patch implementation that uses OpenSubdiv for eval */
+
+struct OsdPatch : Patch {
+       OsdData* osd_data;
+
+       OsdPatch(OsdData* data) : osd_data(data) {}
+
+       void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)
+       {
+               const Far::PatchTable::PatchHandle* handle = osd_data->patch_map->FindPatch(patch_index, u, v);
+               assert(handle);
+
+               float p_weights[20], du_weights[20], dv_weights[20];
+               osd_data->patch_table->EvaluateBasis(*handle, u, v, p_weights, du_weights, dv_weights);
+
+               Far::ConstIndexArray cv = osd_data->patch_table->GetPatchVertices(*handle);
+
+               float3 du, dv;
+               if(P) *P = make_float3(0.0f, 0.0f, 0.0f);
+               du = make_float3(0.0f, 0.0f, 0.0f);
+               dv = make_float3(0.0f, 0.0f, 0.0f);
+
+               for(int i = 0; i < cv.size(); i++) {
+                       float3 p = osd_data->verts[cv[i]].value;
+
+                       if(P) *P += p * p_weights[i];
+                       du += p * du_weights[i];
+                       dv += p * dv_weights[i];
+               }
+
+               if(dPdu) *dPdu = du;
+               if(dPdv) *dPdv = dv;
+               if(N) *N = normalize(cross(du, dv));
+       }
+
+       BoundBox bound() { return BoundBox::empty; }
+};
+
+#endif
+
 void Mesh::tessellate(DiagSplit *split)
 {
+#ifdef WITH_OPENSUBDIV
+       OsdData osd_data;
+       bool need_packed_patch_table = false;
+
+       if(subdivision_type == SUBDIVISION_CATMULL_CLARK) {
+               osd_data.build_from_mesh(this);
+       }
+       else
+#endif
+       {
+               /* force linear subdivision if OpenSubdiv is unavailable to avoid
+                * falling into catmull-clark code paths by accident
+                */
+               subdivision_type = SUBDIVISION_LINEAR;
+
+               /* force disable attribute subdivision for same reason as above */
+               foreach(Attribute& attr, subd_attributes.attributes) {
+                       attr.flags &= ~ATTR_SUBDIVIDED;
+               }
+       }
+
        int num_faces = subd_faces.size();
 
        Attribute *attr_vN = subd_attributes.find(ATTR_STD_VERTEX_NORMAL);
@@ -36,113 +325,158 @@ void Mesh::tessellate(DiagSplit *split)
 
                if(face.is_quad()) {
                        /* quad */
-                       LinearQuadPatch patch;
-                       float3 *hull = patch.hull;
-                       float3 *normals = patch.normals;
+                       QuadDice::SubPatch subpatch;
 
-                       patch.patch_index = face.ptex_offset;
-                       patch.shader = face.shader;
+                       LinearQuadPatch quad_patch;
+#ifdef WITH_OPENSUBDIV
+                       OsdPatch osd_patch(&osd_data);
 
-                       for(int i = 0; i < 4; i++) {
-                               hull[i] = verts[subd_face_corners[face.start_corner+i]];
+                       if(subdivision_type == SUBDIVISION_CATMULL_CLARK) {
+                               osd_patch.patch_index = face.ptex_offset;
+
+                               subpatch.patch = &osd_patch;
                        }
+                       else
+#endif
+                       {
+                               float3 *hull = quad_patch.hull;
+                               float3 *normals = quad_patch.normals;
+
+                               quad_patch.patch_index = face.ptex_offset;
 
-                       if(face.smooth) {
                                for(int i = 0; i < 4; i++) {
-                                       normals[i] = vN[subd_face_corners[face.start_corner+i]];
+                                       hull[i] = verts[subd_face_corners[face.start_corner+i]];
                                }
-                       }
-                       else {
-                               float3 N = face.normal(this);
-                               for(int i = 0; i < 4; i++) {
-                                       normals[i] = N;
+
+                               if(face.smooth) {
+                                       for(int i = 0; i < 4; i++) {
+                                               normals[i] = vN[subd_face_corners[face.start_corner+i]];
+                                       }
+                               }
+                               else {
+                                       float3 N = face.normal(this);
+                                       for(int i = 0; i < 4; i++) {
+                                               normals[i] = N;
+                                       }
                                }
+
+                               swap(hull[2], hull[3]);
+                               swap(normals[2], normals[3]);
+
+                               subpatch.patch = &quad_patch;
                        }
 
-                       swap(hull[2], hull[3]);
-                       swap(normals[2], normals[3]);
+                       subpatch.patch->shader = face.shader;
 
                        /* Quad faces need to be split at least once to line up with split ngons, we do this
                         * here in this manner because if we do it later edge factors may end up slightly off.
                         */
-                       QuadDice::SubPatch subpatch;
-                       subpatch.patch = &patch;
-
                        subpatch.P00 = make_float2(0.0f, 0.0f);
                        subpatch.P10 = make_float2(0.5f, 0.0f);
                        subpatch.P01 = make_float2(0.0f, 0.5f);
                        subpatch.P11 = make_float2(0.5f, 0.5f);
-                       split->split_quad(&patch, &subpatch);
+                       split->split_quad(subpatch.patch, &subpatch);
 
                        subpatch.P00 = make_float2(0.5f, 0.0f);
                        subpatch.P10 = make_float2(1.0f, 0.0f);
                        subpatch.P01 = make_float2(0.5f, 0.5f);
                        subpatch.P11 = make_float2(1.0f, 0.5f);
-                       split->split_quad(&patch, &subpatch);
+                       split->split_quad(subpatch.patch, &subpatch);
 
                        subpatch.P00 = make_float2(0.0f, 0.5f);
                        subpatch.P10 = make_float2(0.5f, 0.5f);
                        subpatch.P01 = make_float2(0.0f, 1.0f);
                        subpatch.P11 = make_float2(0.5f, 1.0f);
-                       split->split_quad(&patch, &subpatch);
+                       split->split_quad(subpatch.patch, &subpatch);
 
                        subpatch.P00 = make_float2(0.5f, 0.5f);
                        subpatch.P10 = make_float2(1.0f, 0.5f);
                        subpatch.P01 = make_float2(0.5f, 1.0f);
                        subpatch.P11 = make_float2(1.0f, 1.0f);
-                       split->split_quad(&patch, &subpatch);
+                       split->split_quad(subpatch.patch, &subpatch);
                }
                else {
                        /* ngon */
-                       float3 center_vert = make_float3(0.0f, 0.0f, 0.0f);
-                       float3 center_normal = make_float3(0.0f, 0.0f, 0.0f);
+#ifdef WITH_OPENSUBDIV
+                       if(subdivision_type == SUBDIVISION_CATMULL_CLARK) {
+                               OsdPatch patch(&osd_data);
+
+                               patch.shader = face.shader;
 
-                       float inv_num_corners = 1.0f/float(face.num_corners);
-                       for(int corner = 0; corner < face.num_corners; corner++) {
-                               center_vert += verts[subd_face_corners[face.start_corner + corner]] * inv_num_corners;
-                               center_normal += vN[subd_face_corners[face.start_corner + corner]] * inv_num_corners;
+                               for(int corner = 0; corner < face.num_corners; corner++) {
+                                       patch.patch_index = face.ptex_offset + corner;
+
+                                       split->split_quad(&patch);
+                               }
                        }
+                       else
+#endif
+                       {
+                               float3 center_vert = make_float3(0.0f, 0.0f, 0.0f);
+                               float3 center_normal = make_float3(0.0f, 0.0f, 0.0f);
+
+                               float inv_num_corners = 1.0f/float(face.num_corners);
+                               for(int corner = 0; corner < face.num_corners; corner++) {
+                                       center_vert += verts[subd_face_corners[face.start_corner + corner]] * inv_num_corners;
+                                       center_normal += vN[subd_face_corners[face.start_corner + corner]] * inv_num_corners;
+                               }
 
-                       for(int corner = 0; corner < face.num_corners; corner++) {
-                               LinearQuadPatch patch;
-                               float3 *hull = patch.hull;
-                               float3 *normals = patch.normals;
+                               for(int corner = 0; corner < face.num_corners; corner++) {
+                                       LinearQuadPatch patch;
+                                       float3 *hull = patch.hull;
+                                       float3 *normals = patch.normals;
 
-                               patch.patch_index = face.ptex_offset + corner;
+                                       patch.patch_index = face.ptex_offset + corner;
 
-                               patch.shader = face.shader;
+                                       patch.shader = face.shader;
 
-                               hull[0] = verts[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]];
-                               hull[1] = verts[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]];
-                               hull[2] = verts[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]];
-                               hull[3] = center_vert;
+                                       hull[0] = verts[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]];
+                                       hull[1] = verts[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]];
+                                       hull[2] = verts[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]];
+                                       hull[3] = center_vert;
 
-                               hull[1] = (hull[1] + hull[0]) * 0.5;
-                               hull[2] = (hull[2] + hull[0]) * 0.5;
+                                       hull[1] = (hull[1] + hull[0]) * 0.5;
+                                       hull[2] = (hull[2] + hull[0]) * 0.5;
 
-                               if(face.smooth) {
-                                       normals[0] = vN[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]];
-                                       normals[1] = vN[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]];
-                                       normals[2] = vN[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]];
-                                       normals[3] = center_normal;
+                                       if(face.smooth) {
+                                               normals[0] = vN[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]];
+                                               normals[1] = vN[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]];
+                                               normals[2] = vN[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]];
+                                               normals[3] = center_normal;
 
-                                       normals[1] = (normals[1] + normals[0]) * 0.5;
-                                       normals[2] = (normals[2] + normals[0]) * 0.5;
-                               }
-                               else {
-                                       float3 N = face.normal(this);
-                                       for(int i = 0; i < 4; i++) {
-                                               normals[i] = N;
+                                               normals[1] = (normals[1] + normals[0]) * 0.5;
+                                               normals[2] = (normals[2] + normals[0]) * 0.5;
+                                       }
+                                       else {
+                                               float3 N = face.normal(this);
+                                               for(int i = 0; i < 4; i++) {
+                                                       normals[i] = N;
+                                               }
                                        }
-                               }
 
-                               split->split_quad(&patch);
+                                       split->split_quad(&patch);
+                               }
                        }
                }
        }
 
        /* interpolate center points for attributes */
        foreach(Attribute& attr, subd_attributes.attributes) {
+#ifdef WITH_OPENSUBDIV
+               if(subdivision_type == SUBDIVISION_CATMULL_CLARK && attr.flags & ATTR_SUBDIVIDED) {
+                       if(attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) {
+                               /* keep subdivision for corner attributes disabled for now */
+                               attr.flags &= ~ATTR_SUBDIVIDED;
+                       }
+                       else {
+                               osd_data.subdivide_attribute(attr);
+
+                               need_packed_patch_table = true;
+                               continue;
+                       }
+               }
+#endif
+
                char* data = attr.data();
                size_t stride = attr.data_sizeof();
                int ngons = 0;
@@ -218,6 +552,15 @@ void Mesh::tessellate(DiagSplit *split)
                        default: break;
                }
        }
+
+#ifdef WITH_OPENSUBDIV
+       /* pack patch tables */
+       if(need_packed_patch_table) {
+               delete patch_table;
+               patch_table = new PackedPatchTable;
+               patch_table->pack(osd_data.patch_table);
+       }
+#endif
 }
 
 CCL_NAMESPACE_END
index 5dbe2e4d6a12824f012a3631d7f2d1bf29abc633..eeb44f4668571e5e3f4d8e2a8641ea3ab5a3ff40 100644 (file)
@@ -29,6 +29,8 @@
 #include "util_progress.h"
 #include "util_vector.h"
 
+#include "subd_patch_table.h"
+
 CCL_NAMESPACE_BEGIN
 
 /* Object */
@@ -607,6 +609,37 @@ void ObjectManager::device_update_flags(Device *device,
        device->tex_alloc("__object_flag", dscene->object_flag);
 }
 
+void ObjectManager::device_update_patch_map_offsets(Device *device, DeviceScene *dscene, Scene *scene)
+{
+       uint4* objects = (uint4*)dscene->objects.get_data();
+
+       bool update = false;
+
+       int object_index = 0;
+       foreach(Object *object, scene->objects) {
+               int offset = object_index*OBJECT_SIZE + 11;
+
+               Mesh* mesh = object->mesh;
+
+               if(mesh->patch_table) {
+                       uint patch_map_offset = 2*(mesh->patch_table_offset + mesh->patch_table->total_size() -
+                                                  mesh->patch_table->num_nodes * PATCH_NODE_SIZE) - mesh->patch_offset;
+
+                       if(objects[offset].x != patch_map_offset) {
+                               objects[offset].x = patch_map_offset;
+                               update = true;
+                       }
+               }
+
+               object_index++;
+       }
+
+       if(update) {
+               device->tex_free(dscene->objects);
+               device->tex_alloc("__objects", dscene->objects);
+       }
+}
+
 void ObjectManager::device_free(Device *device, DeviceScene *dscene)
 {
        device->tex_free(dscene->objects);
index 7ab73f3c91a4b8bc9d915ec226634c306e9705a1..2e5837f672f88d58e582900cbec720cde085f93d 100644 (file)
@@ -97,6 +97,8 @@ public:
                                 Scene *scene,
                                 Progress& progress,
                                 bool bounds_valid = true);
+       void device_update_patch_map_offsets(Device *device, DeviceScene *dscene, Scene *scene);
+
        void device_free(Device *device, DeviceScene *dscene);
 
        void tag_update(Scene *scene);
index db4970136931e926d9da5088cbf9c29cb18ab572..9265299e82b852b82eeff497814de15f76fcaa31 100644 (file)
@@ -16,6 +16,7 @@ set(SRC
        subd_dice.cpp
        subd_patch.cpp
        subd_split.cpp
+       subd_patch_table.cpp
 )
 
 set(SRC_HEADERS
@@ -24,10 +25,6 @@ set(SRC_HEADERS
        subd_split.h
 )
 
-if(WITH_CYCLES_OPENSUBDIV)
-       add_definitions(-DWITH_OPENSUBDIV)
-endif()
-
 include_directories(${INC})
 include_directories(SYSTEM ${INC_SYS})
 
diff --git a/intern/cycles/subd/subd_patch_table.cpp b/intern/cycles/subd/subd_patch_table.cpp
new file mode 100644 (file)
index 0000000..a32533b
--- /dev/null
@@ -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
+
diff --git a/intern/cycles/subd/subd_patch_table.h b/intern/cycles/subd/subd_patch_table.h
new file mode 100644 (file)
index 0000000..c8c7ecf
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011-2016 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SUBD_PATCH_TABLE_H__
+#define __SUBD_PATCH_TABLE_H__
+
+#include "util_types.h"
+#include "util_vector.h"
+
+#ifdef WITH_OPENSUBDIV
+#ifdef _MSC_VER
+#  include "iso646.h"
+#endif
+
+#include <opensubdiv/far/patchTable.h>
+#endif
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef WITH_OPENSUBDIV
+using namespace OpenSubdiv;
+#else
+/* forward declare for when OpenSubdiv is unavailable */
+namespace Far { struct PatchTable; }
+#endif
+
+#define PATCH_ARRAY_SIZE 4
+#define PATCH_PARAM_SIZE 2
+#define PATCH_HANDLE_SIZE 3
+#define PATCH_NODE_SIZE 1
+
+struct PackedPatchTable {
+       vector<uint> table;
+
+       size_t num_arrays;
+       size_t num_indices;
+       size_t num_patches;
+       size_t num_nodes;
+
+       /* calculated size from num_* members */
+       size_t total_size();
+
+       void pack(Far::PatchTable* patch_table, int offset = 0);
+       void copy_adjusting_offsets(uint* dest, int doffset);
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __SUBD_PATCH_TABLE_H__ */
+
index 6f8c3f6f3de3bfaf9c6bfabf4a8499bc33114839..546b17570bbf36eae90a1133af9e9be14c5e3d9c 100644 (file)
@@ -222,6 +222,11 @@ public:
                return datasize_;
        }
 
+       T* data()
+       {
+               return data_;
+       }
+
        const T* data() const
        {
                return data_;