Blender V4.3
mesh_subdivision.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "scene/attribute.h"
6#include "scene/camera.h"
7#include "scene/mesh.h"
8
9#include "subd/patch.h"
10#include "subd/patch_table.h"
11#include "subd/split.h"
12
13#include "util/algorithm.h"
14#include "util/foreach.h"
15#include "util/hash.h"
16
18
19#ifdef WITH_OPENSUBDIV
20
22
23# include <opensubdiv/far/patchMap.h>
24# include <opensubdiv/far/patchTableFactory.h>
25# include <opensubdiv/far/primvarRefiner.h>
26# include <opensubdiv/far/topologyRefinerFactory.h>
27
28/* specializations of TopologyRefinerFactory for ccl::Mesh */
29
30namespace OpenSubdiv {
31namespace OPENSUBDIV_VERSION {
32namespace Far {
33template<>
34bool TopologyRefinerFactory<ccl::Mesh>::resizeComponentTopology(TopologyRefiner &refiner,
35 ccl::Mesh const &mesh)
36{
37 setNumBaseVertices(refiner, mesh.get_verts().size());
38 setNumBaseFaces(refiner, mesh.get_num_subd_faces());
39
40 for (int i = 0; i < mesh.get_num_subd_faces(); i++) {
41 setNumBaseFaceVertices(refiner, i, mesh.get_subd_num_corners()[i]);
42 }
43
44 return true;
45}
46
47template<>
48bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTopology(TopologyRefiner &refiner,
49 ccl::Mesh const &mesh)
50{
51 const ccl::array<int> &subd_face_corners = mesh.get_subd_face_corners();
52 const ccl::array<int> &subd_start_corner = mesh.get_subd_start_corner();
53 const ccl::array<int> &subd_num_corners = mesh.get_subd_num_corners();
54
55 for (int i = 0; i < mesh.get_num_subd_faces(); i++) {
56 IndexArray face_verts = getBaseFaceVertices(refiner, i);
57
58 int start_corner = subd_start_corner[i];
59 int *corner = &subd_face_corners[start_corner];
60
61 for (int j = 0; j < subd_num_corners[i]; j++, corner++) {
62 face_verts[j] = *corner;
63 }
64 }
65
66 return true;
67}
68
69template<>
70bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTags(TopologyRefiner &refiner,
71 ccl::Mesh const &mesh)
72{
73 /* Historical maximum crease weight used at Pixar, influencing the maximum in OpenSubDiv. */
74 static constexpr float CREASE_SCALE = 10.0f;
75
76 size_t num_creases = mesh.get_subd_creases_weight().size();
77 size_t num_vertex_creases = mesh.get_subd_vert_creases().size();
78
79 /* The last loop is over the vertices, so early exit to avoid iterating them needlessly. */
80 if (num_creases == 0 && num_vertex_creases == 0) {
81 return true;
82 }
83
84 for (int i = 0; i < num_creases; i++) {
85 ccl::Mesh::SubdEdgeCrease crease = mesh.get_subd_crease(i);
86 Index edge = findBaseEdge(refiner, crease.v[0], crease.v[1]);
87
88 if (edge != INDEX_INVALID) {
89 setBaseEdgeSharpness(refiner, edge, crease.crease * CREASE_SCALE);
90 }
91 }
92
93 std::map<int, float> vertex_creases;
94
95 for (size_t i = 0; i < num_vertex_creases; ++i) {
96 const int vertex_idx = mesh.get_subd_vert_creases()[i];
97 const float weight = mesh.get_subd_vert_creases_weight()[i];
98
99 vertex_creases[vertex_idx] = weight * CREASE_SCALE;
100 }
101
102 for (int i = 0; i < mesh.get_verts().size(); i++) {
103 float sharpness = 0.0f;
104 std::map<int, float>::const_iterator iter = vertex_creases.find(i);
105
106 if (iter != vertex_creases.end()) {
107 sharpness = iter->second;
108 }
109
110 ConstIndexArray vert_edges = getBaseVertexEdges(refiner, i);
111
112 if (vert_edges.size() == 2) {
113 const float sharpness0 = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]);
114 const float sharpness1 = refiner.getLevel(0).getEdgeSharpness(vert_edges[1]);
115
116 sharpness += ccl::min(sharpness0, sharpness1);
117 sharpness = ccl::min(sharpness, CREASE_SCALE);
118 }
119
120 if (sharpness != 0.0f) {
121 setBaseVertexSharpness(refiner, i, sharpness);
122 }
123 }
124
125 return true;
126}
127
128template<>
129bool TopologyRefinerFactory<ccl::Mesh>::assignFaceVaryingTopology(TopologyRefiner & /*refiner*/,
130 ccl::Mesh const & /*mesh*/)
131{
132 return true;
133}
134
135template<>
136void TopologyRefinerFactory<ccl::Mesh>::reportInvalidTopology(TopologyError /*err_code*/,
137 char const * /*msg*/,
138 ccl::Mesh const & /*mesh*/)
139{
140}
141} /* namespace Far */
142} /* namespace OPENSUBDIV_VERSION */
143} /* namespace OpenSubdiv */
144
146
147using namespace OpenSubdiv;
148
149/* struct that implements OpenSubdiv's vertex interface */
150
151template<typename T> struct OsdValue {
152 T value;
153
154 OsdValue() {}
155
156 void Clear(void * = 0)
157 {
158 memset(&value, 0, sizeof(T));
159 }
160
161 void AddWithWeight(OsdValue<T> const &src, float weight)
162 {
163 value += src.value * weight;
164 }
165};
166
167template<> void OsdValue<uchar4>::AddWithWeight(OsdValue<uchar4> const &src, float weight)
168{
169 for (int i = 0; i < 4; i++) {
170 value[i] += (uchar)(src.value[i] * weight);
171 }
172}
173
174/* class for holding OpenSubdiv data used during tessellation */
175
176class OsdData {
177 Mesh *mesh;
179 Far::TopologyRefiner *refiner;
180 Far::PatchTable *patch_table;
181 Far::PatchMap *patch_map;
182
183 public:
184 OsdData() : mesh(NULL), refiner(NULL), patch_table(NULL), patch_map(NULL) {}
185
186 ~OsdData()
187 {
188 delete refiner;
189 delete patch_table;
190 delete patch_map;
191 }
192
193 void build_from_mesh(Mesh *mesh_)
194 {
195 mesh = mesh_;
196
197 /* type and options */
198 Sdc::SchemeType type = Sdc::SCHEME_CATMARK;
199
200 Sdc::Options options;
201 options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
202
203 /* create refiner */
204 refiner = Far::TopologyRefinerFactory<Mesh>::Create(
205 *mesh, Far::TopologyRefinerFactory<Mesh>::Options(type, options));
206
207 /* adaptive refinement */
208 int max_isolation = calculate_max_isolation();
209 refiner->RefineAdaptive(Far::TopologyRefiner::AdaptiveOptions(max_isolation));
210
211 /* create patch table */
212 Far::PatchTableFactory::Options patch_options;
213 patch_options.endCapType = Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
214
215 patch_table = Far::PatchTableFactory::Create(*refiner, patch_options);
216
217 /* interpolate verts */
218 int num_refiner_verts = refiner->GetNumVerticesTotal();
219 int num_local_points = patch_table->GetNumLocalPoints();
220
221 verts.resize(num_refiner_verts + num_local_points);
222 for (int i = 0; i < mesh->get_verts().size(); i++) {
223 verts[i].value = mesh->get_verts()[i];
224 }
225
226 OsdValue<float3> *src = verts.data();
227 for (int i = 0; i < refiner->GetMaxLevel(); i++) {
228 OsdValue<float3> *dest = src + refiner->GetLevel(i).GetNumVertices();
229 Far::PrimvarRefiner(*refiner).Interpolate(i + 1, src, dest);
230 src = dest;
231 }
232
233 if (num_local_points) {
234 patch_table->ComputeLocalPointValues(&verts[0], &verts[num_refiner_verts]);
235 }
236
237 /* create patch map */
238 patch_map = new Far::PatchMap(*patch_table);
239 }
240
241 void subdivide_attribute(Attribute &attr)
242 {
243 Far::PrimvarRefiner primvar_refiner(*refiner);
244
245 if (attr.element == ATTR_ELEMENT_VERTEX) {
246 int num_refiner_verts = refiner->GetNumVerticesTotal();
247 int num_local_points = patch_table->GetNumLocalPoints();
248
249 attr.resize(num_refiner_verts + num_local_points);
250 attr.flags |= ATTR_FINAL_SIZE;
251
252 char *src = attr.buffer.data();
253
254 for (int i = 0; i < refiner->GetMaxLevel(); i++) {
255 char *dest = src + refiner->GetLevel(i).GetNumVertices() * attr.data_sizeof();
256
257 if (attr.same_storage(attr.type, TypeDesc::TypeFloat)) {
258 primvar_refiner.Interpolate(i + 1, (OsdValue<float> *)src, (OsdValue<float> *&)dest);
259 }
260 else if (attr.same_storage(attr.type, TypeFloat2)) {
261 primvar_refiner.Interpolate(i + 1, (OsdValue<float2> *)src, (OsdValue<float2> *&)dest);
262 // float3 is not interchangeable with float4 and so needs to be handled
263 // separately
264 }
265 else if (attr.same_storage(attr.type, TypeFloat4)) {
266 primvar_refiner.Interpolate(i + 1, (OsdValue<float4> *)src, (OsdValue<float4> *&)dest);
267 }
268 else {
269 primvar_refiner.Interpolate(i + 1, (OsdValue<float3> *)src, (OsdValue<float3> *&)dest);
270 }
271
272 src = dest;
273 }
274
275 if (num_local_points) {
276 if (attr.same_storage(attr.type, TypeDesc::TypeFloat)) {
277 patch_table->ComputeLocalPointValues(
278 (OsdValue<float> *)&attr.buffer[0],
279 (OsdValue<float> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
280 }
281 else if (attr.same_storage(attr.type, TypeFloat2)) {
282 patch_table->ComputeLocalPointValues(
283 (OsdValue<float2> *)&attr.buffer[0],
284 (OsdValue<float2> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
285 }
286 else if (attr.same_storage(attr.type, TypeFloat4)) {
287 // float3 is not interchangeable with float4 and so needs to be handled
288 // separately
289 patch_table->ComputeLocalPointValues(
290 (OsdValue<float4> *)&attr.buffer[0],
291 (OsdValue<float4> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
292 }
293 else {
294 // float3 is not interchangeable with float4 and so needs to be handled
295 // separately
296 patch_table->ComputeLocalPointValues(
297 (OsdValue<float3> *)&attr.buffer[0],
298 (OsdValue<float3> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
299 }
300 }
301 }
302 else if (attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) {
303 // TODO(mai): fvar interpolation
304 }
305 }
306
307 int calculate_max_isolation()
308 {
309 /* loop over all edges to find longest in screen space */
310 const Far::TopologyLevel &level = refiner->GetLevel(0);
311 const SubdParams *subd_params = mesh->get_subd_params();
312 Transform objecttoworld = subd_params->objecttoworld;
313 Camera *cam = subd_params->camera;
314
315 float longest_edge = 0.0f;
316
317 for (size_t i = 0; i < level.GetNumEdges(); i++) {
318 Far::ConstIndexArray verts = level.GetEdgeVertices(i);
319
320 float3 a = mesh->get_verts()[verts[0]];
321 float3 b = mesh->get_verts()[verts[1]];
322
323 float edge_len;
324
325 if (cam) {
326 a = transform_point(&objecttoworld, a);
327 b = transform_point(&objecttoworld, b);
328
329 edge_len = len(a - b) / cam->world_to_raster_size((a + b) * 0.5f);
330 }
331 else {
332 edge_len = len(a - b);
333 }
334
335 longest_edge = max(longest_edge, edge_len);
336 }
337
338 /* calculate isolation level */
339 int isolation = (int)(log2f(max(longest_edge / subd_params->dicing_rate, 1.0f)) + 1.0f);
340
341 return min(isolation, 10);
342 }
343
344 friend struct OsdPatch;
345 friend class Mesh;
346};
347
348/* ccl::Patch implementation that uses OpenSubdiv for eval */
349
350struct OsdPatch : Patch {
351 OsdData *osd_data;
352
353 OsdPatch() {}
354 OsdPatch(OsdData *data) : osd_data(data) {}
355
356 void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)
357 {
358 const Far::PatchTable::PatchHandle *handle = osd_data->patch_map->FindPatch(
359 patch_index, (double)u, (double)v);
360 assert(handle);
361
362 float p_weights[20], du_weights[20], dv_weights[20];
363 osd_data->patch_table->EvaluateBasis(*handle, u, v, p_weights, du_weights, dv_weights);
364
365 Far::ConstIndexArray cv = osd_data->patch_table->GetPatchVertices(*handle);
366
367 float3 du, dv;
368 if (P)
369 *P = zero_float3();
370 du = zero_float3();
371 dv = zero_float3();
372
373 for (int i = 0; i < cv.size(); i++) {
374 float3 p = osd_data->verts[cv[i]].value;
375
376 if (P)
377 *P += p * p_weights[i];
378 du += p * du_weights[i];
379 dv += p * dv_weights[i];
380 }
381
382 if (dPdu)
383 *dPdu = du;
384 if (dPdv)
385 *dPdv = dv;
386 if (N) {
387 *N = cross(du, dv);
388
389 float t = len(*N);
390 *N = (t != 0.0f) ? *N / t : make_float3(0.0f, 0.0f, 1.0f);
391 }
392 }
393};
394
395#endif
396
398{
399 /* reset the number of subdivision vertices, in case the Mesh was not cleared
400 * between calls or data updates */
401 num_subd_verts = 0;
402
403#ifdef WITH_OPENSUBDIV
404 OsdData osd_data;
405 bool need_packed_patch_table = false;
406
407 if (subdivision_type == SUBDIVISION_CATMULL_CLARK) {
408 if (get_num_subd_faces()) {
409 osd_data.build_from_mesh(this);
410 }
411 }
412 else
413#endif
414 {
415 /* force linear subdivision if OpenSubdiv is unavailable to avoid
416 * falling into catmull-clark code paths by accident
417 */
418 subdivision_type = SUBDIVISION_LINEAR;
419
420 /* force disable attribute subdivision for same reason as above */
421 foreach (Attribute &attr, subd_attributes.attributes) {
422 attr.flags &= ~ATTR_SUBDIVIDED;
423 }
424 }
425
426 int num_faces = get_num_subd_faces();
427
429 float3 *vN = (attr_vN) ? attr_vN->data_float3() : NULL;
430
431 /* count patches */
432 int num_patches = 0;
433 for (int f = 0; f < num_faces; f++) {
434 SubdFace face = get_subd_face(f);
435
436 if (face.is_quad()) {
437 num_patches++;
438 }
439 else {
440 num_patches += face.num_corners;
441 }
442 }
443
444 /* build patches from faces */
445#ifdef WITH_OPENSUBDIV
446 if (subdivision_type == SUBDIVISION_CATMULL_CLARK) {
447 vector<OsdPatch> osd_patches(num_patches, &osd_data);
448 OsdPatch *patch = osd_patches.data();
449
450 for (int f = 0; f < num_faces; f++) {
451 SubdFace face = get_subd_face(f);
452
453 if (face.is_quad()) {
454 patch->patch_index = face.ptex_offset;
455 patch->from_ngon = false;
456 patch->shader = face.shader;
457 patch++;
458 }
459 else {
460 for (int corner = 0; corner < face.num_corners; corner++) {
461 patch->patch_index = face.ptex_offset + corner;
462 patch->from_ngon = true;
463 patch->shader = face.shader;
464 patch++;
465 }
466 }
467 }
468
469 /* split patches */
470 split->split_patches(osd_patches.data(), sizeof(OsdPatch));
471 }
472 else
473#endif
474 {
475 vector<LinearQuadPatch> linear_patches(num_patches);
476 LinearQuadPatch *patch = linear_patches.data();
477
478 for (int f = 0; f < num_faces; f++) {
479 SubdFace face = get_subd_face(f);
480
481 if (face.is_quad()) {
482 float3 *hull = patch->hull;
483 float3 *normals = patch->normals;
484
485 patch->patch_index = face.ptex_offset;
486 patch->from_ngon = false;
487
488 for (int i = 0; i < 4; i++) {
489 hull[i] = verts[subd_face_corners[face.start_corner + i]];
490 }
491
492 if (face.smooth) {
493 for (int i = 0; i < 4; i++) {
494 normals[i] = vN[subd_face_corners[face.start_corner + i]];
495 }
496 }
497 else {
498 float3 N = face.normal(this);
499 for (int i = 0; i < 4; i++) {
500 normals[i] = N;
501 }
502 }
503
504 swap(hull[2], hull[3]);
505 swap(normals[2], normals[3]);
506
507 patch->shader = face.shader;
508 patch++;
509 }
510 else {
511 /* ngon */
512 float3 center_vert = zero_float3();
513 float3 center_normal = zero_float3();
514
515 float inv_num_corners = 1.0f / float(face.num_corners);
516 for (int corner = 0; corner < face.num_corners; corner++) {
517 center_vert += verts[subd_face_corners[face.start_corner + corner]] * inv_num_corners;
518 center_normal += vN[subd_face_corners[face.start_corner + corner]] * inv_num_corners;
519 }
520
521 for (int corner = 0; corner < face.num_corners; corner++) {
522 float3 *hull = patch->hull;
523 float3 *normals = patch->normals;
524
525 patch->patch_index = face.ptex_offset + corner;
526 patch->from_ngon = true;
527
528 patch->shader = face.shader;
529
530 hull[0] =
531 verts[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]];
532 hull[1] =
533 verts[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]];
534 hull[2] =
535 verts[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]];
536 hull[3] = center_vert;
537
538 hull[1] = (hull[1] + hull[0]) * 0.5;
539 hull[2] = (hull[2] + hull[0]) * 0.5;
540
541 if (face.smooth) {
542 normals[0] =
543 vN[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]];
544 normals[1] =
545 vN[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]];
546 normals[2] =
547 vN[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]];
548 normals[3] = center_normal;
549
550 normals[1] = (normals[1] + normals[0]) * 0.5;
551 normals[2] = (normals[2] + normals[0]) * 0.5;
552 }
553 else {
554 float3 N = face.normal(this);
555 for (int i = 0; i < 4; i++) {
556 normals[i] = N;
557 }
558 }
559
560 patch++;
561 }
562 }
563 }
564
565 /* split patches */
566 split->split_patches(linear_patches.data(), sizeof(LinearQuadPatch));
567 }
568
569 /* interpolate center points for attributes */
570 foreach (Attribute &attr, subd_attributes.attributes) {
571#ifdef WITH_OPENSUBDIV
572 if (subdivision_type == SUBDIVISION_CATMULL_CLARK && attr.flags & ATTR_SUBDIVIDED) {
574 /* keep subdivision for corner attributes disabled for now */
575 attr.flags &= ~ATTR_SUBDIVIDED;
576 }
577 else if (get_num_subd_faces()) {
578 osd_data.subdivide_attribute(attr);
579
580 need_packed_patch_table = true;
581 continue;
582 }
583 }
584#endif
585
586 char *data = attr.data();
587 size_t stride = attr.data_sizeof();
588 int ngons = 0;
589
590 switch (attr.element) {
591 case ATTR_ELEMENT_VERTEX: {
592 for (int f = 0; f < num_faces; f++) {
593 SubdFace face = get_subd_face(f);
594
595 if (!face.is_quad()) {
596 char *center = data + (verts.size() - num_subd_verts + ngons) * stride;
597 attr.zero_data(center);
598
599 float inv_num_corners = 1.0f / float(face.num_corners);
600
601 for (int corner = 0; corner < face.num_corners; corner++) {
602 attr.add_with_weight(center,
603 data + subd_face_corners[face.start_corner + corner] * stride,
604 inv_num_corners);
605 }
606
607 ngons++;
608 }
609 }
610 break;
611 }
613 // TODO(mai): implement
614 break;
615 }
616 case ATTR_ELEMENT_CORNER: {
617 for (int f = 0; f < num_faces; f++) {
618 SubdFace face = get_subd_face(f);
619
620 if (!face.is_quad()) {
621 char *center = data + (subd_face_corners.size() + ngons) * stride;
622 attr.zero_data(center);
623
624 float inv_num_corners = 1.0f / float(face.num_corners);
625
626 for (int corner = 0; corner < face.num_corners; corner++) {
627 attr.add_with_weight(
628 center, data + (face.start_corner + corner) * stride, inv_num_corners);
629 }
630
631 ngons++;
632 }
633 }
634 break;
635 }
637 for (int f = 0; f < num_faces; f++) {
638 SubdFace face = get_subd_face(f);
639
640 if (!face.is_quad()) {
641 uchar *center = (uchar *)data + (subd_face_corners.size() + ngons) * stride;
642
643 float inv_num_corners = 1.0f / float(face.num_corners);
644 float4 val = zero_float4();
645
646 for (int corner = 0; corner < face.num_corners; corner++) {
647 for (int i = 0; i < 4; i++) {
648 val[i] += float(*(data + (face.start_corner + corner) * stride + i)) *
649 inv_num_corners;
650 }
651 }
652
653 for (int i = 0; i < 4; i++) {
654 center[i] = uchar(min(max(val[i], 0.0f), 255.0f));
655 }
656
657 ngons++;
658 }
659 }
660 break;
661 }
662 default:
663 break;
664 }
665 }
666
667#ifdef WITH_OPENSUBDIV
668 /* pack patch tables */
669 if (need_packed_patch_table) {
670 delete patch_table;
671 patch_table = new PackedPatchTable;
672 patch_table->pack(osd_data.patch_table);
673 }
674#endif
675}
676
unsigned char uchar
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
list< Attribute > attributes
Attribute * find(ustring name) const
AttributeElement element
static bool same_storage(TypeDesc a, TypeDesc b)
void add_with_weight(void *dst, void *src, float weight)
char * data()
TypeDesc type
void resize(Geometry *geom, AttributePrimitive prim, bool reserve_only)
vector< char > buffer
void zero_data(void *dst)
size_t data_sizeof() const
float3 * data_float3()
float3 hull[4]
Definition subd/patch.h:30
float3 normals[4]
Definition subd/patch.h:31
int shader
Definition subd/patch.h:22
bool from_ngon
Definition subd/patch.h:23
int patch_index
Definition subd/patch.h:21
virtual void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)=0
local_group_size(16, 16) .push_constant(Type b
CCL_NAMESPACE_BEGIN struct Options options
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define NULL
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
@ ATTR_STD_VERTEX_NORMAL
@ ATTR_FINAL_SIZE
@ ATTR_SUBDIVIDED
@ ATTR_ELEMENT_CORNER_BYTE
@ ATTR_ELEMENT_CORNER
@ ATTR_ELEMENT_VERTEX_MOTION
@ ATTR_ELEMENT_VERTEX
ccl_device_inline float cross(const float2 a, const float2 b)
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:15
CCL_NAMESPACE_BEGIN ccl_device_inline float4 zero_float4()
Definition math_float4.h:15
#define N
#define INDEX_INVALID
CCL_NAMESPACE_BEGIN static OIIO_NAMESPACE_USING constexpr TypeDesc TypeFloat2(TypeDesc::FLOAT, TypeDesc::VEC2)
#define swap(a, b)
Definition sort.c:55
#define min(a, b)
Definition sort.c:32
float world_to_raster_size(float3 P)
void tessellate(DiagSplit *split)
size_t get_num_subd_faces() const
Definition scene/mesh.h:233
AttributeSet subd_attributes
Definition scene/mesh.h:159
@ SUBDIVISION_LINEAR
Definition scene/mesh.h:122
@ SUBDIVISION_CATMULL_CLARK
Definition scene/mesh.h:123
SubdFace get_subd_face(size_t index) const
void pack(Far::PatchTable *patch_table, int offset=0)
Camera * camera
Definition dice.h:32
Transform objecttoworld
Definition dice.h:33
float dicing_rate
Definition dice.h:30
CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN ccl_device_inline float3 transform_point(ccl_private const Transform *t, const float3 a)
Definition transform.h:63
float max
ccl_device_inline int mod(int x, int m)
Definition util/math.h:520