/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation * * SPDX-License-Identifier: Apache-2.0 */ #include "scene/attribute.h" #include "scene/mesh.h" #include "subd/interpolation.h" #include "subd/osd.h" #include "subd/patch.h" #include "subd/split.h" #include "util/algorithm.h" #include "util/vector.h" CCL_NAMESPACE_BEGIN void Mesh::tessellate(SubdParams ¶ms) { /* reset the number of subdivision vertices, in case the Mesh was not cleared * between calls or data updates */ num_subd_added_verts = 0; const int num_faces = get_num_subd_faces(); /* If there are no faces there is nothing to subdivide, and the required * subd position attribute may not exist. */ if (num_faces == 0) { return; } #ifdef WITH_OPENSUBDIV OsdMesh osd_mesh(*this); OsdData osd_data; if (subdivision_type == SUBDIVISION_CATMULL_CLARK) { if (get_num_subd_faces()) { osd_data.build(osd_mesh); } } else #endif { /* force linear subdivision if OpenSubdiv is unavailable to avoid * falling into catmull-clark code paths by accident */ subdivision_type = SUBDIVISION_LINEAR; } /* count patches */ int num_patches = 0; for (int f = 0; f < num_faces; f++) { SubdFace face = get_subd_face(f); if (face.is_quad()) { num_patches++; } else { num_patches += face.num_corners; } } /* build patches from faces */ #ifdef WITH_OPENSUBDIV if (subdivision_type == SUBDIVISION_CATMULL_CLARK) { vector osd_patches(num_patches, OsdPatch(osd_data)); OsdPatch *patch = osd_patches.data(); for (int f = 0; f < num_faces; f++) { SubdFace face = get_subd_face(f); if (face.is_quad()) { patch->patch_index = face.ptex_offset; patch->from_ngon = false; patch->shader = face.shader; patch->smooth = face.smooth; patch++; } else { for (int corner = 0; corner < face.num_corners; corner++) { patch->patch_index = face.ptex_offset + corner; patch->from_ngon = true; patch->shader = face.shader; patch->smooth = face.smooth; patch++; } } } /* Split patches. */ DiagSplit split(params); split.split_patches(osd_patches.data(), sizeof(OsdPatch)); /* Setup interpolation. */ SubdAttributeInterpolation interpolation(*this, osd_mesh, osd_data); /* Dice patches. */ EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles(), interpolation); dice.dice(split); } else #endif { vector linear_patches(num_patches); LinearQuadPatch *patch = linear_patches.data(); const Attribute *subd_attr_P = subd_attributes.find(ATTR_STD_POSITION); const packed_float3 *verts = subd_attr_P->data(); for (int f = 0; f < num_faces; f++) { SubdFace face = get_subd_face(f); if (face.is_quad()) { /* Simple quad case. */ float3 *hull = patch->hull; patch->patch_index = face.ptex_offset; patch->from_ngon = false; hull[0] = verts[subd_face_corners[face.start_corner + 0]]; hull[1] = verts[subd_face_corners[face.start_corner + 1]]; hull[2] = verts[subd_face_corners[face.start_corner + 3]]; hull[3] = verts[subd_face_corners[face.start_corner + 2]]; patch->shader = face.shader; patch->smooth = face.smooth; patch++; } else { /* N-gon split into N quads. */ float3 center_vert = zero_float3(); const 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; } for (int corner = 0; corner < face.num_corners; corner++) { float3 *hull = patch->hull; patch->patch_index = face.ptex_offset + corner; patch->from_ngon = true; patch->shader = face.shader; patch->smooth = face.smooth; const int v0 = subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]; const int v1 = subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]; const int v3 = subd_face_corners[face.start_corner + mod(corner + face.num_corners - 1, face.num_corners)]; hull[0] = verts[v0]; hull[1] = 0.5f * (verts[v0] + verts[v1]); hull[2] = 0.5f * (verts[v3] + verts[v0]); hull[3] = center_vert; patch++; } } } /* Split patches. */ DiagSplit split(params); split.split_patches(linear_patches.data(), sizeof(LinearQuadPatch)); /* Setup interpolation. */ #ifdef WITH_OPENSUBDIV SubdAttributeInterpolation interpolation(*this, osd_mesh, osd_data); #else SubdAttributeInterpolation interpolation(*this); #endif /* Dice patches. */ EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles(), interpolation); dice.dice(split); } // TODO: Free subd base data? Or will this break interactive updates? } CCL_NAMESPACE_END