1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "bvh/bvh.h"
18 #include "bvh/bvh_build.h"
19 
20 #include "device/device.h"
21 
22 #include "render/graph.h"
23 #include "render/hair.h"
24 #include "render/mesh.h"
25 #include "render/object.h"
26 #include "render/scene.h"
27 
28 #include "subd/subd_patch_table.h"
29 #include "subd/subd_split.h"
30 
31 #include "util/util_foreach.h"
32 #include "util/util_logging.h"
33 #include "util/util_progress.h"
34 #include "util/util_set.h"
35 
36 CCL_NAMESPACE_BEGIN
37 
38 /* Triangle */
39 
bounds_grow(const float3 * verts,BoundBox & bounds) const40 void Mesh::Triangle::bounds_grow(const float3 *verts, BoundBox &bounds) const
41 {
42   bounds.grow(verts[v[0]]);
43   bounds.grow(verts[v[1]]);
44   bounds.grow(verts[v[2]]);
45 }
46 
motion_verts(const float3 * verts,const float3 * vert_steps,size_t num_verts,size_t num_steps,float time,float3 r_verts[3]) const47 void Mesh::Triangle::motion_verts(const float3 *verts,
48                                   const float3 *vert_steps,
49                                   size_t num_verts,
50                                   size_t num_steps,
51                                   float time,
52                                   float3 r_verts[3]) const
53 {
54   /* Figure out which steps we need to fetch and their interpolation factor. */
55   const size_t max_step = num_steps - 1;
56   const size_t step = min((int)(time * max_step), max_step - 1);
57   const float t = time * max_step - step;
58   /* Fetch vertex coordinates. */
59   float3 curr_verts[3];
60   float3 next_verts[3];
61   verts_for_step(verts, vert_steps, num_verts, num_steps, step, curr_verts);
62   verts_for_step(verts, vert_steps, num_verts, num_steps, step + 1, next_verts);
63   /* Interpolate between steps. */
64   r_verts[0] = (1.0f - t) * curr_verts[0] + t * next_verts[0];
65   r_verts[1] = (1.0f - t) * curr_verts[1] + t * next_verts[1];
66   r_verts[2] = (1.0f - t) * curr_verts[2] + t * next_verts[2];
67 }
68 
verts_for_step(const float3 * verts,const float3 * vert_steps,size_t num_verts,size_t num_steps,size_t step,float3 r_verts[3]) const69 void Mesh::Triangle::verts_for_step(const float3 *verts,
70                                     const float3 *vert_steps,
71                                     size_t num_verts,
72                                     size_t num_steps,
73                                     size_t step,
74                                     float3 r_verts[3]) const
75 {
76   const size_t center_step = ((num_steps - 1) / 2);
77   if (step == center_step) {
78     /* Center step: regular vertex location. */
79     r_verts[0] = verts[v[0]];
80     r_verts[1] = verts[v[1]];
81     r_verts[2] = verts[v[2]];
82   }
83   else {
84     /* Center step not stored in the attribute array array. */
85     if (step > center_step) {
86       step--;
87     }
88     size_t offset = step * num_verts;
89     r_verts[0] = vert_steps[offset + v[0]];
90     r_verts[1] = vert_steps[offset + v[1]];
91     r_verts[2] = vert_steps[offset + v[2]];
92   }
93 }
94 
compute_normal(const float3 * verts) const95 float3 Mesh::Triangle::compute_normal(const float3 *verts) const
96 {
97   const float3 &v0 = verts[v[0]];
98   const float3 &v1 = verts[v[1]];
99   const float3 &v2 = verts[v[2]];
100   const float3 norm = cross(v1 - v0, v2 - v0);
101   const float normlen = len(norm);
102   if (normlen == 0.0f) {
103     return make_float3(1.0f, 0.0f, 0.0f);
104   }
105   return norm / normlen;
106 }
107 
valid(const float3 * verts) const108 bool Mesh::Triangle::valid(const float3 *verts) const
109 {
110   return isfinite3_safe(verts[v[0]]) && isfinite3_safe(verts[v[1]]) && isfinite3_safe(verts[v[2]]);
111 }
112 
113 /* SubdFace */
114 
normal(const Mesh * mesh) const115 float3 Mesh::SubdFace::normal(const Mesh *mesh) const
116 {
117   float3 v0 = mesh->verts[mesh->subd_face_corners[start_corner + 0]];
118   float3 v1 = mesh->verts[mesh->subd_face_corners[start_corner + 1]];
119   float3 v2 = mesh->verts[mesh->subd_face_corners[start_corner + 2]];
120 
121   return safe_normalize(cross(v1 - v0, v2 - v0));
122 }
123 
124 /* Mesh */
125 
NODE_DEFINE(Mesh)126 NODE_DEFINE(Mesh)
127 {
128   NodeType *type = NodeType::add("mesh", create, NodeType::NONE, Geometry::node_base_type);
129 
130   SOCKET_INT_ARRAY(triangles, "Triangles", array<int>());
131   SOCKET_POINT_ARRAY(verts, "Vertices", array<float3>());
132   SOCKET_INT_ARRAY(shader, "Shader", array<int>());
133   SOCKET_BOOLEAN_ARRAY(smooth, "Smooth", array<bool>());
134 
135   return type;
136 }
137 
Mesh(const NodeType * node_type_,Type geom_type_)138 Mesh::Mesh(const NodeType *node_type_, Type geom_type_)
139     : Geometry(node_type_, geom_type_), subd_attributes(this, ATTR_PRIM_SUBD)
140 {
141   vert_offset = 0;
142 
143   patch_offset = 0;
144   face_offset = 0;
145   corner_offset = 0;
146 
147   num_subd_verts = 0;
148 
149   num_ngons = 0;
150 
151   subdivision_type = SUBDIVISION_NONE;
152   subd_params = NULL;
153 
154   patch_table = NULL;
155 }
156 
Mesh()157 Mesh::Mesh() : Mesh(node_type, Geometry::MESH)
158 {
159 }
160 
~Mesh()161 Mesh::~Mesh()
162 {
163   delete patch_table;
164   delete subd_params;
165 }
166 
resize_mesh(int numverts,int numtris)167 void Mesh::resize_mesh(int numverts, int numtris)
168 {
169   verts.resize(numverts);
170   triangles.resize(numtris * 3);
171   shader.resize(numtris);
172   smooth.resize(numtris);
173 
174   if (subd_faces.size()) {
175     triangle_patch.resize(numtris);
176     vert_patch_uv.resize(numverts);
177   }
178 
179   attributes.resize();
180 }
181 
reserve_mesh(int numverts,int numtris)182 void Mesh::reserve_mesh(int numverts, int numtris)
183 {
184   /* reserve space to add verts and triangles later */
185   verts.reserve(numverts);
186   triangles.reserve(numtris * 3);
187   shader.reserve(numtris);
188   smooth.reserve(numtris);
189 
190   if (subd_faces.size()) {
191     triangle_patch.reserve(numtris);
192     vert_patch_uv.reserve(numverts);
193   }
194 
195   attributes.resize(true);
196 }
197 
resize_subd_faces(int numfaces,int num_ngons_,int numcorners)198 void Mesh::resize_subd_faces(int numfaces, int num_ngons_, int numcorners)
199 {
200   subd_faces.resize(numfaces);
201   subd_face_corners.resize(numcorners);
202   num_ngons = num_ngons_;
203 
204   subd_attributes.resize();
205 }
206 
reserve_subd_faces(int numfaces,int num_ngons_,int numcorners)207 void Mesh::reserve_subd_faces(int numfaces, int num_ngons_, int numcorners)
208 {
209   subd_faces.reserve(numfaces);
210   subd_face_corners.reserve(numcorners);
211   num_ngons = num_ngons_;
212 
213   subd_attributes.resize(true);
214 }
215 
clear(bool preserve_voxel_data)216 void Mesh::clear(bool preserve_voxel_data)
217 {
218   Geometry::clear();
219 
220   /* clear all verts and triangles */
221   verts.clear();
222   triangles.clear();
223   shader.clear();
224   smooth.clear();
225 
226   triangle_patch.clear();
227   vert_patch_uv.clear();
228 
229   subd_faces.clear();
230   subd_face_corners.clear();
231 
232   num_subd_verts = 0;
233 
234   subd_creases.clear();
235 
236   subd_attributes.clear();
237   attributes.clear(preserve_voxel_data);
238 
239   vert_to_stitching_key_map.clear();
240   vert_stitching_map.clear();
241 
242   delete patch_table;
243   patch_table = NULL;
244 }
245 
clear()246 void Mesh::clear()
247 {
248   clear(false);
249 }
250 
add_vertex(float3 P)251 void Mesh::add_vertex(float3 P)
252 {
253   verts.push_back_reserved(P);
254 
255   if (subd_faces.size()) {
256     vert_patch_uv.push_back_reserved(make_float2(0.0f, 0.0f));
257   }
258 }
259 
add_vertex_slow(float3 P)260 void Mesh::add_vertex_slow(float3 P)
261 {
262   verts.push_back_slow(P);
263 
264   if (subd_faces.size()) {
265     vert_patch_uv.push_back_slow(make_float2(0.0f, 0.0f));
266   }
267 }
268 
add_triangle(int v0,int v1,int v2,int shader_,bool smooth_)269 void Mesh::add_triangle(int v0, int v1, int v2, int shader_, bool smooth_)
270 {
271   triangles.push_back_reserved(v0);
272   triangles.push_back_reserved(v1);
273   triangles.push_back_reserved(v2);
274   shader.push_back_reserved(shader_);
275   smooth.push_back_reserved(smooth_);
276 
277   if (subd_faces.size()) {
278     triangle_patch.push_back_reserved(-1);
279   }
280 }
281 
add_subd_face(int * corners,int num_corners,int shader_,bool smooth_)282 void Mesh::add_subd_face(int *corners, int num_corners, int shader_, bool smooth_)
283 {
284   int start_corner = subd_face_corners.size();
285 
286   for (int i = 0; i < num_corners; i++) {
287     subd_face_corners.push_back_reserved(corners[i]);
288   }
289 
290   int ptex_offset = 0;
291 
292   if (subd_faces.size()) {
293     SubdFace &s = subd_faces[subd_faces.size() - 1];
294     ptex_offset = s.ptex_offset + s.num_ptex_faces();
295   }
296 
297   SubdFace face = {start_corner, num_corners, shader_, smooth_, ptex_offset};
298   subd_faces.push_back_reserved(face);
299 }
300 
copy_center_to_motion_step(const int motion_step)301 void Mesh::copy_center_to_motion_step(const int motion_step)
302 {
303   Attribute *attr_mP = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
304 
305   if (attr_mP) {
306     Attribute *attr_mN = attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
307     Attribute *attr_N = attributes.find(ATTR_STD_VERTEX_NORMAL);
308     float3 *P = &verts[0];
309     float3 *N = (attr_N) ? attr_N->data_float3() : NULL;
310     size_t numverts = verts.size();
311 
312     memcpy(attr_mP->data_float3() + motion_step * numverts, P, sizeof(float3) * numverts);
313     if (attr_mN)
314       memcpy(attr_mN->data_float3() + motion_step * numverts, N, sizeof(float3) * numverts);
315   }
316 }
317 
get_uv_tiles(ustring map,unordered_set<int> & tiles)318 void Mesh::get_uv_tiles(ustring map, unordered_set<int> &tiles)
319 {
320   Attribute *attr, *subd_attr;
321 
322   if (map.empty()) {
323     attr = attributes.find(ATTR_STD_UV);
324     subd_attr = subd_attributes.find(ATTR_STD_UV);
325   }
326   else {
327     attr = attributes.find(map);
328     subd_attr = subd_attributes.find(map);
329   }
330 
331   if (attr) {
332     attr->get_uv_tiles(this, ATTR_PRIM_GEOMETRY, tiles);
333   }
334   if (subd_attr) {
335     subd_attr->get_uv_tiles(this, ATTR_PRIM_SUBD, tiles);
336   }
337 }
338 
compute_bounds()339 void Mesh::compute_bounds()
340 {
341   BoundBox bnds = BoundBox::empty;
342   size_t verts_size = verts.size();
343 
344   if (verts_size > 0) {
345     for (size_t i = 0; i < verts_size; i++)
346       bnds.grow(verts[i]);
347 
348     Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
349     if (use_motion_blur && attr) {
350       size_t steps_size = verts.size() * (motion_steps - 1);
351       float3 *vert_steps = attr->data_float3();
352 
353       for (size_t i = 0; i < steps_size; i++)
354         bnds.grow(vert_steps[i]);
355     }
356 
357     if (!bnds.valid()) {
358       bnds = BoundBox::empty;
359 
360       /* skip nan or inf coordinates */
361       for (size_t i = 0; i < verts_size; i++)
362         bnds.grow_safe(verts[i]);
363 
364       if (use_motion_blur && attr) {
365         size_t steps_size = verts.size() * (motion_steps - 1);
366         float3 *vert_steps = attr->data_float3();
367 
368         for (size_t i = 0; i < steps_size; i++)
369           bnds.grow_safe(vert_steps[i]);
370       }
371     }
372   }
373 
374   if (!bnds.valid()) {
375     /* empty mesh */
376     bnds.grow(make_float3(0.0f, 0.0f, 0.0f));
377   }
378 
379   bounds = bnds;
380 }
381 
apply_transform(const Transform & tfm,const bool apply_to_motion)382 void Mesh::apply_transform(const Transform &tfm, const bool apply_to_motion)
383 {
384   transform_normal = transform_transposed_inverse(tfm);
385 
386   /* apply to mesh vertices */
387   for (size_t i = 0; i < verts.size(); i++)
388     verts[i] = transform_point(&tfm, verts[i]);
389 
390   if (apply_to_motion) {
391     Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
392 
393     if (attr) {
394       size_t steps_size = verts.size() * (motion_steps - 1);
395       float3 *vert_steps = attr->data_float3();
396 
397       for (size_t i = 0; i < steps_size; i++)
398         vert_steps[i] = transform_point(&tfm, vert_steps[i]);
399     }
400 
401     Attribute *attr_N = attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
402 
403     if (attr_N) {
404       Transform ntfm = transform_normal;
405       size_t steps_size = verts.size() * (motion_steps - 1);
406       float3 *normal_steps = attr_N->data_float3();
407 
408       for (size_t i = 0; i < steps_size; i++)
409         normal_steps[i] = normalize(transform_direction(&ntfm, normal_steps[i]));
410     }
411   }
412 }
413 
add_face_normals()414 void Mesh::add_face_normals()
415 {
416   /* don't compute if already there */
417   if (attributes.find(ATTR_STD_FACE_NORMAL))
418     return;
419 
420   /* get attributes */
421   Attribute *attr_fN = attributes.add(ATTR_STD_FACE_NORMAL);
422   float3 *fN = attr_fN->data_float3();
423 
424   /* compute face normals */
425   size_t triangles_size = num_triangles();
426 
427   if (triangles_size) {
428     float3 *verts_ptr = verts.data();
429 
430     for (size_t i = 0; i < triangles_size; i++) {
431       fN[i] = get_triangle(i).compute_normal(verts_ptr);
432     }
433   }
434 
435   /* expected to be in local space */
436   if (transform_applied) {
437     Transform ntfm = transform_inverse(transform_normal);
438 
439     for (size_t i = 0; i < triangles_size; i++)
440       fN[i] = normalize(transform_direction(&ntfm, fN[i]));
441   }
442 }
443 
add_vertex_normals()444 void Mesh::add_vertex_normals()
445 {
446   bool flip = transform_negative_scaled;
447   size_t verts_size = verts.size();
448   size_t triangles_size = num_triangles();
449 
450   /* static vertex normals */
451   if (!attributes.find(ATTR_STD_VERTEX_NORMAL) && triangles_size) {
452     /* get attributes */
453     Attribute *attr_fN = attributes.find(ATTR_STD_FACE_NORMAL);
454     Attribute *attr_vN = attributes.add(ATTR_STD_VERTEX_NORMAL);
455 
456     float3 *fN = attr_fN->data_float3();
457     float3 *vN = attr_vN->data_float3();
458 
459     /* compute vertex normals */
460     memset(vN, 0, verts.size() * sizeof(float3));
461 
462     for (size_t i = 0; i < triangles_size; i++) {
463       for (size_t j = 0; j < 3; j++) {
464         vN[get_triangle(i).v[j]] += fN[i];
465       }
466     }
467 
468     for (size_t i = 0; i < verts_size; i++) {
469       vN[i] = normalize(vN[i]);
470       if (flip) {
471         vN[i] = -vN[i];
472       }
473     }
474   }
475 
476   /* motion vertex normals */
477   Attribute *attr_mP = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
478   Attribute *attr_mN = attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
479 
480   if (has_motion_blur() && attr_mP && !attr_mN && triangles_size) {
481     /* create attribute */
482     attr_mN = attributes.add(ATTR_STD_MOTION_VERTEX_NORMAL);
483 
484     for (int step = 0; step < motion_steps - 1; step++) {
485       float3 *mP = attr_mP->data_float3() + step * verts.size();
486       float3 *mN = attr_mN->data_float3() + step * verts.size();
487 
488       /* compute */
489       memset(mN, 0, verts.size() * sizeof(float3));
490 
491       for (size_t i = 0; i < triangles_size; i++) {
492         for (size_t j = 0; j < 3; j++) {
493           float3 fN = get_triangle(i).compute_normal(mP);
494           mN[get_triangle(i).v[j]] += fN;
495         }
496       }
497 
498       for (size_t i = 0; i < verts_size; i++) {
499         mN[i] = normalize(mN[i]);
500         if (flip) {
501           mN[i] = -mN[i];
502         }
503       }
504     }
505   }
506 
507   /* subd vertex normals */
508   if (!subd_attributes.find(ATTR_STD_VERTEX_NORMAL) && subd_faces.size()) {
509     /* get attributes */
510     Attribute *attr_vN = subd_attributes.add(ATTR_STD_VERTEX_NORMAL);
511     float3 *vN = attr_vN->data_float3();
512 
513     /* compute vertex normals */
514     memset(vN, 0, verts.size() * sizeof(float3));
515 
516     for (size_t i = 0; i < subd_faces.size(); i++) {
517       SubdFace &face = subd_faces[i];
518       float3 fN = face.normal(this);
519 
520       for (size_t j = 0; j < face.num_corners; j++) {
521         size_t corner = subd_face_corners[face.start_corner + j];
522         vN[corner] += fN;
523       }
524     }
525 
526     for (size_t i = 0; i < verts_size; i++) {
527       vN[i] = normalize(vN[i]);
528       if (flip) {
529         vN[i] = -vN[i];
530       }
531     }
532   }
533 }
534 
add_undisplaced()535 void Mesh::add_undisplaced()
536 {
537   AttributeSet &attrs = (subdivision_type == SUBDIVISION_NONE) ? attributes : subd_attributes;
538 
539   /* don't compute if already there */
540   if (attrs.find(ATTR_STD_POSITION_UNDISPLACED)) {
541     return;
542   }
543 
544   /* get attribute */
545   Attribute *attr = attrs.add(ATTR_STD_POSITION_UNDISPLACED);
546   attr->flags |= ATTR_SUBDIVIDED;
547 
548   float3 *data = attr->data_float3();
549 
550   /* copy verts */
551   size_t size = attr->buffer_size(this, attrs.prim);
552 
553   /* Center points for ngons aren't stored in Mesh::verts but are included in size since they will
554    * be calculated later, we subtract them from size here so we don't have an overflow while
555    * copying.
556    */
557   size -= num_ngons * attr->data_sizeof();
558 
559   if (size) {
560     memcpy(data, verts.data(), size);
561   }
562 }
563 
pack_shaders(Scene * scene,uint * tri_shader)564 void Mesh::pack_shaders(Scene *scene, uint *tri_shader)
565 {
566   uint shader_id = 0;
567   uint last_shader = -1;
568   bool last_smooth = false;
569 
570   size_t triangles_size = num_triangles();
571   int *shader_ptr = shader.data();
572 
573   for (size_t i = 0; i < triangles_size; i++) {
574     if (shader_ptr[i] != last_shader || last_smooth != smooth[i]) {
575       last_shader = shader_ptr[i];
576       last_smooth = smooth[i];
577       Shader *shader = (last_shader < used_shaders.size()) ? used_shaders[last_shader] :
578                                                              scene->default_surface;
579       shader_id = scene->shader_manager->get_shader_id(shader, last_smooth);
580     }
581 
582     tri_shader[i] = shader_id;
583   }
584 }
585 
pack_normals(float4 * vnormal)586 void Mesh::pack_normals(float4 *vnormal)
587 {
588   Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL);
589   if (attr_vN == NULL) {
590     /* Happens on objects with just hair. */
591     return;
592   }
593 
594   bool do_transform = transform_applied;
595   Transform ntfm = transform_normal;
596 
597   float3 *vN = attr_vN->data_float3();
598   size_t verts_size = verts.size();
599 
600   for (size_t i = 0; i < verts_size; i++) {
601     float3 vNi = vN[i];
602 
603     if (do_transform)
604       vNi = safe_normalize(transform_direction(&ntfm, vNi));
605 
606     vnormal[i] = make_float4(vNi.x, vNi.y, vNi.z, 0.0f);
607   }
608 }
609 
pack_verts(const vector<uint> & tri_prim_index,uint4 * tri_vindex,uint * tri_patch,float2 * tri_patch_uv,size_t vert_offset,size_t tri_offset)610 void Mesh::pack_verts(const vector<uint> &tri_prim_index,
611                       uint4 *tri_vindex,
612                       uint *tri_patch,
613                       float2 *tri_patch_uv,
614                       size_t vert_offset,
615                       size_t tri_offset)
616 {
617   size_t verts_size = verts.size();
618 
619   if (verts_size && subd_faces.size()) {
620     float2 *vert_patch_uv_ptr = vert_patch_uv.data();
621 
622     for (size_t i = 0; i < verts_size; i++) {
623       tri_patch_uv[i] = vert_patch_uv_ptr[i];
624     }
625   }
626 
627   size_t triangles_size = num_triangles();
628 
629   for (size_t i = 0; i < triangles_size; i++) {
630     Triangle t = get_triangle(i);
631     tri_vindex[i] = make_uint4(t.v[0] + vert_offset,
632                                t.v[1] + vert_offset,
633                                t.v[2] + vert_offset,
634                                tri_prim_index[i + tri_offset]);
635 
636     tri_patch[i] = (!subd_faces.size()) ? -1 : (triangle_patch[i] * 8 + patch_offset);
637   }
638 }
639 
pack_patches(uint * patch_data,uint vert_offset,uint face_offset,uint corner_offset)640 void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset)
641 {
642   size_t num_faces = subd_faces.size();
643   int ngons = 0;
644 
645   for (size_t f = 0; f < num_faces; f++) {
646     SubdFace face = subd_faces[f];
647 
648     if (face.is_quad()) {
649       int c[4];
650       memcpy(c, &subd_face_corners[face.start_corner], sizeof(int) * 4);
651 
652       *(patch_data++) = c[0] + vert_offset;
653       *(patch_data++) = c[1] + vert_offset;
654       *(patch_data++) = c[2] + vert_offset;
655       *(patch_data++) = c[3] + vert_offset;
656 
657       *(patch_data++) = f + face_offset;
658       *(patch_data++) = face.num_corners;
659       *(patch_data++) = face.start_corner + corner_offset;
660       *(patch_data++) = 0;
661     }
662     else {
663       for (int i = 0; i < face.num_corners; i++) {
664         int c[4];
665         c[0] = subd_face_corners[face.start_corner + mod(i + 0, face.num_corners)];
666         c[1] = subd_face_corners[face.start_corner + mod(i + 1, face.num_corners)];
667         c[2] = verts.size() - num_subd_verts + ngons;
668         c[3] = subd_face_corners[face.start_corner + mod(i - 1, face.num_corners)];
669 
670         *(patch_data++) = c[0] + vert_offset;
671         *(patch_data++) = c[1] + vert_offset;
672         *(patch_data++) = c[2] + vert_offset;
673         *(patch_data++) = c[3] + vert_offset;
674 
675         *(patch_data++) = f + face_offset;
676         *(patch_data++) = face.num_corners | (i << 16);
677         *(patch_data++) = face.start_corner + corner_offset;
678         *(patch_data++) = subd_face_corners.size() + ngons + corner_offset;
679       }
680 
681       ngons++;
682     }
683   }
684 }
685 
686 CCL_NAMESPACE_END
687