/* * 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. */ #include "mesh.h" #include "attribute.h" #include "camera.h" #include "subd_split.h" #include "subd_patch.h" #include "subd_patch_table.h" #include "util_foreach.h" #include "util_algorithm.h" CCL_NAMESPACE_BEGIN #ifdef WITH_OPENSUBDIV CCL_NAMESPACE_END #include #include #include #include /* specializations of TopologyRefinerFactory for ccl::Mesh */ namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { namespace Far { template<> bool TopologyRefinerFactory::resizeComponentTopology(TopologyRefiner& refiner, ccl::Mesh const& mesh) { setNumBaseVertices(refiner, mesh.verts.size()); setNumBaseFaces(refiner, mesh.subd_faces.size()); const ccl::Mesh::SubdFace* face = mesh.subd_faces.data(); for(int i = 0; i < mesh.subd_faces.size(); i++, face++) { setNumBaseFaceVertices(refiner, i, face->num_corners); } return true; } template<> bool TopologyRefinerFactory::assignComponentTopology(TopologyRefiner& refiner, ccl::Mesh const& mesh) { const ccl::Mesh::SubdFace* face = mesh.subd_faces.data(); 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::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 = ccl::min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1])); setBaseVertexSharpness(refiner, i, sharpness); } } return true; } template<> bool TopologyRefinerFactory::assignFaceVaryingTopology(TopologyRefiner& /*refiner*/, ccl::Mesh const& /*mesh*/) { return true; } template<> void TopologyRefinerFactory::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 struct OsdValue { T value; OsdValue() {} void Clear(void* = 0) { memset(&value, 0, sizeof(T)); } void AddWithWeight(OsdValue const& src, float weight) { value += src.value * weight; } }; template<> void OsdValue::AddWithWeight(OsdValue 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 > 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::Create(*mesh, Far::TopologyRefinerFactory::Options(type, options)); /* adaptive refinement */ int max_isolation = calculate_max_isolation(); 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* src = verts.data(); for(int i = 0; i < refiner->GetMaxLevel(); i++) { OsdValue* 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.data(); 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*)src, (OsdValue*&)dest); } else { primvar_refiner.Interpolate(i+1, (OsdValue*)src, (OsdValue*&)dest); } src = dest; } if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) { patch_table->ComputeLocalPointValues((OsdValue*)&attr.buffer[0], (OsdValue*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]); } else { patch_table->ComputeLocalPointValues((OsdValue*)&attr.buffer[0], (OsdValue*)&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 } } int calculate_max_isolation() { /* loop over all edges to find longest in screen space */ const Far::TopologyLevel& level = refiner->GetLevel(0); Transform objecttoworld = mesh->subd_params->objecttoworld; Camera* cam = mesh->subd_params->camera; float longest_edge = 0.0f; for(size_t i = 0; i < level.GetNumEdges(); i++) { Far::ConstIndexArray verts = level.GetEdgeVertices(i); float3 a = mesh->verts[verts[0]]; float3 b = mesh->verts[verts[1]]; float edge_len; if(cam) { a = transform_point(&objecttoworld, a); b = transform_point(&objecttoworld, b); edge_len = len(a - b) / cam->world_to_raster_size((a + b) * 0.5f); } else { edge_len = len(a - b); } longest_edge = max(longest_edge, edge_len); } /* calculate isolation level */ int isolation = (int)(log2f(max(longest_edge / mesh->subd_params->dicing_rate, 1.0f)) + 1.0f); return min(isolation, 10); } 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 = cross(du, dv); float t = len(*N); *N = (t != 0.0f) ? *N/t : make_float3(0.0f, 0.0f, 1.0f); } } 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) { if(subd_faces.size()) { 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); float3* vN = attr_vN->data_float3(); for(int f = 0; f < num_faces; f++) { SubdFace& face = subd_faces[f]; if(face.is_quad()) { /* quad */ QuadDice::SubPatch subpatch; LinearQuadPatch quad_patch; #ifdef WITH_OPENSUBDIV OsdPatch osd_patch(&osd_data); 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; for(int i = 0; i < 4; i++) { hull[i] = verts[subd_face_corners[face.start_corner+i]]; } 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; } 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. */ 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(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(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(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(subpatch.patch, &subpatch); } else { /* ngon */ #ifdef WITH_OPENSUBDIV if(subdivision_type == SUBDIVISION_CATMULL_CLARK) { OsdPatch patch(&osd_data); patch.shader = face.shader; 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; patch.patch_index = face.ptex_offset + corner; 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[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; 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); } } } } /* 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 if(subd_faces.size()) { 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; switch(attr.element) { case ATTR_ELEMENT_VERTEX: { for(int f = 0; f < num_faces; f++) { SubdFace& face = subd_faces[f]; if(!face.is_quad()) { char* center = data + (verts.size() - num_subd_verts + ngons) * stride; attr.zero_data(center); float inv_num_corners = 1.0f / float(face.num_corners); for(int corner = 0; corner < face.num_corners; corner++) { attr.add_with_weight(center, data + subd_face_corners[face.start_corner + corner] * stride, inv_num_corners); } ngons++; } } } break; case ATTR_ELEMENT_VERTEX_MOTION: { // TODO(mai): implement } break; case ATTR_ELEMENT_CORNER: { for(int f = 0; f < num_faces; f++) { SubdFace& face = subd_faces[f]; if(!face.is_quad()) { char* center = data + (subd_face_corners.size() + ngons) * stride; attr.zero_data(center); float inv_num_corners = 1.0f / float(face.num_corners); for(int corner = 0; corner < face.num_corners; corner++) { attr.add_with_weight(center, data + (face.start_corner + corner) * stride, inv_num_corners); } ngons++; } } } break; case ATTR_ELEMENT_CORNER_BYTE: { for(int f = 0; f < num_faces; f++) { SubdFace& face = subd_faces[f]; if(!face.is_quad()) { uchar* center = (uchar*)data + (subd_face_corners.size() + ngons) * stride; float inv_num_corners = 1.0f / float(face.num_corners); float4 val = make_float4(0.0f, 0.0f, 0.0f, 0.0f); for(int corner = 0; corner < face.num_corners; corner++) { for(int i = 0; i < 4; i++) { val[i] += float(*(data + (face.start_corner + corner) * stride + i)) * inv_num_corners; } } for(int i = 0; i < 4; i++) { center[i] = uchar(min(max(val[i], 0.0f), 255.0f)); } ngons++; } } } break; 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