Blender V4.3
blender/mesh.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 <optional>
6
8#include "blender/session.h"
9#include "blender/sync.h"
10#include "blender/util.h"
11
12#include "scene/camera.h"
13#include "scene/colorspace.h"
14#include "scene/mesh.h"
15#include "scene/object.h"
16#include "scene/scene.h"
17
18#include "subd/patch.h"
19#include "subd/split.h"
20
21#include "util/algorithm.h"
22#include "util/color.h"
23#include "util/disjoint_set.h"
24#include "util/foreach.h"
25#include "util/hash.h"
26#include "util/log.h"
27#include "util/math.h"
28
29#include "mikktspace.hh"
30
31#include "BKE_attribute.hh"
32#include "BKE_attribute_math.hh"
33#include "BKE_customdata.hh"
34#include "BKE_mesh.hh"
35
37
38/* Tangent Space */
39
40template<bool is_subd> struct MikkMeshWrapper {
41 MikkMeshWrapper(const ::Mesh &b_mesh,
42 const char *layer_name,
43 const Mesh *mesh,
45 float *tangent_sign)
47 {
48 const AttributeSet &attributes = is_subd ? mesh->subd_attributes : mesh->attributes;
49
50 Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL);
51 vertex_normal = attr_vN->data_float3();
52
53 if (layer_name == NULL) {
54 Attribute *attr_orco = attributes.find(ATTR_STD_GENERATED);
55
56 if (attr_orco) {
57 orco = attr_orco->data_float3();
58 float3 orco_size;
59 mesh_texture_space(b_mesh, orco_loc, orco_size);
60 inv_orco_size = 1.0f / orco_size;
61 }
62 }
63 else {
64 Attribute *attr_uv = attributes.find(ustring(layer_name));
65 if (attr_uv != NULL) {
66 uv = attr_uv->data_float2();
67 }
68 }
69 }
70
72 {
73 if constexpr (is_subd) {
74 return mesh->get_num_subd_faces();
75 }
76 else {
77 return mesh->num_triangles();
78 }
79 }
80
81 int GetNumVerticesOfFace(const int face_num)
82 {
83 if constexpr (is_subd) {
84 return mesh->get_subd_num_corners()[face_num];
85 }
86 else {
87 return 3;
88 }
89 }
90
91 int CornerIndex(const int face_num, const int vert_num)
92 {
93 if constexpr (is_subd) {
94 const Mesh::SubdFace &face = mesh->get_subd_face(face_num);
95 return face.start_corner + vert_num;
96 }
97 else {
98 return face_num * 3 + vert_num;
99 }
100 }
101
102 int VertexIndex(const int face_num, const int vert_num)
103 {
104 int corner = CornerIndex(face_num, vert_num);
105 if constexpr (is_subd) {
106 return mesh->get_subd_face_corners()[corner];
107 }
108 else {
109 return mesh->get_triangles()[corner];
110 }
111 }
112
113 mikk::float3 GetPosition(const int face_num, const int vert_num)
114 {
115 const float3 vP = mesh->get_verts()[VertexIndex(face_num, vert_num)];
116 return mikk::float3(vP.x, vP.y, vP.z);
117 }
118
119 mikk::float3 GetTexCoord(const int face_num, const int vert_num)
120 {
121 /* TODO: Check whether introducing a template boolean in order to
122 * turn this into a constexpr is worth it. */
123 if (uv != NULL) {
124 const int corner_index = CornerIndex(face_num, vert_num);
125 float2 tfuv = uv[corner_index];
126 return mikk::float3(tfuv.x, tfuv.y, 1.0f);
127 }
128 else if (orco != NULL) {
129 const int vertex_index = VertexIndex(face_num, vert_num);
130 const float2 uv = map_to_sphere((orco[vertex_index] + orco_loc) * inv_orco_size);
131 return mikk::float3(uv.x, uv.y, 1.0f);
132 }
133 else {
134 return mikk::float3(0.0f, 0.0f, 1.0f);
135 }
136 }
137
138 mikk::float3 GetNormal(const int face_num, const int vert_num)
139 {
140 float3 vN;
141 if (is_subd) {
142 const Mesh::SubdFace &face = mesh->get_subd_face(face_num);
143 if (face.smooth) {
144 const int vertex_index = VertexIndex(face_num, vert_num);
145 vN = vertex_normal[vertex_index];
146 }
147 else {
148 vN = face.normal(mesh);
149 }
150 }
151 else {
152 if (mesh->get_smooth()[face_num]) {
153 const int vertex_index = VertexIndex(face_num, vert_num);
154 vN = vertex_normal[vertex_index];
155 }
156 else {
157 const Mesh::Triangle tri = mesh->get_triangle(face_num);
158 vN = tri.compute_normal(&mesh->get_verts()[0]);
159 }
160 }
161 return mikk::float3(vN.x, vN.y, vN.z);
162 }
163
164 void SetTangentSpace(const int face_num, const int vert_num, mikk::float3 T, bool orientation)
165 {
166 const int corner_index = CornerIndex(face_num, vert_num);
167 tangent[corner_index] = make_float3(T.x, T.y, T.z);
168 if (tangent_sign != NULL) {
169 tangent_sign[corner_index] = orientation ? 1.0f : -1.0f;
170 }
171 }
172
173 const Mesh *mesh;
175
181
184};
185
187 const ::Mesh &b_mesh, const char *layer_name, Mesh *mesh, bool need_sign, bool active_render)
188{
189 /* Create tangent attributes. */
190 const bool is_subd = mesh->get_num_subd_faces();
191 AttributeSet &attributes = is_subd ? mesh->subd_attributes : mesh->attributes;
192 Attribute *attr;
193 ustring name;
194 if (layer_name != NULL) {
195 name = ustring((string(layer_name) + ".tangent").c_str());
196 }
197 else {
198 name = ustring("orco.tangent");
199 }
200 if (active_render) {
201 attr = attributes.add(ATTR_STD_UV_TANGENT, name);
202 }
203 else {
204 attr = attributes.add(name, TypeVector, ATTR_ELEMENT_CORNER);
205 }
206 float3 *tangent = attr->data_float3();
207 /* Create bitangent sign attribute. */
208 float *tangent_sign = NULL;
209 if (need_sign) {
210 Attribute *attr_sign;
211 ustring name_sign;
212 if (layer_name != NULL) {
213 name_sign = ustring((string(layer_name) + ".tangent_sign").c_str());
214 }
215 else {
216 name_sign = ustring("orco.tangent_sign");
217 }
218
219 if (active_render) {
220 attr_sign = attributes.add(ATTR_STD_UV_TANGENT_SIGN, name_sign);
221 }
222 else {
223 attr_sign = attributes.add(name_sign, TypeFloat, ATTR_ELEMENT_CORNER);
224 }
225 tangent_sign = attr_sign->data_float();
226 }
227
228 /* Setup userdata. */
229 if (is_subd) {
230 MikkMeshWrapper<true> userdata(b_mesh, layer_name, mesh, tangent, tangent_sign);
231 /* Compute tangents. */
232 mikk::Mikktspace(userdata).genTangSpace();
233 }
234 else {
235 MikkMeshWrapper<false> userdata(b_mesh, layer_name, mesh, tangent, tangent_sign);
236 /* Compute tangents. */
237 mikk::Mikktspace(userdata).genTangSpace();
238 }
239}
240
243 const float motion_scale)
244{
245 const int numverts = mesh->get_verts().size();
246
247 /* Override motion steps to fixed number. */
248 mesh->set_motion_steps(3);
249
250 /* Find or add attribute */
251 float3 *P = &mesh->get_verts()[0];
253
254 if (!attr_mP) {
256 }
257
258 /* Only export previous and next frame, we don't have any in between data. */
259 float motion_times[2] = {-1.0f, 1.0f};
260 for (int step = 0; step < 2; step++) {
261 const float relative_time = motion_times[step] * 0.5f * motion_scale;
262 float3 *mP = attr_mP->data_float3() + step * numverts;
263
264 for (int i = 0; i < numverts; i++) {
265 mP[i] = P[i] + make_float3(b_attr[i][0], b_attr[i][1], b_attr[i][2]) * relative_time;
266 }
267 }
268}
269
270static void attr_create_generic(Scene *scene,
271 Mesh *mesh,
272 const ::Mesh &b_mesh,
273 const bool subdivision,
274 const bool need_motion,
275 const float motion_scale)
276{
278 blender::Span<int> tri_faces;
279 if (!subdivision) {
280 corner_tris = b_mesh.corner_tris();
281 tri_faces = b_mesh.corner_tri_faces();
282 }
283 const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes();
284 AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
285 static const ustring u_velocity("velocity");
286 const ustring default_color_name{BKE_id_attributes_default_color_name(&b_mesh.id)};
287
288 b_attributes.foreach_attribute([&](const blender::bke::AttributeIter &iter) {
289 const ustring name{std::string_view(iter.name)};
290 const bool is_render_color = name == default_color_name;
291
292 if (need_motion && name == u_velocity) {
293 const blender::VArraySpan b_attribute = *iter.get<blender::float3>(
295 attr_create_motion_from_velocity(mesh, b_attribute, motion_scale);
296 }
297
298 if (!(mesh->need_attribute(scene, name) ||
299 (is_render_color && mesh->need_attribute(scene, ATTR_STD_VERTEX_COLOR))))
300 {
301 return;
302 }
303 if (attributes.find(name)) {
304 return;
305 }
306
307 blender::bke::AttrDomain b_domain = iter.domain;
308 if (b_domain == blender::bke::AttrDomain::Edge) {
309 /* Blender's attribute API handles edge to vertex attribute domain interpolation. */
311 }
312
313 const blender::bke::GAttributeReader b_attr = iter.get(b_domain);
314 if (b_attr.varray.is_empty()) {
315 return;
316 }
317
319 {
320 Attribute *attr = attributes.add(name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE);
321 if (is_render_color) {
323 }
324
325 uchar4 *data = attr->data_uchar4();
327 if (subdivision) {
328 for (const int i : src.index_range()) {
329 data[i] = make_uchar4(src[i][0], src[i][1], src[i][2], src[i][3]);
330 }
331 }
332 else {
333 for (const int i : corner_tris.index_range()) {
334 const blender::int3 &tri = corner_tris[i];
335 data[i * 3 + 0] = make_uchar4(
336 src[tri[0]][0], src[tri[0]][1], src[tri[0]][2], src[tri[0]][3]);
337 data[i * 3 + 1] = make_uchar4(
338 src[tri[1]][0], src[tri[1]][1], src[tri[1]][2], src[tri[1]][3]);
339 data[i * 3 + 2] = make_uchar4(
340 src[tri[2]][0], src[tri[2]][1], src[tri[2]][2], src[tri[2]][3]);
341 }
342 }
343 return;
344 }
345
347 switch (b_domain) {
350 break;
353 break;
356 break;
357 default:
358 assert(false);
359 return;
360 }
361
363 using BlenderT = decltype(dummy);
364 using Converter = typename ccl::AttributeConverter<BlenderT>;
365 using CyclesT = typename Converter::CyclesT;
366 if constexpr (!std::is_void_v<CyclesT>) {
367 Attribute *attr = attributes.add(name, Converter::type_desc, element);
368 if (is_render_color) {
369 attr->std = ATTR_STD_VERTEX_COLOR;
370 }
371
372 CyclesT *data = reinterpret_cast<CyclesT *>(attr->data());
373
374 const blender::VArraySpan src = b_attr.varray.typed<BlenderT>();
375 switch (b_attr.domain) {
376 case blender::bke::AttrDomain::Corner: {
377 if (subdivision) {
378 for (const int i : src.index_range()) {
379 data[i] = Converter::convert(src[i]);
380 }
381 }
382 else {
383 for (const int i : corner_tris.index_range()) {
384 const blender::int3 &tri = corner_tris[i];
385 data[i * 3 + 0] = Converter::convert(src[tri[0]]);
386 data[i * 3 + 1] = Converter::convert(src[tri[1]]);
387 data[i * 3 + 2] = Converter::convert(src[tri[2]]);
388 }
389 }
390 break;
391 }
392 case blender::bke::AttrDomain::Point: {
393 for (const int i : src.index_range()) {
394 data[i] = Converter::convert(src[i]);
395 }
396 break;
397 }
398 case blender::bke::AttrDomain::Face: {
399 if (subdivision) {
400 for (const int i : src.index_range()) {
401 data[i] = Converter::convert(src[i]);
402 }
403 }
404 else {
405 for (const int i : corner_tris.index_range()) {
406 data[i] = Converter::convert(src[tri_faces[i]]);
407 }
408 }
409 break;
410 }
411 default: {
412 assert(false);
413 break;
414 }
415 }
416 }
417 });
418 });
419}
420
421static set<ustring> get_blender_uv_names(const ::Mesh &b_mesh)
422{
423 set<ustring> uv_names;
424 b_mesh.attributes().foreach_attribute([&](const blender::bke::AttributeIter &iter) {
427 uv_names.emplace(std::string_view(iter.name));
428 }
429 }
430 });
431 return uv_names;
432}
433
434/* Create uv map attributes. */
435static void attr_create_uv_map(Scene *scene,
436 Mesh *mesh,
437 const ::Mesh &b_mesh,
438 const set<ustring> &blender_uv_names)
439{
440 const blender::Span<blender::int3> corner_tris = b_mesh.corner_tris();
441 const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes();
442 const ustring render_name(CustomData_get_render_layer_name(&b_mesh.corner_data, CD_PROP_FLOAT2));
443 if (!blender_uv_names.empty()) {
444 for (const ustring &uv_name : blender_uv_names) {
445 const bool active_render = uv_name == render_name;
446 AttributeStandard uv_std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE;
447 AttributeStandard tangent_std = (active_render) ? ATTR_STD_UV_TANGENT : ATTR_STD_NONE;
448 ustring tangent_name = ustring((string(uv_name) + ".tangent").c_str());
449
450 /* Denotes whether UV map was requested directly. */
451 const bool need_uv = mesh->need_attribute(scene, uv_name) ||
452 mesh->need_attribute(scene, uv_std);
453 /* Denotes whether tangent was requested directly. */
454 const bool need_tangent = mesh->need_attribute(scene, tangent_name) ||
455 (active_render && mesh->need_attribute(scene, tangent_std));
456
457 /* UV map */
458 /* NOTE: We create temporary UV layer if its needed for tangent but
459 * wasn't requested by other nodes in shaders.
460 */
461 Attribute *uv_attr = NULL;
462 if (need_uv || need_tangent) {
463 if (active_render) {
464 uv_attr = mesh->attributes.add(uv_std, uv_name);
465 }
466 else {
467 uv_attr = mesh->attributes.add(uv_name, TypeFloat2, ATTR_ELEMENT_CORNER);
468 }
469
470 const blender::VArraySpan b_uv_map = *b_attributes.lookup<blender::float2>(
471 uv_name.c_str(), blender::bke::AttrDomain::Corner);
472 float2 *fdata = uv_attr->data_float2();
473 for (const int i : corner_tris.index_range()) {
474 const blender::int3 &tri = corner_tris[i];
475 fdata[i * 3 + 0] = make_float2(b_uv_map[tri[0]][0], b_uv_map[tri[0]][1]);
476 fdata[i * 3 + 1] = make_float2(b_uv_map[tri[1]][0], b_uv_map[tri[1]][1]);
477 fdata[i * 3 + 2] = make_float2(b_uv_map[tri[2]][0], b_uv_map[tri[2]][1]);
478 }
479 }
480
481 /* UV tangent */
482 if (need_tangent) {
483 AttributeStandard sign_std = (active_render) ? ATTR_STD_UV_TANGENT_SIGN : ATTR_STD_NONE;
484 ustring sign_name = ustring((string(uv_name) + ".tangent_sign").c_str());
485 bool need_sign = (mesh->need_attribute(scene, sign_name) ||
486 mesh->need_attribute(scene, sign_std));
487 mikk_compute_tangents(b_mesh, uv_name.c_str(), mesh, need_sign, active_render);
488 }
489 /* Remove temporarily created UV attribute. */
490 if (!need_uv && uv_attr != NULL) {
491 mesh->attributes.remove(uv_attr);
492 }
493 }
494 }
495 else if (mesh->need_attribute(scene, ATTR_STD_UV_TANGENT)) {
496 bool need_sign = mesh->need_attribute(scene, ATTR_STD_UV_TANGENT_SIGN);
497 mikk_compute_tangents(b_mesh, NULL, mesh, need_sign, true);
498 if (!mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
500 }
501 }
502}
503
505 Mesh *mesh,
506 const ::Mesh &b_mesh,
507 bool subdivide_uvs,
508 const set<ustring> &blender_uv_names)
509{
510 const blender::OffsetIndices faces = b_mesh.faces();
511 if (faces.is_empty()) {
512 return;
513 }
514
515 if (!blender_uv_names.empty()) {
516 const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes();
517 const ustring render_name(
519 for (const ustring &uv_name : blender_uv_names) {
520 const bool active_render = uv_name == render_name;
521 AttributeStandard uv_std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE;
522 AttributeStandard tangent_std = (active_render) ? ATTR_STD_UV_TANGENT : ATTR_STD_NONE;
523 ustring tangent_name = ustring((string(uv_name) + ".tangent").c_str());
524
525 /* Denotes whether UV map was requested directly. */
526 const bool need_uv = mesh->need_attribute(scene, uv_name) ||
527 mesh->need_attribute(scene, uv_std);
528 /* Denotes whether tangent was requested directly. */
529 const bool need_tangent = mesh->need_attribute(scene, tangent_name) ||
530 (active_render && mesh->need_attribute(scene, tangent_std));
531
532 Attribute *uv_attr = NULL;
533
534 /* UV map */
535 if (need_uv || need_tangent) {
536 if (active_render) {
537 uv_attr = mesh->subd_attributes.add(uv_std, uv_name);
538 }
539 else {
540 uv_attr = mesh->subd_attributes.add(uv_name, TypeFloat2, ATTR_ELEMENT_CORNER);
541 }
542
543 if (subdivide_uvs) {
544 uv_attr->flags |= ATTR_SUBDIVIDED;
545 }
546
547 const blender::VArraySpan b_uv_map = *b_attributes.lookup<blender::float2>(
548 uv_name.c_str(), blender::bke::AttrDomain::Corner);
549 float2 *fdata = uv_attr->data_float2();
550
551 for (const int i : faces.index_range()) {
552 const blender::IndexRange face = faces[i];
553 for (const int corner : face) {
554 *(fdata++) = make_float2(b_uv_map[corner][0], b_uv_map[corner][1]);
555 }
556 }
557 }
558
559 /* UV tangent */
560 if (need_tangent) {
561 AttributeStandard sign_std = (active_render) ? ATTR_STD_UV_TANGENT_SIGN : ATTR_STD_NONE;
562 ustring sign_name = ustring((string(uv_name) + ".tangent_sign").c_str());
563 bool need_sign = (mesh->need_attribute(scene, sign_name) ||
564 mesh->need_attribute(scene, sign_std));
565 mikk_compute_tangents(b_mesh, uv_name.c_str(), mesh, need_sign, active_render);
566 }
567 /* Remove temporarily created UV attribute. */
568 if (!need_uv && uv_attr != NULL) {
569 mesh->subd_attributes.remove(uv_attr);
570 }
571 }
572 }
573 else if (mesh->need_attribute(scene, ATTR_STD_UV_TANGENT)) {
574 bool need_sign = mesh->need_attribute(scene, ATTR_STD_UV_TANGENT_SIGN);
575 mikk_compute_tangents(b_mesh, NULL, mesh, need_sign, true);
576 if (!mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
578 }
579 }
580}
581
582/* Create vertex pointiness attributes. */
583
584/* Compare vertices by sum of their coordinates. */
586 public:
588
589 bool operator()(const int &vert_idx_a, const int &vert_idx_b)
590 {
591 const float3 &vert_a = verts_[vert_idx_a];
592 const float3 &vert_b = verts_[vert_idx_b];
593 if (vert_a == vert_b) {
594 /* Special case for doubles, so we ensure ordering. */
595 return vert_idx_a > vert_idx_b;
596 }
597 const float x1 = vert_a.x + vert_a.y + vert_a.z;
598 const float x2 = vert_b.x + vert_b.y + vert_b.z;
599 return x1 < x2;
600 }
601
602 protected:
604};
605
606static void attr_create_pointiness(Mesh *mesh,
607 const blender::Span<blender::float3> positions,
608 const blender::Span<blender::float3> b_vert_normals,
610 bool subdivision)
611{
612 const int num_verts = positions.size();
613 if (positions.is_empty()) {
614 return;
615 }
616
617 /* STEP 1: Find out duplicated vertices and point duplicates to a single
618 * original vertex.
619 */
620 vector<int> sorted_vert_indeices(num_verts);
621 for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
622 sorted_vert_indeices[vert_index] = vert_index;
623 }
624 VertexAverageComparator compare(mesh->get_verts());
625 sort(sorted_vert_indeices.begin(), sorted_vert_indeices.end(), compare);
626 /* This array stores index of the original vertex for the given vertex
627 * index.
628 */
629 vector<int> vert_orig_index(num_verts);
630 for (int sorted_vert_index = 0; sorted_vert_index < num_verts; ++sorted_vert_index) {
631 const int vert_index = sorted_vert_indeices[sorted_vert_index];
632 const float3 &vert_co = mesh->get_verts()[vert_index];
633 bool found = false;
634 for (int other_sorted_vert_index = sorted_vert_index + 1; other_sorted_vert_index < num_verts;
635 ++other_sorted_vert_index)
636 {
637 const int other_vert_index = sorted_vert_indeices[other_sorted_vert_index];
638 const float3 &other_vert_co = mesh->get_verts()[other_vert_index];
639 /* We are too far away now, we wouldn't have duplicate. */
640 if ((other_vert_co.x + other_vert_co.y + other_vert_co.z) -
641 (vert_co.x + vert_co.y + vert_co.z) >
642 3 * FLT_EPSILON)
643 {
644 break;
645 }
646 /* Found duplicate. */
647 if (len_squared(other_vert_co - vert_co) < FLT_EPSILON) {
648 found = true;
649 vert_orig_index[vert_index] = other_vert_index;
650 break;
651 }
652 }
653 if (!found) {
654 vert_orig_index[vert_index] = vert_index;
655 }
656 }
657 /* Make sure we always points to the very first orig vertex. */
658 for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
659 int orig_index = vert_orig_index[vert_index];
660 while (orig_index != vert_orig_index[orig_index]) {
661 orig_index = vert_orig_index[orig_index];
662 }
663 vert_orig_index[vert_index] = orig_index;
664 }
665 sorted_vert_indeices.free_memory();
666 /* STEP 2: Calculate vertex normals taking into account their possible
667 * duplicates which gets "welded" together.
668 */
669 vector<float3> vert_normal(num_verts, zero_float3());
670 /* First we accumulate all vertex normals in the original index. */
671 for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
672 const float *b_vert_normal = b_vert_normals[vert_index];
673 const int orig_index = vert_orig_index[vert_index];
674 vert_normal[orig_index] += make_float3(b_vert_normal[0], b_vert_normal[1], b_vert_normal[2]);
675 }
676 /* Then we normalize the accumulated result and flush it to all duplicates
677 * as well.
678 */
679 for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
680 const int orig_index = vert_orig_index[vert_index];
681 vert_normal[vert_index] = normalize(vert_normal[orig_index]);
682 }
683 /* STEP 3: Calculate pointiness using single ring neighborhood. */
684 vector<int> counter(num_verts, 0);
685 vector<float> raw_data(num_verts, 0.0f);
686 vector<float3> edge_accum(num_verts, zero_float3());
687 EdgeMap visited_edges;
688 memset(&counter[0], 0, sizeof(int) * counter.size());
689
690 for (const int i : edges.index_range()) {
691 const blender::int2 b_edge = edges[i];
692 const int v0 = vert_orig_index[b_edge[0]];
693 const int v1 = vert_orig_index[b_edge[1]];
694 if (visited_edges.exists(v0, v1)) {
695 continue;
696 }
697 visited_edges.insert(v0, v1);
698 float3 co0 = make_float3(positions[v0][0], positions[v0][1], positions[v0][2]);
699 float3 co1 = make_float3(positions[v1][0], positions[v1][1], positions[v1][2]);
700 float3 edge = normalize(co1 - co0);
701 edge_accum[v0] += edge;
702 edge_accum[v1] += -edge;
703 ++counter[v0];
704 ++counter[v1];
705 }
706 for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
707 const int orig_index = vert_orig_index[vert_index];
708 if (orig_index != vert_index) {
709 /* Skip duplicates, they'll be overwritten later on. */
710 continue;
711 }
712 if (counter[vert_index] > 0) {
713 const float3 normal = vert_normal[vert_index];
714 const float angle = safe_acosf(dot(normal, edge_accum[vert_index] / counter[vert_index]));
715 raw_data[vert_index] = angle * M_1_PI_F;
716 }
717 else {
718 raw_data[vert_index] = 0.0f;
719 }
720 }
721 /* STEP 3: Blur vertices to approximate 2 ring neighborhood. */
722 AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
723 Attribute *attr = attributes.add(ATTR_STD_POINTINESS);
724 float *data = attr->data_float();
725 memcpy(data, &raw_data[0], sizeof(float) * raw_data.size());
726 memset(&counter[0], 0, sizeof(int) * counter.size());
727 visited_edges.clear();
728 for (const int i : edges.index_range()) {
729 const blender::int2 b_edge = edges[i];
730 const int v0 = vert_orig_index[b_edge[0]];
731 const int v1 = vert_orig_index[b_edge[1]];
732 if (visited_edges.exists(v0, v1)) {
733 continue;
734 }
735 visited_edges.insert(v0, v1);
736 data[v0] += raw_data[v1];
737 data[v1] += raw_data[v0];
738 ++counter[v0];
739 ++counter[v1];
740 }
741 for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
742 data[vert_index] /= counter[vert_index] + 1;
743 }
744 /* STEP 4: Copy attribute to the duplicated vertices. */
745 for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
746 const int orig_index = vert_orig_index[vert_index];
747 data[vert_index] = data[orig_index];
748 }
749}
750
751/* The Random Per Island attribute is a random float associated with each
752 * connected component (island) of the mesh. The attribute is computed by
753 * first classifying the vertices into different sets using a Disjoint Set
754 * data structure. Then the index of the root of each vertex (Which is the
755 * representative of the set the vertex belongs to) is hashed and stored.
756 *
757 * We are using a face attribute to avoid interpolation during rendering,
758 * allowing the user to safely hash the output further. Had we used vertex
759 * attribute, the interpolation will introduce very slight variations,
760 * making the output unsafe to hash. */
762 Mesh *mesh,
763 const ::Mesh &b_mesh,
764 bool subdivision)
765{
766 if (!mesh->need_attribute(scene, ATTR_STD_RANDOM_PER_ISLAND)) {
767 return;
768 }
769
770 if (b_mesh.verts_num == 0) {
771 return;
772 }
773
774 DisjointSet vertices_sets(b_mesh.verts_num);
775
776 const blender::Span<blender::int2> edges = b_mesh.edges();
777 const blender::Span<int> corner_verts = b_mesh.corner_verts();
778
779 for (const int i : edges.index_range()) {
780 vertices_sets.join(edges[i][0], edges[i][1]);
781 }
782
783 AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
785 float *data = attribute->data_float();
786
787 if (!subdivision) {
788 const blender::Span<blender::int3> corner_tris = b_mesh.corner_tris();
789 if (!corner_tris.is_empty()) {
790 for (const int i : corner_tris.index_range()) {
791 const int vert = corner_verts[corner_tris[i][0]];
792 data[i] = hash_uint_to_float(vertices_sets.find(vert));
793 }
794 }
795 }
796 else {
797 const blender::OffsetIndices<int> faces = b_mesh.faces();
798 if (!faces.is_empty()) {
799 for (const int i : faces.index_range()) {
800 const int vert = corner_verts[faces[i].start()];
801 data[i] = hash_uint_to_float(vertices_sets.find(vert));
802 }
803 }
804 }
805}
806
807/* Create Mesh */
808
809static void create_mesh(Scene *scene,
810 Mesh *mesh,
811 const ::Mesh &b_mesh,
812 const array<Node *> &used_shaders,
813 const bool need_motion,
814 const float motion_scale,
815 const bool subdivision = false,
816 const bool subdivide_uvs = true)
817{
818 const blender::Span<blender::float3> positions = b_mesh.vert_positions();
819 const blender::OffsetIndices faces = b_mesh.faces();
820 const blender::Span<int> corner_verts = b_mesh.corner_verts();
821 const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes();
822 const blender::bke::MeshNormalDomain normals_domain = b_mesh.normals_domain(true);
823 int numfaces = (!subdivision) ? b_mesh.corner_tris().size() : faces.size();
824
825 bool use_corner_normals = normals_domain == blender::bke::MeshNormalDomain::Corner &&
826 (mesh->get_subdivision_type() != Mesh::SUBDIVISION_CATMULL_CLARK);
827
828 /* If no faces, create empty mesh. */
829 if (faces.is_empty()) {
830 return;
831 }
832
833 const blender::VArraySpan material_indices = *b_attributes.lookup<int>(
834 "material_index", blender::bke::AttrDomain::Face);
835 const blender::VArraySpan sharp_faces = *b_attributes.lookup<bool>(
836 "sharp_face", blender::bke::AttrDomain::Face);
837 blender::Span<blender::float3> corner_normals;
838 if (use_corner_normals) {
839 corner_normals = b_mesh.corner_normals();
840 }
841
842 int numngons = 0;
843 int numtris = 0;
844 if (!subdivision) {
845 numtris = numfaces;
846 }
847 else {
848 const blender::OffsetIndices faces = b_mesh.faces();
849 for (const int i : faces.index_range()) {
850 numngons += (faces[i].size() == 4) ? 0 : 1;
851 }
852 }
853
854 /* allocate memory */
855 if (subdivision) {
856 mesh->resize_subd_faces(numfaces, numngons, corner_verts.size());
857 }
858 mesh->resize_mesh(positions.size(), numtris);
859
860 float3 *verts = mesh->get_verts().data();
861 for (const int i : positions.index_range()) {
862 verts[i] = make_float3(positions[i][0], positions[i][1], positions[i][2]);
863 }
864
865 AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
866 Attribute *attr_N = attributes.add(ATTR_STD_VERTEX_NORMAL);
867 float3 *N = attr_N->data_float3();
868
869 if (subdivision || !(use_corner_normals && !corner_normals.is_empty())) {
870 const blender::Span<blender::float3> vert_normals = b_mesh.vert_normals();
871 for (const int i : vert_normals.index_range()) {
872 N[i] = make_float3(vert_normals[i][0], vert_normals[i][1], vert_normals[i][2]);
873 }
874 }
875
876 const set<ustring> blender_uv_names = get_blender_uv_names(b_mesh);
877
878 /* create generated coordinates from undeformed coordinates */
879 const bool need_default_tangent = (subdivision == false) && (blender_uv_names.empty()) &&
880 (mesh->need_attribute(scene, ATTR_STD_UV_TANGENT));
881 if (mesh->need_attribute(scene, ATTR_STD_GENERATED) || need_default_tangent) {
882 const float(*orco)[3] = static_cast<const float(*)[3]>(
883 CustomData_get_layer(&b_mesh.vert_data, CD_ORCO));
884 Attribute *attr = attributes.add(ATTR_STD_GENERATED);
885 attr->flags |= ATTR_SUBDIVIDED;
886
887 float3 loc, size;
888 mesh_texture_space(b_mesh, loc, size);
889
890 float texspace_location[3], texspace_size[3];
891 BKE_mesh_texspace_get(const_cast<::Mesh *>(b_mesh.texcomesh ? b_mesh.texcomesh : &b_mesh),
892 texspace_location,
893 texspace_size);
894
895 float3 *generated = attr->data_float3();
896
897 for (const int i : positions.index_range()) {
898 blender::float3 value;
899 if (orco) {
900 madd_v3_v3v3v3(value, texspace_location, orco[i], texspace_size);
901 }
902 else {
903 value = positions[i];
904 }
905 generated[i] = make_float3(value[0], value[1], value[2]) * size - loc;
906 }
907 }
908
909 auto clamp_material_index = [&](const int material_index) -> int {
910 return clamp(material_index, 0, used_shaders.size() - 1);
911 };
912
913 /* create faces */
914 if (!subdivision) {
915 int *triangles = mesh->get_triangles().data();
916 bool *smooth = mesh->get_smooth().data();
917 int *shader = mesh->get_shader().data();
918
919 const blender::Span<blender::int3> corner_tris = b_mesh.corner_tris();
920 for (const int i : corner_tris.index_range()) {
921 const blender::int3 &tri = corner_tris[i];
922 triangles[i * 3 + 0] = corner_verts[tri[0]];
923 triangles[i * 3 + 1] = corner_verts[tri[1]];
924 triangles[i * 3 + 2] = corner_verts[tri[2]];
925 }
926
927 if (!material_indices.is_empty()) {
928 const blender::Span<int> tri_faces = b_mesh.corner_tri_faces();
929 for (const int i : corner_tris.index_range()) {
930 shader[i] = clamp_material_index(material_indices[tri_faces[i]]);
931 }
932 }
933 else {
934 std::fill(shader, shader + numtris, 0);
935 }
936
937 if (!sharp_faces.is_empty() && !(use_corner_normals && !corner_normals.is_empty())) {
938 const blender::Span<int> tri_faces = b_mesh.corner_tri_faces();
939 for (const int i : corner_tris.index_range()) {
940 smooth[i] = !sharp_faces[tri_faces[i]];
941 }
942 }
943 else {
944 /* If only face normals are needed, all faces are sharp. */
945 std::fill(smooth, smooth + numtris, normals_domain != blender::bke::MeshNormalDomain::Face);
946 }
947
948 if (use_corner_normals && !corner_normals.is_empty()) {
949 for (const int i : corner_tris.index_range()) {
950 const blender::int3 &tri = corner_tris[i];
951 for (int i = 0; i < 3; i++) {
952 const int corner = tri[i];
953 const int vert = corner_verts[corner];
954 const float *normal = corner_normals[corner];
955 N[vert] = make_float3(normal[0], normal[1], normal[2]);
956 }
957 }
958 }
959
960 mesh->tag_triangles_modified();
961 mesh->tag_shader_modified();
962 mesh->tag_smooth_modified();
963 }
964 else {
965 int *subd_start_corner = mesh->get_subd_start_corner().data();
966 int *subd_num_corners = mesh->get_subd_num_corners().data();
967 int *subd_shader = mesh->get_subd_shader().data();
968 bool *subd_smooth = mesh->get_subd_smooth().data();
969 int *subd_ptex_offset = mesh->get_subd_ptex_offset().data();
970 int *subd_face_corners = mesh->get_subd_face_corners().data();
971
972 if (!sharp_faces.is_empty() && !use_corner_normals) {
973 for (int i = 0; i < numfaces; i++) {
974 subd_smooth[i] = !sharp_faces[i];
975 }
976 }
977 else {
978 std::fill(subd_smooth, subd_smooth + numfaces, true);
979 }
980
981 if (!material_indices.is_empty()) {
982 for (int i = 0; i < numfaces; i++) {
983 subd_shader[i] = clamp_material_index(material_indices[i]);
984 }
985 }
986 else {
987 std::fill(subd_shader, subd_shader + numfaces, 0);
988 }
989
990 std::copy(corner_verts.data(), corner_verts.data() + corner_verts.size(), subd_face_corners);
991
992 const blender::OffsetIndices faces = b_mesh.faces();
993 int ptex_offset = 0;
994 for (const int i : faces.index_range()) {
995 const blender::IndexRange face = faces[i];
996
997 subd_start_corner[i] = face.start();
998 subd_num_corners[i] = face.size();
999 subd_ptex_offset[i] = ptex_offset;
1000 const int num_ptex = (face.size() == 4) ? 1 : face.size();
1001 ptex_offset += num_ptex;
1002 }
1003
1004 mesh->tag_subd_face_corners_modified();
1005 mesh->tag_subd_start_corner_modified();
1006 mesh->tag_subd_num_corners_modified();
1007 mesh->tag_subd_shader_modified();
1008 mesh->tag_subd_smooth_modified();
1009 mesh->tag_subd_ptex_offset_modified();
1010 }
1011
1012 /* Create all needed attributes.
1013 * The calculate functions will check whether they're needed or not.
1014 */
1015 if (mesh->need_attribute(scene, ATTR_STD_POINTINESS)) {
1016 attr_create_pointiness(mesh, positions, b_mesh.vert_normals(), b_mesh.edges(), subdivision);
1017 }
1018 attr_create_random_per_island(scene, mesh, b_mesh, subdivision);
1019 attr_create_generic(scene, mesh, b_mesh, subdivision, need_motion, motion_scale);
1020
1021 if (subdivision) {
1022 attr_create_subd_uv_map(scene, mesh, b_mesh, subdivide_uvs, blender_uv_names);
1023 }
1024 else {
1025 attr_create_uv_map(scene, mesh, b_mesh, blender_uv_names);
1026 }
1027
1028 /* For volume objects, create a matrix to transform from object space to
1029 * mesh texture space. this does not work with deformations but that can
1030 * probably only be done well with a volume grid mapping of coordinates. */
1031 if (mesh->need_attribute(scene, ATTR_STD_GENERATED_TRANSFORM)) {
1033 Transform *tfm = attr->data_transform();
1034
1035 float3 loc, size;
1036 mesh_texture_space(b_mesh, loc, size);
1037
1039 }
1040}
1041
1042static void create_subd_mesh(Scene *scene,
1043 Mesh *mesh,
1044 BObjectInfo &b_ob_info,
1045 const ::Mesh &b_mesh,
1046 const array<Node *> &used_shaders,
1047 const bool need_motion,
1048 const float motion_scale,
1049 float dicing_rate,
1050 int max_subdivisions)
1051{
1052 BL::Object b_ob = b_ob_info.real_object;
1053
1054 BL::SubsurfModifier subsurf_mod(b_ob.modifiers[b_ob.modifiers.length() - 1]);
1055 bool subdivide_uvs = subsurf_mod.uv_smooth() != BL::SubsurfModifier::uv_smooth_NONE;
1056
1057 create_mesh(scene, mesh, b_mesh, used_shaders, need_motion, motion_scale, true, subdivide_uvs);
1058
1059 const blender::VArraySpan creases = *b_mesh.attributes().lookup<float>(
1060 "crease_edge", blender::bke::AttrDomain::Edge);
1061 if (!creases.is_empty()) {
1062 size_t num_creases = 0;
1063 for (const int i : creases.index_range()) {
1064 if (creases[i] != 0.0f) {
1065 num_creases++;
1066 }
1067 }
1068
1069 mesh->reserve_subd_creases(num_creases);
1070
1071 const blender::Span<blender::int2> edges = b_mesh.edges();
1072 for (const int i : edges.index_range()) {
1073 const float crease = creases[i];
1074 if (crease != 0.0f) {
1075 const blender::int2 &b_edge = edges[i];
1076 mesh->add_edge_crease(b_edge[0], b_edge[1], crease);
1077 }
1078 }
1079 }
1080
1081 const blender::VArraySpan vert_creases = *b_mesh.attributes().lookup<float>(
1082 "crease_vert", blender::bke::AttrDomain::Point);
1083 if (!vert_creases.is_empty()) {
1084 for (const int i : vert_creases.index_range()) {
1085 if (vert_creases[i] != 0.0f) {
1086 mesh->add_vertex_crease(i, vert_creases[i]);
1087 }
1088 }
1089 }
1090
1091 /* set subd params */
1092 PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles");
1093 float subd_dicing_rate = max(0.1f, RNA_float_get(&cobj, "dicing_rate") * dicing_rate);
1094
1095 mesh->set_subd_dicing_rate(subd_dicing_rate);
1096 mesh->set_subd_max_level(max_subdivisions);
1097 mesh->set_subd_objecttoworld(get_transform(b_ob.matrix_world()));
1098}
1099
1100/* Sync */
1101
1102void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Mesh *mesh)
1103{
1104 /* make a copy of the shaders as the caller in the main thread still need them for syncing the
1105 * attributes */
1106 array<Node *> used_shaders = mesh->get_used_shaders();
1107
1108 Mesh new_mesh;
1109 new_mesh.set_used_shaders(used_shaders);
1110
1111 if (view_layer.use_surfaces) {
1112 /* Adaptive subdivision setup. Not for baking since that requires
1113 * exact mapping to the Blender mesh. */
1114 if (!scene->bake_manager->get_baking()) {
1115 new_mesh.set_subdivision_type(
1116 object_subdivision_type(b_ob_info.real_object, preview, experimental));
1117 }
1118
1119 /* For some reason, meshes do not need this... */
1120 bool need_undeformed = new_mesh.need_attribute(scene, ATTR_STD_GENERATED);
1121 BL::Mesh b_mesh = object_to_mesh(
1122 b_data, b_ob_info, b_depsgraph, need_undeformed, new_mesh.get_subdivision_type());
1123
1124 if (b_mesh) {
1125 /* Motion blur attribute is relative to seconds, we need it relative to frames. */
1126 const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
1127 const float motion_scale = (need_motion) ?
1128 scene->motion_shutter_time() /
1129 (b_scene.render().fps() / b_scene.render().fps_base()) :
1130 0.0f;
1131
1132 /* Sync mesh itself. */
1133 if (new_mesh.get_subdivision_type() != Mesh::SUBDIVISION_NONE) {
1134 create_subd_mesh(scene,
1135 &new_mesh,
1136 b_ob_info,
1137 *static_cast<const ::Mesh *>(b_mesh.ptr.data),
1138 new_mesh.get_used_shaders(),
1139 need_motion,
1140 motion_scale,
1141 dicing_rate,
1142 max_subdivisions);
1143 }
1144 else {
1145 create_mesh(scene,
1146 &new_mesh,
1147 *static_cast<const ::Mesh *>(b_mesh.ptr.data),
1148 new_mesh.get_used_shaders(),
1149 need_motion,
1150 motion_scale,
1151 false);
1152 }
1153
1154 free_object_to_mesh(b_data, b_ob_info, b_mesh);
1155 }
1156 }
1157
1158 /* update original sockets */
1159
1160 mesh->clear_non_sockets();
1161
1162 for (const SocketType &socket : new_mesh.type->inputs) {
1163 /* Those sockets are updated in sync_object, so do not modify them. */
1164 if (socket.name == "use_motion_blur" || socket.name == "motion_steps" ||
1165 socket.name == "used_shaders")
1166 {
1167 continue;
1168 }
1169 mesh->set_value(socket, new_mesh, socket);
1170 }
1171
1172 mesh->attributes.update(std::move(new_mesh.attributes));
1173 mesh->subd_attributes.update(std::move(new_mesh.subd_attributes));
1174
1175 mesh->set_num_subd_faces(new_mesh.get_num_subd_faces());
1176
1177 /* tag update */
1178 bool rebuild = (mesh->triangles_is_modified()) || (mesh->subd_num_corners_is_modified()) ||
1179 (mesh->subd_shader_is_modified()) || (mesh->subd_smooth_is_modified()) ||
1180 (mesh->subd_ptex_offset_is_modified()) ||
1181 (mesh->subd_start_corner_is_modified()) ||
1182 (mesh->subd_face_corners_is_modified());
1183
1184 mesh->tag_update(scene, rebuild);
1185}
1186
1187void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
1188 BObjectInfo &b_ob_info,
1189 Mesh *mesh,
1190 int motion_step)
1191{
1192 /* Skip if no vertices were exported. */
1193 size_t numverts = mesh->get_verts().size();
1194 if (numverts == 0) {
1195 return;
1196 }
1197
1198 /* Skip objects without deforming modifiers. this is not totally reliable,
1199 * would need a more extensive check to see which objects are animated. */
1200 BL::Mesh b_mesh_rna(PointerRNA_NULL);
1201 if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
1202 /* get derived mesh */
1203 b_mesh_rna = object_to_mesh(b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
1204 }
1205
1206 const std::string ob_name = b_ob_info.real_object.name();
1207
1208 /* TODO(sergey): Perform preliminary check for number of vertices. */
1209 if (b_mesh_rna) {
1210 const ::Mesh &b_mesh = *static_cast<const ::Mesh *>(b_mesh_rna.ptr.data);
1211 const int b_verts_num = b_mesh.verts_num;
1212 const blender::Span<blender::float3> positions = b_mesh.vert_positions();
1213 if (positions.is_empty()) {
1214 free_object_to_mesh(b_data, b_ob_info, b_mesh_rna);
1215 return;
1216 }
1217
1218 /* Export deformed coordinates. */
1219 /* Find attributes. */
1223 bool new_attribute = false;
1224 /* Add new attributes if they don't exist already. */
1225 if (!attr_mP) {
1227 if (attr_N) {
1229 }
1230
1231 new_attribute = true;
1232 }
1233 /* Load vertex data from mesh. */
1234 float3 *mP = attr_mP->data_float3() + motion_step * numverts;
1235 float3 *mN = (attr_mN) ? attr_mN->data_float3() + motion_step * numverts : NULL;
1236
1237 /* NOTE: We don't copy more that existing amount of vertices to prevent
1238 * possible memory corruption.
1239 */
1240 for (int i = 0; i < std::min<size_t>(b_verts_num, numverts); i++) {
1241 mP[i] = make_float3(positions[i][0], positions[i][1], positions[i][2]);
1242 }
1243 if (mN) {
1244 const blender::Span<blender::float3> b_vert_normals = b_mesh.vert_normals();
1245 for (int i = 0; i < std::min<size_t>(b_verts_num, numverts); i++) {
1246 mN[i] = make_float3(b_vert_normals[i][0], b_vert_normals[i][1], b_vert_normals[i][2]);
1247 }
1248 }
1249 if (new_attribute) {
1250 /* In case of new attribute, we verify if there really was any motion. */
1251 if (b_verts_num != numverts ||
1252 memcmp(mP, &mesh->get_verts()[0], sizeof(float3) * numverts) == 0)
1253 {
1254 /* no motion, remove attributes again */
1255 if (b_verts_num != numverts) {
1256 VLOG_WARNING << "Topology differs, disabling motion blur for object " << ob_name;
1257 }
1258 else {
1259 VLOG_DEBUG << "No actual deformation motion for object " << ob_name;
1260 }
1262 if (attr_mN) {
1264 }
1265 }
1266 else if (motion_step > 0) {
1267 VLOG_DEBUG << "Filling deformation motion for object " << ob_name;
1268 /* motion, fill up previous steps that we might have skipped because
1269 * they had no motion, but we need them anyway now */
1270 float3 *P = &mesh->get_verts()[0];
1271 float3 *N = (attr_N) ? attr_N->data_float3() : NULL;
1272 for (int step = 0; step < motion_step; step++) {
1273 memcpy(attr_mP->data_float3() + step * numverts, P, sizeof(float3) * numverts);
1274 if (attr_mN) {
1275 memcpy(attr_mN->data_float3() + step * numverts, N, sizeof(float3) * numverts);
1276 }
1277 }
1278 }
1279 }
1280 else {
1281 if (b_verts_num != numverts) {
1282 VLOG_WARNING << "Topology differs, discarding motion blur for object " << ob_name
1283 << " at time " << motion_step;
1284 memcpy(mP, &mesh->get_verts()[0], sizeof(float3) * numverts);
1285 if (mN != NULL) {
1286 memcpy(mN, attr_N->data_float3(), sizeof(float3) * numverts);
1287 }
1288 }
1289 }
1290
1291 free_object_to_mesh(b_data, b_ob_info, b_mesh_rna);
1292 return;
1293 }
1294
1295 /* No deformation on this frame, copy coordinates if other frames did have it. */
1296 mesh->copy_center_to_motion_step(motion_step);
1297}
1298
const char * BKE_id_attributes_default_color_name(const struct ID *id)
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
const char * CustomData_get_render_layer_name(const CustomData *data, eCustomDataType type)
void BKE_mesh_texspace_get(Mesh *mesh, float r_texspace_location[3], float r_texspace_size[3])
MINLINE float safe_acosf(float a)
bool map_to_sphere(float *r_u, float *r_v, float x, float y, float z)
MINLINE void madd_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float c[3])
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT2
struct Mesh Mesh
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Attribute
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
constexpr PointerRNA PointerRNA_NULL
Definition RNA_types.hh:45
static void attr_create_generic(Scene *scene, Mesh *mesh, const ::Mesh &b_mesh, const bool subdivision, const bool need_motion, const float motion_scale)
static void attr_create_random_per_island(Scene *scene, Mesh *mesh, const ::Mesh &b_mesh, bool subdivision)
static void mikk_compute_tangents(const ::Mesh &b_mesh, const char *layer_name, Mesh *mesh, bool need_sign, bool active_render)
static void attr_create_subd_uv_map(Scene *scene, Mesh *mesh, const ::Mesh &b_mesh, bool subdivide_uvs, const set< ustring > &blender_uv_names)
static void create_subd_mesh(Scene *scene, Mesh *mesh, BObjectInfo &b_ob_info, const ::Mesh &b_mesh, const array< Node * > &used_shaders, const bool need_motion, const float motion_scale, float dicing_rate, int max_subdivisions)
static void create_mesh(Scene *scene, Mesh *mesh, const ::Mesh &b_mesh, const array< Node * > &used_shaders, const bool need_motion, const float motion_scale, const bool subdivision=false, const bool subdivide_uvs=true)
static void attr_create_motion_from_velocity(Mesh *mesh, const blender::Span< blender::float3 > b_attr, const float motion_scale)
static void attr_create_pointiness(Mesh *mesh, const blender::Span< blender::float3 > positions, const blender::Span< blender::float3 > b_vert_normals, const blender::Span< blender::int2 > edges, bool subdivision)
static void attr_create_uv_map(Scene *scene, Mesh *mesh, const ::Mesh &b_mesh, const set< ustring > &blender_uv_names)
static set< ustring > get_blender_uv_names(const ::Mesh &b_mesh)
ATTR_WARN_UNUSED_RESULT const void * element
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static DBVT_INLINE btDbvtNode * sort(btDbvtNode *n, btDbvtNode *&r)
Definition btDbvt.cpp:418
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
VecBase< float, 3 > float3
Attribute * add(ustring name, TypeDesc type, AttributeElement element)
void update(AttributeSet &&new_attributes)
Attribute * find(ustring name) const
void remove(ustring name)
uchar4 * data_uchar4()
float * data_float()
AttributeStandard std
float3 * data_float3()
Transform * data_transform()
float2 * data_float2()
void join(size_t x, size_t y)
size_t find(size_t x)
void insert(int v0, int v1)
bool exists(int v0, int v1)
void tag_update(Scene *scene, bool rebuild)
bool need_attribute(Scene *scene, AttributeStandard std)
AttributeSet attributes
VertexAverageComparator(const array< float3 > &verts)
bool operator()(const int &vert_idx_a, const int &vert_idx_b)
const array< float3 > & verts_
size_t size() const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeReader get() const
void free_memory()
additional_info("compositor_sum_squared_difference_float_shared") .push_constant(Type output_img float dot(value.rgb, luminance_coefficients)") .define("LOAD(value)"
static void mesh_texture_space(const ::Mesh &b_mesh, float3 &loc, float3 &size)
static void free_object_to_mesh(BL::BlendData &, BObjectInfo &b_ob_info, BL::Mesh &mesh)
static BL::Mesh object_to_mesh(BL::BlendData &, BObjectInfo &b_ob_info, BL::Depsgraph &, bool, Mesh::SubdivisionType subdivision_type)
static bool object_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene)
static Mesh::SubdivisionType object_subdivision_type(BL::Object &b_ob, bool preview, bool experimental)
static Transform get_transform(const BL::Array< float, 16 > &array)
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
ccl_device_forceinline uchar4 make_uchar4(const uchar x, const uchar y, const uchar z, const uchar w)
#define NULL
ccl_device_forceinline float2 make_float2(const float x, const float y)
draw_view in_light_buf[] float
smooth(Type::VEC3, "P") .flat(Type out_color storage_buf(0, Qualifier::READ, "Surfel", "surfels_buf[]") .push_constant(Type smooth(Type::VEC4, "interp_color")
static float verts[][3]
ccl_device_inline float hash_uint_to_float(uint kx)
Definition hash.h:136
AttributeStandard
@ ATTR_STD_GENERATED_TRANSFORM
@ ATTR_STD_UV
@ ATTR_STD_MOTION_VERTEX_NORMAL
@ ATTR_STD_VERTEX_NORMAL
@ ATTR_STD_UV_TANGENT
@ ATTR_STD_NONE
@ ATTR_STD_VERTEX_COLOR
@ ATTR_STD_MOTION_VERTEX_POSITION
@ ATTR_STD_UV_TANGENT_SIGN
@ ATTR_STD_POINTINESS
@ ATTR_STD_GENERATED
@ ATTR_STD_RANDOM_PER_ISLAND
@ ATTR_SUBDIVIDED
AttributeElement
@ ATTR_ELEMENT_NONE
@ ATTR_ELEMENT_CORNER_BYTE
@ ATTR_ELEMENT_CORNER
@ ATTR_ELEMENT_VERTEX
@ ATTR_ELEMENT_FACE
#define VLOG_WARNING
Definition log.h:70
#define VLOG_DEBUG
Definition log.h:81
ccl_device_inline float len_squared(const float2 a)
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:15
#define N
#define T
static char faces[256]
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
bool attribute_name_is_anonymous(const StringRef name)
T step(const T &edge, const T &value)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
Definition BLI_color.hh:338
static constexpr TypeDesc TypeRGBA(TypeDesc::FLOAT, TypeDesc::VEC4, TypeDesc::COLOR)
CCL_NAMESPACE_BEGIN static OIIO_NAMESPACE_USING constexpr TypeDesc TypeFloat2(TypeDesc::FLOAT, TypeDesc::VEC2)
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
BL::Object real_object
float3 normal(const Mesh *mesh) const
float3 compute_normal(const float3 *verts) const
size_t get_num_subd_faces() const
Definition scene/mesh.h:233
void add_edge_crease(int v0, int v1, float weight)
void reserve_subd_creases(size_t num_creases)
AttributeSet subd_attributes
Definition scene/mesh.h:159
void copy_center_to_motion_step(const int motion_step)
void resize_subd_faces(int numfaces, int num_ngons, int numcorners)
@ SUBDIVISION_NONE
Definition scene/mesh.h:121
@ SUBDIVISION_CATMULL_CLARK
Definition scene/mesh.h:123
void clear_non_sockets()
void set_num_subd_faces(size_t num_subd_faces_)
Definition scene/mesh.h:238
void add_vertex_crease(int v, float weight)
void resize_mesh(int numverts, int numfaces)
int verts_num
mikk::float3 GetPosition(const int face_num, const int vert_num)
int GetNumVerticesOfFace(const int face_num)
void SetTangentSpace(const int face_num, const int vert_num, mikk::float3 T, bool orientation)
MikkMeshWrapper(const ::Mesh &b_mesh, const char *layer_name, const Mesh *mesh, float3 *tangent, float *tangent_sign)
mikk::float3 GetNormal(const int face_num, const int vert_num)
mikk::float3 GetTexCoord(const int face_num, const int vert_num)
int CornerIndex(const int face_num, const int vert_num)
int VertexIndex(const int face_num, const int vert_num)
const Mesh * mesh
vector< SocketType, std::allocator< SocketType > > inputs
Definition node_type.h:125
const NodeType * type
Definition graph/node.h:178
void set_value(const SocketType &input, const Node &other, const SocketType &other_input)
ustring name
Definition node_type.h:79
float x
float y
float z
Definition sky_float3.h:27
float y
Definition sky_float3.h:27
float x
Definition sky_float3.h:27
ccl_device_inline Transform transform_translate(float3 t)
Definition transform.h:244
ccl_device_inline Transform transform_scale(float3 s)
Definition transform.h:254
float max
static Mesh * create_mesh(SnapObjectContext *sctx, const Object *ob_eval, eSnapEditType)
ccl_device_inline int clamp(int a, int mn, int mx)
Definition util/math.h:379