Blender V5.0
mesh_normals.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12
13#include <climits>
14
15#include "BLI_math_geom.h"
16#include "BLI_math_vector.h"
17
18#include "BLI_array_utils.hh"
19#include "BLI_bit_vector.hh"
21#include "BLI_linklist.h"
22#include "BLI_math_base.hh"
23#include "BLI_math_vector.hh"
24#include "BLI_memarena.h"
25#include "BLI_span.hh"
26#include "BLI_task.hh"
27#include "BLI_utildefines.h"
28
29#include "BKE_attribute.hh"
30#include "BKE_global.hh"
31#include "BKE_mesh.hh"
32#include "BKE_mesh_mapping.hh"
33
34// #define DEBUG_TIME
35
36#ifdef DEBUG_TIME
37# include "BLI_timeit.hh"
38#endif
39
40/* -------------------------------------------------------------------- */
45
46namespace blender::bke {
47
49{
50 mesh.runtime->vert_normals_true_cache.ensure(
51 [&](Vector<float3> &r_data) { r_data = vert_normals; });
52}
53
55{
56 mesh.runtime->vert_normals_true_cache.ensure(
57 [&](Vector<float3> &r_data) { r_data = std::move(vert_normals); });
58}
59
61{
62 if (auto *vector = std::get_if<Vector<float3>>(&this->data)) {
63 vector->resize(size);
64 }
65 else {
66 this->data = Vector<float3>(size);
67 }
68 return std::get<Vector<float3>>(this->data).as_mutable_span();
69}
70
72{
73 if (const auto *vector = std::get_if<Vector<float3>>(&this->data)) {
74 return vector->as_span();
75 }
76 return std::get<Span<float3>>(this->data);
77}
78
80{
81 if (data.is_span()) {
82 this->data = data.get_internal_span();
83 }
84 else {
85 data.materialize(this->ensure_vector_size(data.size()));
86 }
87}
88
90{
91 this->data = std::move(data);
92}
93
94} // namespace blender::bke
95
97{
98 return mesh->runtime->vert_normals_cache.is_dirty();
99}
100
102{
103 return mesh->runtime->face_normals_cache.is_dirty();
104}
105
107
108namespace blender::bke::mesh {
109
110/* -------------------------------------------------------------------- */
113
114/*
115 * COMPUTE POLY NORMAL
116 *
117 * Computes the normal of a planar
118 * face See Graphics Gems for
119 * computing newell normal.
120 */
121static float3 normal_calc_ngon(const Span<float3> vert_positions, const Span<int> face_verts)
122{
123 float3 normal(0);
124
125 /* Newell's Method */
126 const float *v_prev = vert_positions[face_verts.last()];
127 for (const int i : face_verts.index_range()) {
128 const float *v_curr = vert_positions[face_verts[i]];
129 add_newell_cross_v3_v3v3(normal, v_prev, v_curr);
130 v_prev = v_curr;
131 }
132
133 if (UNLIKELY(normalize_v3(normal) == 0.0f)) {
134 /* Other axis are already set to zero. */
135 normal[2] = 1.0f;
136 }
137
138 return normal;
139}
140
141float3 face_normal_calc(const Span<float3> vert_positions, const Span<int> face_verts)
142{
143 float3 normal;
144 if (face_verts.size() == 4) {
145 normal_quad_v3(normal,
146 vert_positions[face_verts[0]],
147 vert_positions[face_verts[1]],
148 vert_positions[face_verts[2]],
149 vert_positions[face_verts[3]]);
150 }
151 else if (face_verts.size() == 3) {
152 normal = math::normal_tri(vert_positions[face_verts[0]],
153 vert_positions[face_verts[1]],
154 vert_positions[face_verts[2]]);
155 }
156 else {
157 BLI_assert(face_verts.size() > 4);
158 normal = normal_calc_ngon(vert_positions, face_verts);
159 }
160
161 if (UNLIKELY(math::is_zero(normal))) {
162 normal.z = 1.0f;
163 }
164
165 BLI_ASSERT_UNIT_V3(normal);
166 return normal;
167}
168
170
171/* -------------------------------------------------------------------- */
177
178void normals_calc_faces(const Span<float3> positions,
180 const Span<int> corner_verts,
181 MutableSpan<float3> face_normals)
182{
183 BLI_assert(faces.size() == face_normals.size());
184 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
185 for (const int i : range) {
186 face_normals[i] = normal_calc_ngon(positions, corner_verts.slice(faces[i]));
187 }
188 });
189}
190
191void normals_calc_verts(const Span<float3> vert_positions,
193 const Span<int> corner_verts,
194 const GroupedSpan<int> vert_to_face_map,
195 const Span<float3> face_normals,
196 MutableSpan<float3> vert_normals)
197{
198 const Span<float3> positions = vert_positions;
199 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
200 for (const int vert : range) {
201 const Span<int> vert_faces = vert_to_face_map[vert];
202 if (vert_faces.is_empty()) {
203 vert_normals[vert] = math::normalize(positions[vert]);
204 continue;
205 }
206
207 float3 vert_normal(0);
208 for (const int face : vert_faces) {
209 const int2 adjacent_verts = face_find_adjacent_verts(faces[face], corner_verts, vert);
210 const float3 dir_prev = math::normalize(positions[adjacent_verts[0]] - positions[vert]);
211 const float3 dir_next = math::normalize(positions[adjacent_verts[1]] - positions[vert]);
212 const float factor = math::safe_acos_approx(math::dot(dir_prev, dir_next));
213
214 vert_normal += face_normals[face] * factor;
215 }
216
217 vert_normals[vert] = math::normalize(vert_normal);
218 }
219 });
220}
221
223
224static void mix_normals_corner_to_vert(const Span<float3> vert_positions,
226 const Span<int> corner_verts,
227 const GroupedSpan<int> vert_to_face_map,
228 const Span<float3> corner_normals,
229 MutableSpan<float3> vert_normals)
230{
231 const Span<float3> positions = vert_positions;
232 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
233 for (const int vert : range) {
234 const Span<int> vert_faces = vert_to_face_map[vert];
235 if (vert_faces.is_empty()) {
236 vert_normals[vert] = math::normalize(positions[vert]);
237 continue;
238 }
239
240 float3 vert_normal(0);
241 for (const int face : vert_faces) {
242 const int corner = mesh::face_find_corner_from_vert(faces[face], corner_verts, vert);
243 const int2 adjacent_verts{corner_verts[mesh::face_corner_prev(faces[face], corner)],
244 corner_verts[mesh::face_corner_next(faces[face], corner)]};
245
246 const float3 dir_prev = math::normalize(positions[adjacent_verts[0]] - positions[vert]);
247 const float3 dir_next = math::normalize(positions[adjacent_verts[1]] - positions[vert]);
248 const float factor = math::safe_acos_approx(math::dot(dir_prev, dir_next));
249
250 vert_normal += corner_normals[corner] * factor;
251 }
252
253 vert_normals[vert] = math::normalize(vert_normal);
254 }
255 });
256}
257
259 const Span<int> corner_verts,
260 const Span<float3> vert_normals,
261 MutableSpan<float3> face_normals)
262{
263 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
264 for (const int face : range) {
265 float3 sum(0);
266 for (const int vert : corner_verts.slice(faces[face])) {
267 sum += vert_normals[vert];
268 }
269 face_normals[face] = math::normalize(sum);
270 }
271 });
272}
273
275 const Span<float3> corner_normals,
276 MutableSpan<float3> face_normals)
277{
278 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
279 for (const int face : range) {
280 const Span<float3> face_corner_normals = corner_normals.slice(faces[face]);
281 const float3 sum = std::accumulate(
282 face_corner_normals.begin(), face_corner_normals.end(), float3(0));
283 face_normals[face] = math::normalize(sum);
284 }
285 });
286}
287
288} // namespace blender::bke::mesh
289
290/* -------------------------------------------------------------------- */
293
294blender::bke::MeshNormalDomain Mesh::normals_domain(const bool support_sharp_face) const
295{
296 using namespace blender;
297 using namespace blender::bke;
298 if (this->faces_num == 0) {
300 }
301
302 const bke::AttributeAccessor attributes = this->attributes();
303 if (const std::optional<AttributeMetaData> custom = attributes.lookup_meta_data("custom_normal"))
304 {
305 switch (custom->domain) {
306 case AttrDomain::Point:
307 return MeshNormalDomain::Point;
308 case AttrDomain::Edge:
309 break;
310 case AttrDomain::Face:
311 return MeshNormalDomain::Face;
312 case AttrDomain::Corner:
313 return MeshNormalDomain::Corner;
314 default:
316 }
317 }
318
319 const VArray<bool> sharp_faces = *attributes.lookup_or_default<bool>(
320 "sharp_face", AttrDomain::Face, false);
321
322 const array_utils::BooleanMix face_mix = array_utils::booleans_mix_calc(sharp_faces);
323 if (face_mix == array_utils::BooleanMix::AllTrue) {
324 return MeshNormalDomain::Face;
325 }
326
327 const VArray<bool> sharp_edges = *attributes.lookup_or_default<bool>(
328 "sharp_edge", AttrDomain::Edge, false);
329 const array_utils::BooleanMix edge_mix = array_utils::booleans_mix_calc(sharp_edges);
330 if (edge_mix == array_utils::BooleanMix::AllTrue) {
331 return MeshNormalDomain::Face;
332 }
333
334 if (edge_mix == array_utils::BooleanMix::AllFalse &&
335 (face_mix == array_utils::BooleanMix::AllFalse || support_sharp_face))
336 {
337 return MeshNormalDomain::Point;
338 }
339
340 return MeshNormalDomain::Corner;
341}
342
343blender::Span<blender::float3> Mesh::vert_normals() const
344{
345 using namespace blender;
346 using namespace blender::bke;
347 this->runtime->vert_normals_cache.ensure([&](NormalsCache &r_data) {
348 if (const GAttributeReader custom = this->attributes().lookup("custom_normal")) {
349 if (custom.varray.type().is<float3>()) {
350 if (custom.domain == AttrDomain::Point) {
351 r_data.store_varray(custom.varray.typed<float3>());
352 return;
353 }
354 if (custom.domain == AttrDomain::Face) {
355 mesh::normals_calc_verts(this->vert_positions(),
356 this->faces(),
357 this->corner_verts(),
358 this->vert_to_face_map(),
359 VArraySpan<float3>(custom.varray.typed<float3>()),
360 r_data.ensure_vector_size(this->verts_num));
361
362 return;
363 }
364 if (custom.domain == AttrDomain::Corner) {
365 mesh::mix_normals_corner_to_vert(this->vert_positions(),
366 this->faces(),
367 this->corner_verts(),
368 this->vert_to_face_map(),
369 VArraySpan<float3>(custom.varray.typed<float3>()),
370 r_data.ensure_vector_size(this->verts_num));
371 return;
372 }
373 }
374 else if (custom.varray.type().is<short2>() && custom.domain == AttrDomain::Corner) {
375 mesh::mix_normals_corner_to_vert(this->vert_positions(),
376 this->faces(),
377 this->corner_verts(),
378 this->vert_to_face_map(),
379 this->corner_normals(),
380 r_data.ensure_vector_size(this->verts_num));
381 return;
382 }
383 }
384 r_data.data = NormalsCache::UseTrueCache();
385 });
386 if (std::holds_alternative<NormalsCache::UseTrueCache>(
387 this->runtime->vert_normals_cache.data().data))
388 {
389 return this->vert_normals_true();
390 }
391
392 return this->runtime->vert_normals_cache.data().get_span();
393}
394
395blender::Span<blender::float3> Mesh::vert_normals_true() const
396{
397 using namespace blender;
398 using namespace blender::bke;
399 this->runtime->vert_normals_true_cache.ensure([&](Vector<float3> &r_data) {
400 r_data.reinitialize(this->verts_num);
401 mesh::normals_calc_verts(this->vert_positions(),
402 this->faces(),
403 this->corner_verts(),
404 this->vert_to_face_map(),
405 this->face_normals_true(),
406 r_data);
407 });
408 return this->runtime->vert_normals_true_cache.data();
409}
410
411blender::Span<blender::float3> Mesh::face_normals() const
412{
413 using namespace blender;
414 using namespace blender::bke;
415 if (this->faces_num == 0) {
416 return {};
417 }
418 this->runtime->face_normals_cache.ensure([&](NormalsCache &r_data) {
419 if (const GAttributeReader custom = this->attributes().lookup("custom_normal")) {
420 if (custom.varray.type().is<float3>()) {
421 if (custom.domain == AttrDomain::Face) {
422 r_data.store_varray(custom.varray.typed<float3>());
423 return;
424 }
425 if (custom.domain == AttrDomain::Point) {
426 mesh::mix_normals_vert_to_face(this->faces(),
427 this->corner_verts(),
428 VArraySpan<float3>(custom.varray.typed<float3>()),
429 r_data.ensure_vector_size(this->faces_num));
430 return;
431 }
432 if (custom.domain == AttrDomain::Corner) {
433 mesh::mix_normals_corner_to_face(this->faces(),
434 VArraySpan<float3>(custom.varray.typed<float3>()),
435 r_data.ensure_vector_size(this->faces_num));
436 return;
437 }
438 }
439 else if (custom.varray.type().is<short2>() && custom.domain == AttrDomain::Corner) {
440 mesh::mix_normals_corner_to_face(
441 this->faces(), this->corner_normals(), r_data.ensure_vector_size(this->faces_num));
442 return;
443 }
444 }
445 r_data.data = NormalsCache::UseTrueCache();
446 });
447 if (std::holds_alternative<NormalsCache::UseTrueCache>(
448 this->runtime->face_normals_cache.data().data))
449 {
450 return this->face_normals_true();
451 }
452 return this->runtime->face_normals_cache.data().get_span();
453}
454
455blender::Span<blender::float3> Mesh::face_normals_true() const
456{
457 using namespace blender;
458 using namespace blender::bke;
459 this->runtime->face_normals_true_cache.ensure([&](Vector<float3> &r_data) {
460 r_data.reinitialize(this->faces_num);
461 mesh::normals_calc_faces(this->vert_positions(), this->faces(), this->corner_verts(), r_data);
462 });
463 return this->runtime->face_normals_true_cache.data();
464}
465
466blender::Span<blender::float3> Mesh::corner_normals() const
467{
468 using namespace blender;
469 using namespace blender::bke;
470 if (this->faces_num == 0) {
471 return {};
472 }
473 this->runtime->corner_normals_cache.ensure([&](NormalsCache &r_data) {
474 const OffsetIndices<int> faces = this->faces();
475 switch (this->normals_domain()) {
476 case MeshNormalDomain::Point: {
477 MutableSpan<float3> data = r_data.ensure_vector_size(this->corners_num);
478 array_utils::gather(this->vert_normals(), this->corner_verts(), data);
479 break;
480 }
481 case MeshNormalDomain::Face: {
482 MutableSpan<float3> data = r_data.ensure_vector_size(this->corners_num);
483 const Span<float3> face_normals = this->face_normals();
484 array_utils::gather_to_groups(faces, faces.index_range(), face_normals, data);
485 break;
486 }
487 case MeshNormalDomain::Corner: {
488 const AttributeAccessor attributes = this->attributes();
489 const GAttributeReader custom = attributes.lookup("custom_normal");
490 if (custom && custom.varray.type().is<float3>()) {
491 if (custom.domain == bke::AttrDomain::Corner) {
492 r_data.store_varray(custom.varray.typed<float3>());
493 }
494 return;
495 }
496 MutableSpan<float3> data = r_data.ensure_vector_size(this->corners_num);
497 const VArraySpan sharp_edges = *attributes.lookup<bool>("sharp_edge", AttrDomain::Edge);
498 const VArraySpan sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
499 mesh::normals_calc_corners(this->vert_positions(),
500 this->faces(),
501 this->corner_verts(),
502 this->corner_edges(),
503 this->vert_to_face_map(),
504 this->face_normals_true(),
505 sharp_edges,
506 sharp_faces,
508 nullptr,
509 data);
510 }
511 }
512 });
513 return this->runtime->corner_normals_cache.data().get_span();
514}
515
517 const int numLoops,
518 const char data_type)
519{
520 if (!(lnors_spacearr->lspacearr && lnors_spacearr->loops_pool)) {
521 MemArena *mem;
522
523 if (!lnors_spacearr->mem) {
524 lnors_spacearr->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
525 }
526 mem = lnors_spacearr->mem;
527 if (numLoops > 0) {
528 lnors_spacearr->lspacearr = (MLoopNorSpace **)BLI_memarena_calloc(
529 mem, sizeof(MLoopNorSpace *) * size_t(numLoops));
530 lnors_spacearr->loops_pool = (LinkNode *)BLI_memarena_alloc(
531 mem, sizeof(LinkNode) * size_t(numLoops));
532 }
533 else {
534 lnors_spacearr->lspacearr = nullptr;
535 lnors_spacearr->loops_pool = nullptr;
536 }
537
538 lnors_spacearr->spaces_num = 0;
539 }
541 lnors_spacearr->data_type = data_type;
542}
543
545 MLoopNorSpaceArray *lnors_spacearr_tls)
546{
547 *lnors_spacearr_tls = *lnors_spacearr;
548 lnors_spacearr_tls->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
549}
550
552 MLoopNorSpaceArray *lnors_spacearr_tls)
553{
554 BLI_assert(lnors_spacearr->data_type == lnors_spacearr_tls->data_type);
555 BLI_assert(lnors_spacearr->mem != lnors_spacearr_tls->mem);
556 lnors_spacearr->spaces_num += lnors_spacearr_tls->spaces_num;
557 BLI_memarena_merge(lnors_spacearr->mem, lnors_spacearr_tls->mem);
558 BLI_memarena_free(lnors_spacearr_tls->mem);
559 lnors_spacearr_tls->mem = nullptr;
560 BKE_lnor_spacearr_clear(lnors_spacearr_tls);
561}
562
564{
565 lnors_spacearr->spaces_num = 0;
566 lnors_spacearr->lspacearr = nullptr;
567 lnors_spacearr->loops_pool = nullptr;
568 if (lnors_spacearr->mem != nullptr) {
569 BLI_memarena_clear(lnors_spacearr->mem);
570 }
571}
572
574{
575 lnors_spacearr->spaces_num = 0;
576 lnors_spacearr->lspacearr = nullptr;
577 lnors_spacearr->loops_pool = nullptr;
578 BLI_memarena_free(lnors_spacearr->mem);
579 lnors_spacearr->mem = nullptr;
580}
581
583{
584 lnors_spacearr->spaces_num++;
585 return (MLoopNorSpace *)BLI_memarena_calloc(lnors_spacearr->mem, sizeof(MLoopNorSpace));
586}
587
588/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
589#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
590
591namespace blender::bke::mesh {
592
594 const float3 &vec_ref,
595 const float3 &vec_other,
596 const Span<float3> edge_vectors)
597{
598 CornerNormalSpace lnor_space{};
599 const float pi2 = float(M_PI) * 2.0f;
600 const float dtp_ref = math::dot(vec_ref, lnor);
601 const float dtp_other = math::dot(vec_other, lnor);
602
603 if (UNLIKELY(std::abs(dtp_ref) >= LNOR_SPACE_TRIGO_THRESHOLD ||
604 std::abs(dtp_other) >= LNOR_SPACE_TRIGO_THRESHOLD))
605 {
606 /* If vec_ref or vec_other are too much aligned with lnor, we can't build lnor space,
607 * tag it as invalid and abort. */
608 lnor_space.ref_alpha = lnor_space.ref_beta = 0.0f;
609 return lnor_space;
610 }
611
612 lnor_space.vec_lnor = lnor;
613
614 /* Compute ref alpha, average angle of all available edge vectors to lnor. */
615 if (!edge_vectors.is_empty()) {
616 float alpha = 0.0f;
617 for (const float3 &vec : edge_vectors) {
618 alpha += math::safe_acos_approx(math::dot(vec, lnor));
619 }
620 /* This piece of code shall only be called for more than one loop. */
621 /* NOTE: In theory, this could be `count > 2`,
622 * but there is one case where we only have two edges for two loops:
623 * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.).
624 */
625 BLI_assert(edge_vectors.size() >= 2);
626 lnor_space.ref_alpha = alpha / float(edge_vectors.size());
627 }
628 else {
629 lnor_space.ref_alpha = (math::safe_acos_approx(math::dot(vec_ref, lnor)) +
630 math::safe_acos_approx(math::dot(vec_other, lnor))) /
631 2.0f;
632 }
633
634 /* Project vec_ref on lnor's ortho plane. */
635 lnor_space.vec_ref = math::normalize(vec_ref - lnor * dtp_ref);
636 lnor_space.vec_ortho = math::normalize(math::cross(lnor, lnor_space.vec_ref));
637
638 /* Project vec_other on lnor's ortho plane. */
639 const float3 vec_other_proj = math::normalize(vec_other - lnor * dtp_other);
640
641 /* Beta is angle between ref_vec and other_vec, around lnor. */
642 const float dtp = math::dot(lnor_space.vec_ref, vec_other_proj);
644 const float beta = math::safe_acos_approx(dtp);
645 lnor_space.ref_beta = (math::dot(lnor_space.vec_ortho, vec_other_proj) < 0.0f) ? pi2 - beta :
646 beta;
647 }
648 else {
649 lnor_space.ref_beta = pi2;
650 }
651
652 return lnor_space;
653}
654
655} // namespace blender::bke::mesh
656
658 const float lnor[3],
659 const float vec_ref[3],
660 const float vec_other[3],
661 const blender::Span<blender::float3> edge_vectors)
662{
663 using namespace blender::bke::mesh;
664 const CornerNormalSpace space = corner_fan_space_define(lnor, vec_ref, vec_other, edge_vectors);
665 copy_v3_v3(lnor_space->vec_lnor, space.vec_lnor);
666 copy_v3_v3(lnor_space->vec_ref, space.vec_ref);
667 copy_v3_v3(lnor_space->vec_ortho, space.vec_ortho);
668 lnor_space->ref_alpha = space.ref_alpha;
669 lnor_space->ref_beta = space.ref_beta;
670}
671
673 MLoopNorSpace *lnor_space,
674 const int corner,
675 void *bm_loop,
676 const bool is_single)
677{
678 BLI_assert((lnors_spacearr->data_type == MLNOR_SPACEARR_LOOP_INDEX && bm_loop == nullptr) ||
679 (lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR && bm_loop != nullptr));
680
681 lnors_spacearr->lspacearr[corner] = lnor_space;
682 if (bm_loop == nullptr) {
683 bm_loop = POINTER_FROM_INT(corner);
684 }
685 if (is_single) {
686 BLI_assert(lnor_space->loops == nullptr);
687 lnor_space->flags |= MLNOR_SPACE_IS_SINGLE;
688 lnor_space->loops = (LinkNode *)bm_loop;
689 }
690 else {
691 BLI_assert((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0);
692 BLI_linklist_prepend_nlink(&lnor_space->loops, bm_loop, &lnors_spacearr->loops_pool[corner]);
693 }
694}
695
696MINLINE float unit_short_to_float(const short val)
697{
698 return float(val) / float(SHRT_MAX);
699}
700
701MINLINE short unit_float_to_short(const float val)
702{
703 /* Rounding. */
704 return short(floorf(val * float(SHRT_MAX) + 0.5f));
705}
706
707namespace blender::bke::mesh {
708
710 const short2 clnor_data)
711{
712 /* NOP custom normal data or invalid lnor space, return. */
713 if (clnor_data[0] == 0 || lnor_space.ref_alpha == 0.0f || lnor_space.ref_beta == 0.0f) {
714 return lnor_space.vec_lnor;
715 }
716
717 float3 custom_lnor;
718
719 /* TODO: Check whether using #sincosf() gives any noticeable benefit
720 * (could not even get it working under linux though)! */
721 const float pi2 = float(M_PI * 2.0);
722 const float alphafac = unit_short_to_float(clnor_data[0]);
723 const float alpha = (alphafac > 0.0f ? lnor_space.ref_alpha : pi2 - lnor_space.ref_alpha) *
724 alphafac;
725 const float betafac = unit_short_to_float(clnor_data[1]);
726
727 mul_v3_v3fl(custom_lnor, lnor_space.vec_lnor, cosf(alpha));
728
729 if (betafac == 0.0f) {
730 madd_v3_v3fl(custom_lnor, lnor_space.vec_ref, sinf(alpha));
731 }
732 else {
733 const float sinalpha = sinf(alpha);
734 const float beta = (betafac > 0.0f ? lnor_space.ref_beta : pi2 - lnor_space.ref_beta) *
735 betafac;
736 madd_v3_v3fl(custom_lnor, lnor_space.vec_ref, sinalpha * cosf(beta));
737 madd_v3_v3fl(custom_lnor, lnor_space.vec_ortho, sinalpha * sinf(beta));
738 }
739
740 return custom_lnor;
741}
742
743} // namespace blender::bke::mesh
744
746 const short clnor_data[2],
747 float r_custom_lnor[3])
748{
749 using namespace blender::bke::mesh;
750 CornerNormalSpace space;
751 space.vec_lnor = lnor_space->vec_lnor;
752 space.vec_ref = lnor_space->vec_ref;
753 space.vec_ortho = lnor_space->vec_ortho;
754 space.ref_alpha = lnor_space->ref_alpha;
755 space.ref_beta = lnor_space->ref_beta;
756 copy_v3_v3(r_custom_lnor, corner_space_custom_data_to_normal(space, clnor_data));
757}
758
759namespace blender::bke::mesh {
760
762 const float3 &custom_lnor)
763{
764 /* We use zero vector as NOP custom normal (can be simpler than giving auto-computed `lnor`). */
765 if (is_zero_v3(custom_lnor) || compare_v3v3(lnor_space.vec_lnor, custom_lnor, 1e-4f)) {
766 return short2(0);
767 }
768
769 short2 clnor_data;
770
771 const float pi2 = float(M_PI * 2.0);
772 const float cos_alpha = math::dot(lnor_space.vec_lnor, custom_lnor);
773
774 const float alpha = math::safe_acos_approx(cos_alpha);
775 if (alpha > lnor_space.ref_alpha) {
776 /* Note we could stick to [0, pi] range here,
777 * but makes decoding more complex, not worth it. */
778 clnor_data[0] = unit_float_to_short(-(pi2 - alpha) / (pi2 - lnor_space.ref_alpha));
779 }
780 else {
781 clnor_data[0] = unit_float_to_short(alpha / lnor_space.ref_alpha);
782 }
783
784 /* Project custom lnor on (vec_ref, vec_ortho) plane. */
785 const float3 vec = math::normalize(lnor_space.vec_lnor * -cos_alpha + custom_lnor);
786
787 const float cos_beta = math::dot(lnor_space.vec_ref, vec);
788
789 if (cos_beta < LNOR_SPACE_TRIGO_THRESHOLD) {
790 float beta = math::safe_acos_approx(cos_beta);
791 if (math::dot(lnor_space.vec_ortho, vec) < 0.0f) {
792 beta = pi2 - beta;
793 }
794
795 if (beta > lnor_space.ref_beta) {
796 clnor_data[1] = unit_float_to_short(-(pi2 - beta) / (pi2 - lnor_space.ref_beta));
797 }
798 else {
799 clnor_data[1] = unit_float_to_short(beta / lnor_space.ref_beta);
800 }
801 }
802 else {
803 clnor_data[1] = 0;
804 }
805
806 return clnor_data;
807}
808
809} // namespace blender::bke::mesh
810
812 const float custom_lnor[3],
813 short r_clnor_data[2])
814{
815 using namespace blender::bke::mesh;
816 CornerNormalSpace space;
817 space.vec_lnor = lnor_space->vec_lnor;
818 space.vec_ref = lnor_space->vec_ref;
819 space.vec_ortho = lnor_space->vec_ortho;
820 space.ref_alpha = lnor_space->ref_alpha;
821 space.ref_beta = lnor_space->ref_beta;
822 copy_v2_v2_short(r_clnor_data, corner_space_custom_normal_to_data(space, custom_lnor));
823}
824
825namespace blender::bke {
826
827namespace mesh {
828
829#define INDEX_UNSET INT_MIN
830#define INDEX_INVALID -1
831/* See comment about edge_to_corners below. */
832#define IS_EDGE_SHARP(_e2l) ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID)
833
835 const Span<int> corner_verts,
836 const Span<int> corner_edges,
837 const Span<int> corner_to_face_map,
838 const Span<float3> face_normals,
839 const Span<bool> sharp_faces,
840 const Span<bool> sharp_edges,
841 const float split_angle,
842 MutableSpan<int2> edge_to_corners,
843 MutableSpan<bool> r_sharp_edges)
844{
845 const float split_angle_cos = cosf(split_angle);
846 auto face_is_smooth = [&](const int face_i) {
847 return sharp_faces.is_empty() || !sharp_faces[face_i];
848 };
849
850 for (const int face_i : faces.index_range()) {
851 for (const int corner : faces[face_i]) {
852 const int vert = corner_verts[corner];
853 const int edge = corner_edges[corner];
854
855 int2 &e2l = edge_to_corners[edge];
856
857 /* Check whether current edge might be smooth or sharp */
858 if ((e2l[0] | e2l[1]) == 0) {
859 /* 'Empty' edge until now, set e2l[0] (and e2l[1] to INDEX_UNSET to tag it as unset). */
860 e2l[0] = corner;
861 /* We have to check this here too, else we might miss some flat faces!!! */
862 e2l[1] = face_is_smooth(face_i) ? INDEX_UNSET : INDEX_INVALID;
863 }
864 else if (e2l[1] == INDEX_UNSET) {
865 const bool is_angle_sharp = math::dot(face_normals[corner_to_face_map[e2l[0]]],
866 face_normals[face_i]) < split_angle_cos;
867
868 /* Second corner using this edge, time to test its sharpness.
869 * An edge is sharp if it is tagged as such, or its face is not smooth,
870 * or both faces have opposed (flipped) normals, i.e. both corners on the same edge share
871 * the same vertex, or angle between both its faces' normals is above split_angle value. */
872 if (!face_is_smooth(face_i) || (!sharp_edges.is_empty() && sharp_edges[edge]) ||
873 vert == corner_verts[e2l[0]] || is_angle_sharp)
874 {
875 /* NOTE: we are sure that corner != 0 here ;). */
876 e2l[1] = INDEX_INVALID;
877
878 /* We want to avoid tagging edges as sharp when it is already defined as such by
879 * other causes than angle threshold. */
880 if (is_angle_sharp) {
881 r_sharp_edges[edge] = true;
882 }
883 }
884 else {
885 e2l[1] = corner;
886 }
887 }
888 else if (!IS_EDGE_SHARP(e2l)) {
889 /* More than two corners using this edge, tag as sharp if not yet done. */
890 e2l[1] = INDEX_INVALID;
891
892 /* We want to avoid tagging edges as sharp when it is already defined as such by
893 * other causes than angle threshold. */
894 r_sharp_edges[edge] = false;
895 }
896 /* Else, edge is already 'disqualified' (i.e. sharp)! */
897 }
898 }
899}
900
902 const Span<int> corner_verts,
903 const Span<int> corner_edges,
904 const Span<float3> face_normals,
905 const Span<int> corner_to_face,
906 const Span<bool> sharp_faces,
907 const float split_angle,
908 MutableSpan<bool> sharp_edges)
909{
910 if (split_angle >= float(M_PI)) {
911 /* Nothing to do! */
912 return;
913 }
914
915 /* Mapping edge -> corners. */
916 Array<int2> edge_to_corners(sharp_edges.size(), int2(0));
917
919 corner_verts,
920 corner_edges,
921 corner_to_face,
922 face_normals,
923 sharp_faces,
924 sharp_edges,
925 split_angle,
926 edge_to_corners,
927 sharp_edges);
928}
929
940
949 const Span<int> corner_verts,
950 const Span<int> vert_faces,
951 const int vert,
952 MutableSpan<VertCornerInfo> r_corner_infos)
953{
954 for (const int i : vert_faces.index_range()) {
955 const int face = vert_faces[i];
956 r_corner_infos[i].face = face;
957 r_corner_infos[i].corner = face_find_corner_from_vert(faces[face], corner_verts, vert);
958 r_corner_infos[i].corner_prev = face_corner_prev(faces[face], r_corner_infos[i].corner);
959 r_corner_infos[i].corner_next = face_corner_next(faces[face], r_corner_infos[i].corner);
960 r_corner_infos[i].vert_prev = corner_verts[r_corner_infos[i].corner_prev];
961 r_corner_infos[i].vert_next = corner_verts[r_corner_infos[i].corner_next];
962 }
963}
964
966using EdgeUninitialized = std::monostate;
967
977
986
992struct EdgeSharp {};
993
994using VertEdgeInfo = std::variant<EdgeUninitialized, EdgeOneCorner, EdgeTwoCorners, EdgeSharp>;
995
996static void add_corner_to_edge(const Span<int> corner_edges,
997 const Span<bool> sharp_edges,
998 const int local_corner,
999 const int corner,
1000 const int other_corner,
1001 const bool winding_torwards_vert,
1002 VertEdgeInfo &info)
1003{
1004 if (std::holds_alternative<EdgeUninitialized>(info)) {
1005 if (!sharp_edges.is_empty()) {
1006 /* The first time we encounter the edge, we check if it is marked sharp. In that case corner
1007 * fans shouldn't propagate past it. To find the edge we need to check if the current corner
1008 * references the edge connected to `other_corner` or if `other_corner` uses the edge. */
1009 if (sharp_edges[corner_edges[winding_torwards_vert ? other_corner : corner]]) {
1010 info = EdgeSharp{};
1011 return;
1012 }
1013 }
1014 info = EdgeOneCorner{local_corner, winding_torwards_vert};
1015 }
1016 else if (const EdgeOneCorner *info_one_edge = std::get_if<EdgeOneCorner>(&info)) {
1017 /* If the edge ends up being used by faces, we still have to check if the winding direction
1018 * changes. Though it's an undesirable situation for the mesh to be in, we shouldn't propagate
1019 * smooth normals across edges facing opposite directions. Breaking the flow on these winding
1020 * direction changes also simplifies the fan traversal later on; without it the we couldn't
1021 * traverse by just continuing to use the next/previous corner. */
1022 if (info_one_edge->winding_torwards_vert == winding_torwards_vert) {
1023 info = EdgeSharp{};
1024 return;
1025 }
1026 info = EdgeTwoCorners{info_one_edge->local_corner_1, local_corner};
1027 }
1028 else {
1029 /* The edge is either already sharp, or we're trying to add a third corner. */
1030 info = EdgeSharp{};
1031 }
1032}
1033
1036 16,
1042
1049 LocalEdgeVectorSet &r_other_vert_to_edge)
1050{
1051 r_other_vert_to_edge.reserve(corner_infos.size());
1052 for (VertCornerInfo &info : corner_infos) {
1053 info.local_edge_prev = r_other_vert_to_edge.index_of_or_add(info.vert_prev);
1054 info.local_edge_next = r_other_vert_to_edge.index_of_or_add(info.vert_next);
1055 }
1056}
1057
1058static void calc_connecting_edge_info(const Span<int> corner_edges,
1059 const Span<bool> sharp_edges,
1060 const Span<bool> sharp_faces,
1061 const Span<VertCornerInfo> corner_infos,
1062 MutableSpan<VertEdgeInfo> edge_infos)
1063{
1064 for (const int local_corner : corner_infos.index_range()) {
1065 const VertCornerInfo &info = corner_infos[local_corner];
1066 if (!sharp_faces.is_empty() && sharp_faces[info.face]) {
1067 /* Sharp faces implicitly cause sharp edges. */
1068 edge_infos[info.local_edge_prev] = EdgeSharp{};
1069 edge_infos[info.local_edge_next] = EdgeSharp{};
1070 continue;
1071 }
1072 /* The "previous" edge is winding towards the vertex, the "next" edge is winding away. */
1073 add_corner_to_edge(corner_edges,
1074 sharp_edges,
1075 local_corner,
1076 info.corner,
1077 info.corner_prev,
1078 true,
1079 edge_infos[info.local_edge_prev]);
1080 add_corner_to_edge(corner_edges,
1081 sharp_edges,
1082 local_corner,
1083 info.corner,
1084 info.corner_next,
1085 false,
1086 edge_infos[info.local_edge_next]);
1087 }
1088}
1089
1096 const Span<VertEdgeInfo> edge_infos,
1097 const int start_local_corner,
1098 Vector<int, 16> &result_fan)
1099{
1100 result_fan.append(start_local_corner);
1101 {
1102 /* Travel around the vertex in a right-handed clockwise direction (based on the normal). The
1103 * corners found in this traversal are reversed so the direction matches with the next
1104 * traversal (or so that the next traversal doesn't have to be added at the beginning of the
1105 * vector). */
1106 int current = start_local_corner;
1107 int local_edge = corner_infos[current].local_edge_next;
1108 bool found_cyclic_fan = false;
1109 while (const EdgeTwoCorners *edge = std::get_if<EdgeTwoCorners>(&edge_infos[local_edge])) {
1110 current = mesh::edge_other_vert(int2(edge->local_corner_1, edge->local_corner_2), current);
1111 if (current == start_local_corner) {
1112 found_cyclic_fan = true;
1113 break;
1114 }
1115 result_fan.append(current);
1116 local_edge = corner_infos[current].local_edge_next;
1117 }
1118 /* Reverse the corners added so the final order is consistent with the next traversal. */
1119 result_fan.as_mutable_span().reverse();
1120
1121 if (found_cyclic_fan) {
1122 /* To match behavior from the previous implementation of face corner normal calculation, the
1123 * final fan is rotated so that the smallest face corner index comes first. */
1124 int *fan_first_corner = std::min_element(
1125 result_fan.begin(), result_fan.end(), [&](const int a, const int b) {
1126 return corner_infos[a].corner < corner_infos[b].corner;
1127 });
1128 std::rotate(result_fan.begin(), fan_first_corner, result_fan.end());
1129 return;
1130 }
1131 }
1132
1133 /* Travel in the other direction. */
1134 int current = start_local_corner;
1135 int local_edge = corner_infos[current].local_edge_prev;
1136 while (const EdgeTwoCorners *edge = std::get_if<EdgeTwoCorners>(&edge_infos[local_edge])) {
1137 current = current == edge->local_corner_1 ? edge->local_corner_2 : edge->local_corner_1;
1138 /* Cyclic fans have already been found, so there's no need to check for them here. */
1139 result_fan.append(current);
1140 local_edge = corner_infos[current].local_edge_prev;
1141 }
1142}
1143
1149static void calc_edge_directions(const Span<float3> vert_positions,
1150 const Span<int> local_edge_by_vert,
1151 const float3 &vert_position,
1152 MutableSpan<float3> edge_dirs)
1153{
1154 for (const int i : local_edge_by_vert.index_range()) {
1155 edge_dirs[i] = math::normalize(vert_positions[local_edge_by_vert[i]] - vert_position);
1156 }
1157}
1158
1161 const Span<float3> edge_dirs,
1162 const Span<float3> face_normals,
1163 const Span<int> local_corners_in_fan)
1164{
1165 if (local_corners_in_fan.size() == 1) {
1166 /* Logically this special case is unnecessary, but due to floating point precision it is
1167 * required for the output to be the same as previous versions of the algorithm. */
1168 return face_normals[corner_infos[local_corners_in_fan.first()].face];
1169 }
1170 float3 fan_normal(0);
1171 for (const int local_corner : local_corners_in_fan) {
1172 const VertCornerInfo &info = corner_infos[local_corner];
1173 const float3 &dir_prev = edge_dirs[info.local_edge_prev];
1174 const float3 &dir_next = edge_dirs[info.local_edge_next];
1175 const float factor = math::safe_acos_approx(math::dot(dir_prev, dir_next));
1176 fan_normal += face_normals[info.face] * factor;
1177 }
1178 return math::normalize(fan_normal);
1179}
1180
1182 /* Maybe acyclic and unordered set of adjacent corners in same smooth group around vertex. */
1185};
1186
1189 const Span<short2> custom_normals,
1190 const Span<VertCornerInfo> corner_infos,
1191 const Span<float3> edge_dirs,
1192 const Span<int> local_corners_in_fan,
1193 float3 &fan_normal,
1194 CornerNormalSpaceArray *r_fan_spaces,
1195 Vector<CornerSpaceGroup, 0> *r_local_space_groups)
1196{
1197 const int local_edge_first = corner_infos[local_corners_in_fan.first()].local_edge_next;
1198 const int local_edge_last = corner_infos[local_corners_in_fan.last()].local_edge_prev;
1199
1200 Vector<float3, 16> fan_edge_dirs;
1201 if (local_corners_in_fan.size() > 1) {
1202 fan_edge_dirs.reserve(local_corners_in_fan.size() + 1);
1203 for (const int local_corner : local_corners_in_fan) {
1204 const VertCornerInfo &info = corner_infos[local_corner];
1205 fan_edge_dirs.append_unchecked(edge_dirs[info.local_edge_next]);
1206 }
1207 if (local_edge_last != local_edge_first) {
1208 fan_edge_dirs.append_unchecked(edge_dirs[local_edge_last]);
1209 }
1210 }
1211
1213 fan_normal, edge_dirs[local_edge_first], edge_dirs[local_edge_last], fan_edge_dirs);
1214
1215 if (!custom_normals.is_empty()) {
1216 int2 average_custom_normal(0);
1217 for (const int local_corner : local_corners_in_fan) {
1218 const VertCornerInfo &info = corner_infos[local_corner];
1219 average_custom_normal += int2(custom_normals[info.corner]);
1220 }
1221 average_custom_normal /= local_corners_in_fan.size();
1222 fan_normal = corner_space_custom_data_to_normal(fan_space, short2(average_custom_normal));
1223 }
1224
1225 if (!r_fan_spaces) {
1226 return;
1227 }
1228
1229 Array<int> fan_corners(local_corners_in_fan.size());
1230 for (const int i : local_corners_in_fan.index_range()) {
1231 const VertCornerInfo &info = corner_infos[local_corners_in_fan[i]];
1232 fan_corners[i] = info.corner;
1233 }
1234 r_local_space_groups->append({std::move(fan_corners), fan_space});
1235}
1236
1237void normals_calc_corners(const Span<float3> vert_positions,
1239 const Span<int> corner_verts,
1240 const Span<int> corner_edges,
1241 const GroupedSpan<int> vert_to_face_map,
1242 const Span<float3> face_normals,
1243 const Span<bool> sharp_edges,
1244 const Span<bool> sharp_faces,
1245 const Span<short2> custom_normals,
1246 CornerNormalSpaceArray *r_fan_spaces,
1247 MutableSpan<float3> r_corner_normals)
1248{
1249 BLI_assert(corner_verts.size() == corner_edges.size());
1250 BLI_assert(custom_normals.is_empty() || corner_verts.size() == custom_normals.size());
1251 BLI_assert(corner_verts.size() == r_corner_normals.size());
1252 BLI_assert(corner_verts.size() == vert_to_face_map.offsets.total_size());
1253
1254 /* Mesh is not empty, but there are no faces, so no normals. */
1255 if (corner_verts.is_empty()) {
1256 return;
1257 }
1258
1260
1261 threading::parallel_for(vert_positions.index_range(), 256, [&](const IndexRange range) {
1262 Vector<VertCornerInfo, 16> corner_infos;
1263 LocalEdgeVectorSet local_edge_by_vert;
1264 Vector<VertEdgeInfo, 16> edge_infos;
1265 Vector<float3, 16> edge_dirs;
1266 Vector<bool, 16> local_corner_visited;
1267 Vector<int, 16> corners_in_fan;
1268
1269 Vector<CornerSpaceGroup, 0> *local_space_groups = r_fan_spaces ? &space_groups.local() :
1270 nullptr;
1271
1272 for (const int vert : range) {
1273 const float3 vert_position = vert_positions[vert];
1274 const Span<int> vert_faces = vert_to_face_map[vert];
1275
1276 /* Because we're iterating over vertices in order to batch work for their connected face
1277 * corners, we have to handle loose vertices and vertices not used by faces. */
1278 if (vert_faces.is_empty()) {
1279 continue;
1280 }
1281
1282 corner_infos.resize(vert_faces.size());
1283 collect_corner_info(faces, corner_verts, vert_faces, vert, corner_infos);
1284
1285 local_edge_by_vert.clear_and_keep_capacity();
1286 calc_local_edge_indices(corner_infos, local_edge_by_vert);
1287
1288 edge_infos.clear();
1289 edge_infos.resize(local_edge_by_vert.size());
1290 calc_connecting_edge_info(corner_edges, sharp_edges, sharp_faces, corner_infos, edge_infos);
1291
1292 edge_dirs.resize(edge_infos.size());
1293 calc_edge_directions(vert_positions, local_edge_by_vert, vert_position, edge_dirs);
1294
1295 /* Though we are protected from traversing to the same corner twice by the fact that 3-way
1296 * connections are marked sharp, we need to maintain the "visited" status of each corner so
1297 * we can find the next start corner for each subsequent fan traversal. Keeping track of the
1298 * number of visited corners is a quick way to avoid this book keeping for the final fan (and
1299 * there are usually just two, so that should be worth it). */
1300 int visited_count = 0;
1301 local_corner_visited.resize(vert_faces.size());
1302 local_corner_visited.fill(false);
1303
1304 int start_local_corner = 0;
1305 while (true) {
1306 corners_in_fan.clear();
1307 traverse_fan_local_corners(corner_infos, edge_infos, start_local_corner, corners_in_fan);
1308
1309 float3 fan_normal = accumulate_fan_normal(
1310 corner_infos, edge_dirs, face_normals, corners_in_fan);
1311
1312 if (!custom_normals.is_empty() || r_fan_spaces) {
1313 handle_fan_result_and_custom_normals(custom_normals,
1314 corner_infos,
1315 edge_dirs,
1316 corners_in_fan,
1317 fan_normal,
1318 r_fan_spaces,
1319 local_space_groups);
1320 }
1321
1322 for (const int local_corner : corners_in_fan) {
1323 const VertCornerInfo &info = corner_infos[local_corner];
1324 r_corner_normals[info.corner] = fan_normal;
1325 }
1326
1327 visited_count += corners_in_fan.size();
1328 if (visited_count == corner_infos.size()) {
1329 break;
1330 }
1331
1332 local_corner_visited.as_mutable_span().fill_indices(corners_in_fan.as_span(), true);
1333 BLI_assert(!local_corner_visited.as_span().take_front(start_local_corner).contains(false));
1334 BLI_assert(local_corner_visited.as_span().drop_front(start_local_corner).contains(false));
1335 /* Will start traversing the next smooth fan mixed in shared index space. */
1336 while (local_corner_visited[start_local_corner]) {
1337 start_local_corner++;
1338 }
1339 }
1340 BLI_assert(visited_count == corner_infos.size());
1341 }
1342 });
1343
1344 if (!r_fan_spaces) {
1345 return;
1346 }
1347
1348 Vector<int> space_groups_count;
1349 Vector<Vector<CornerSpaceGroup, 0>> all_space_groups;
1350 /* WARNING: can't use `auto` here, causes build failure on GCC 15.2, WITH_TBB=OFF. */
1351 for (Vector<CornerSpaceGroup, 0> &groups : space_groups) {
1352 space_groups_count.append(groups.size());
1353 all_space_groups.append(std::move(groups));
1354 }
1355 space_groups_count.append(0);
1357 space_groups_count);
1358
1359 r_fan_spaces->spaces.reinitialize(space_offsets.total_size());
1360 r_fan_spaces->corner_space_indices.reinitialize(corner_verts.size());
1361 if (r_fan_spaces->create_corners_by_space) {
1362 r_fan_spaces->corners_by_space.reinitialize(space_offsets.total_size());
1363 }
1364
1365 /* Copy the data from each local data vector to the final array. it's expected that
1366 * multi-threading has some benefit here, even though the work is largely just copying memory,
1367 * but choose a large grain size to err on the size of less parallelization. */
1368 const int64_t mean_size = std::max<int64_t>(1,
1369 space_offsets.total_size() / space_offsets.size());
1370 const int64_t grain_size = std::max<int64_t>(1, 1024 * 16 / mean_size);
1371 threading::parallel_for(all_space_groups.index_range(), grain_size, [&](const IndexRange range) {
1372 for (const int thread_i : range) {
1373 Vector<CornerSpaceGroup, 0> &local_space_groups = all_space_groups[thread_i];
1374 for (const int group_i : local_space_groups.index_range()) {
1375 const int space_index = space_offsets[thread_i][group_i];
1376 r_fan_spaces->spaces[space_index] = local_space_groups[group_i].space;
1377 r_fan_spaces->corner_space_indices.as_mutable_span().fill_indices(
1378 local_space_groups[group_i].fan_corners.as_span(), space_index);
1379 }
1380 if (!r_fan_spaces->create_corners_by_space) {
1381 continue;
1382 }
1383 for (const int group_i : local_space_groups.index_range()) {
1384 const int space_index = space_offsets[thread_i][group_i];
1385 r_fan_spaces->corners_by_space[space_index] = std::move(
1386 local_space_groups[group_i].fan_corners);
1387 }
1388 }
1389 });
1390}
1391
1392#undef INDEX_UNSET
1393#undef INDEX_INVALID
1394#undef IS_EDGE_SHARP
1395
1405
1408 const Span<int> corner_verts,
1409 const Span<int> corner_edges,
1410 const GroupedSpan<int> vert_to_face_map,
1411 const Span<float3> vert_normals,
1412 const Span<float3> face_normals,
1413 const Span<bool> sharp_faces,
1414 const bool use_vertices,
1415 MutableSpan<float3> r_custom_corner_normals,
1416 MutableSpan<bool> sharp_edges,
1417 MutableSpan<short2> r_clnors_data)
1418{
1419 /* We *may* make that poor #bke::mesh::normals_calc_corners() even more complex by making it
1420 * handling that feature too, would probably be more efficient in absolute. However, this
1421 * function *is not* performance-critical, since it is mostly expected to be called by IO add-ons
1422 * when importing custom normals, and modifier (and perhaps from some editing tools later?). So
1423 * better to keep some simplicity here, and just call #bke::mesh::normals_calc_corners() twice!
1424 */
1425 CornerNormalSpaceArray lnors_spacearr;
1426 lnors_spacearr.create_corners_by_space = true;
1427 BitVector<> done_corners(corner_verts.size(), false);
1428 Array<float3> corner_normals(corner_verts.size());
1429 const Array<int> corner_to_face = build_corner_to_face_map(faces);
1430
1431 /* Compute current lnor spacearr. */
1432 normals_calc_corners(positions,
1433 faces,
1434 corner_verts,
1435 corner_edges,
1436 vert_to_face_map,
1437 face_normals,
1438 sharp_edges,
1439 sharp_faces,
1440 r_clnors_data,
1441 &lnors_spacearr,
1442 corner_normals);
1443
1444 /* Set all given zero vectors to their default value. */
1445 if (use_vertices) {
1446 for (const int i : positions.index_range()) {
1447 if (is_zero_v3(r_custom_corner_normals[i])) {
1448 copy_v3_v3(r_custom_corner_normals[i], vert_normals[i]);
1449 }
1450 }
1451 }
1452 else {
1453 for (const int i : corner_verts.index_range()) {
1454 if (is_zero_v3(r_custom_corner_normals[i])) {
1455 copy_v3_v3(r_custom_corner_normals[i], corner_normals[i]);
1456 }
1457 }
1458 }
1459
1460 /* Now, check each current smooth fan (one lnor space per smooth fan!),
1461 * and if all its matching custom corner_normals are not (enough) equal, add sharp edges as
1462 * needed. This way, next time we run bke::mesh::normals_calc_corners(), we'll get lnor
1463 * spacearr/smooth fans matching given custom corner_normals. Note this code *will never* unsharp
1464 * edges! And quite obviously, when we set custom normals per vertices, running this is
1465 * absolutely useless. */
1466 if (use_vertices) {
1467 done_corners.fill(true);
1468 }
1469 else {
1470 for (const int i : corner_verts.index_range()) {
1471 if (done_corners[i]) {
1472 continue;
1473 }
1474
1475 const int space_index = lnors_spacearr.corner_space_indices[i];
1476 const Span<int> fan_corners = lnors_spacearr.corners_by_space[space_index];
1477
1478 /* Notes:
1479 * - In case of mono-corner smooth fan, we have nothing to do.
1480 * - Loops in this linklist are ordered (in reversed order compared to how they were
1481 * discovered by bke::mesh::normals_calc_corners(), but this is not a problem).
1482 * Which means if we find a mismatching clnor,
1483 * we know all remaining corners will have to be in a new, different smooth fan/lnor space.
1484 * - In smooth fan case, we compare each clnor against a ref one,
1485 * to avoid small differences adding up into a real big one in the end!
1486 */
1487 if (fan_corners.is_empty()) {
1488 done_corners[i].set();
1489 continue;
1490 }
1491
1492 int prev_corner = -1;
1493 const float *org_nor = nullptr;
1494
1495 for (int i = fan_corners.index_range().last(); i >= 0; i--) {
1496 const int corner = fan_corners[i];
1497 float *nor = r_custom_corner_normals[corner];
1498
1499 if (!org_nor) {
1500 org_nor = nor;
1501 }
1502 else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
1503 /* Current normal differs too much from org one, we have to tag the edge between
1504 * previous corner's face and current's one as sharp.
1505 * We know those two corners do not point to the same edge,
1506 * since we do not allow reversed winding in a same smooth fan. */
1507 const IndexRange face = faces[corner_to_face[corner]];
1508 const int corner_prev = face_corner_prev(face, corner);
1509 const int edge = corner_edges[corner];
1510 const int edge_prev = corner_edges[corner_prev];
1511 const int prev_edge = corner_edges[prev_corner];
1512 sharp_edges[prev_edge == edge_prev ? prev_edge : edge] = true;
1513
1514 org_nor = nor;
1515 }
1516
1517 prev_corner = corner;
1518 done_corners[corner].set();
1519 }
1520
1521 /* We also have to check between last and first corners,
1522 * otherwise we may miss some sharp edges here!
1523 * This is just a simplified version of above while loop.
1524 * See #45984. */
1525 if (fan_corners.size() > 1 && org_nor) {
1526 const int corner = fan_corners.last();
1527 float *nor = r_custom_corner_normals[corner];
1528
1529 if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
1530 const IndexRange face = faces[corner_to_face[corner]];
1531 const int corner_prev = face_corner_prev(face, corner);
1532 const int edge = corner_edges[corner];
1533 const int edge_prev = corner_edges[corner_prev];
1534 const int prev_edge = corner_edges[prev_corner];
1535 sharp_edges[prev_edge == edge_prev ? prev_edge : edge] = true;
1536 }
1537 }
1538 }
1539
1540 /* And now, recompute our new auto `corner_normals` and lnor spacearr! */
1541 normals_calc_corners(positions,
1542 faces,
1543 corner_verts,
1544 corner_edges,
1545 vert_to_face_map,
1546 face_normals,
1547 sharp_edges,
1548 sharp_faces,
1549 r_clnors_data,
1550 &lnors_spacearr,
1551 corner_normals);
1552 }
1553
1554 /* And we just have to convert plain object-space custom normals to our
1555 * lnor space-encoded ones. */
1556 for (const int i : corner_verts.index_range()) {
1557 if (lnors_spacearr.corner_space_indices[i] == -1) {
1558 done_corners[i].reset();
1559 if (G.debug & G_DEBUG) {
1560 printf("WARNING! Still getting invalid nullptr corner space in second for loop %d!\n", i);
1561 }
1562 continue;
1563 }
1564 if (!done_corners[i]) {
1565 continue;
1566 }
1567
1568 const int space_index = lnors_spacearr.corner_space_indices[i];
1569 const Span<int> fan_corners = lnors_spacearr.corners_by_space[space_index];
1570
1571 /* Note we accumulate and average all custom normals in current smooth fan,
1572 * to avoid getting different clnors data (tiny differences in plain custom normals can
1573 * give rather huge differences in computed 2D factors). */
1574 if (fan_corners.size() < 2) {
1575 const int nidx = use_vertices ? corner_verts[i] : i;
1576 r_clnors_data[i] = corner_space_custom_normal_to_data(lnors_spacearr.spaces[space_index],
1577 r_custom_corner_normals[nidx]);
1578 done_corners[i].reset();
1579 }
1580 else {
1581 float3 avg_nor(0.0f);
1582 for (const int corner : fan_corners) {
1583 const int nidx = use_vertices ? corner_verts[corner] : corner;
1584 avg_nor += r_custom_corner_normals[nidx];
1585 done_corners[corner].reset();
1586 }
1587
1588 mul_v3_fl(avg_nor, 1.0f / float(fan_corners.size()));
1590 lnors_spacearr.spaces[space_index], avg_nor);
1591
1592 r_clnors_data.fill_indices(fan_corners, clnor_data_tmp);
1593 }
1594 }
1595}
1596
1597void normals_corner_custom_set(const Span<float3> vert_positions,
1599 const Span<int> corner_verts,
1600 const Span<int> corner_edges,
1601 const GroupedSpan<int> vert_to_face_map,
1602 const Span<float3> vert_normals,
1603 const Span<float3> face_normals,
1604 const Span<bool> sharp_faces,
1605 MutableSpan<bool> sharp_edges,
1606 MutableSpan<float3> r_custom_corner_normals,
1607 MutableSpan<short2> r_clnors_data)
1608{
1609 mesh_normals_corner_custom_set(vert_positions,
1610 faces,
1611 corner_verts,
1612 corner_edges,
1613 vert_to_face_map,
1614 vert_normals,
1615 face_normals,
1616 sharp_faces,
1617 false,
1618 r_custom_corner_normals,
1619 sharp_edges,
1620 r_clnors_data);
1621}
1622
1625 const Span<int> corner_verts,
1626 const Span<int> corner_edges,
1627 const GroupedSpan<int> vert_to_face_map,
1628 const Span<float3> vert_normals,
1629 const Span<float3> face_normals,
1630 const Span<bool> sharp_faces,
1631 MutableSpan<bool> sharp_edges,
1632 MutableSpan<float3> r_custom_vert_normals,
1633 MutableSpan<short2> r_clnors_data)
1634{
1635 mesh_normals_corner_custom_set(vert_positions,
1636 faces,
1637 corner_verts,
1638 corner_edges,
1639 vert_to_face_map,
1640 vert_normals,
1641 face_normals,
1642 sharp_faces,
1643 true,
1644 r_custom_vert_normals,
1645 sharp_edges,
1646 r_clnors_data);
1647}
1648
1650 MutableSpan<float3> r_custom_nors,
1651 const bool use_vertices)
1652{
1653 MutableAttributeAccessor attributes = mesh.attributes_for_write();
1654 SpanAttributeWriter custom_normals = attributes.lookup_or_add_for_write_span<short2>(
1655 "custom_normal", AttrDomain::Corner);
1656 if (!custom_normals) {
1657 return;
1658 }
1659 SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>(
1660 "sharp_edge", AttrDomain::Edge);
1661 const VArraySpan sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
1662
1663 mesh_normals_corner_custom_set(mesh.vert_positions(),
1664 mesh.faces(),
1665 mesh.corner_verts(),
1666 mesh.corner_edges(),
1667 mesh.vert_to_face_map(),
1668 mesh.vert_normals_true(),
1669 mesh.face_normals_true(),
1670 sharp_faces,
1671 use_vertices,
1672 r_custom_nors,
1673 sharp_edges.span,
1674 custom_normals.span);
1675
1676 sharp_edges.finish();
1677 custom_normals.finish();
1678}
1679
1680} // namespace mesh
1681
1683{
1684 threading::parallel_for(normals.index_range(), 4096, [&](const IndexRange range) {
1685 for (const int i : range) {
1686 normals[i] = math::normalize(normals[i]);
1687 }
1688 });
1689}
1690
1692{
1693 normalize_vecs(corner_normals);
1694 mesh::mesh_set_custom_normals(mesh, corner_normals, false);
1695}
1696
1698{
1699 mesh::mesh_set_custom_normals(mesh, corner_normals, false);
1700}
1701
1703{
1704 normalize_vecs(vert_normals);
1705 mesh::mesh_set_custom_normals(mesh, vert_normals, true);
1706}
1707
1712
1713namespace mesh {
1714
1716
1718{
1719 return meta_data == CORNER_FAN_META_DATA;
1720}
1721
1735
1740
1748
1750{
1751 if (this->result_domain) {
1752 /* Any combination of point/face domains puts the result normals on the corner domain. */
1753 if (this->result_domain != domain) {
1755 }
1756 }
1757 else {
1758 this->result_domain = domain;
1759 }
1760}
1761
1763{
1764 this->add_domain(domain);
1765 this->result_type = Output::Free;
1766}
1767
1769{
1770 const bke::AttributeAccessor attributes = mesh.attributes();
1771 const std::optional<bke::AttributeMetaData> custom_normal = attributes.lookup_meta_data(
1772 "custom_normal");
1773 if (!custom_normal) {
1774 this->add_no_custom_normals(mesh.normals_domain());
1775 return;
1776 }
1777 if (custom_normal->data_type == bke::AttrType::Float3) {
1778 if (custom_normal->domain == bke::AttrDomain::Edge) {
1779 /* Skip invalid storage on the edge domain. */
1780 this->add_no_custom_normals(mesh.normals_domain());
1781 return;
1782 }
1783 this->add_free_normals(custom_normal->domain);
1784 }
1785 else if (*custom_normal == CORNER_FAN_META_DATA) {
1786 this->add_corner_fan_normals();
1787 }
1788}
1789
1790} // namespace mesh
1791
1792} // namespace blender::bke
1793
1794#undef LNOR_SPACE_TRIGO_THRESHOLD
1795
@ G_DEBUG
void BKE_lnor_space_custom_data_to_normal(const MLoopNorSpace *lnor_space, const short clnor_data[2], float r_custom_lnor[3])
MLoopNorSpace * BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr)
@ MLNOR_SPACE_IS_SINGLE
Definition BKE_mesh.h:278
void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2])
@ MLNOR_SPACEARR_LOOP_INDEX
Definition BKE_mesh.h:299
@ MLNOR_SPACEARR_BMLOOP_PTR
Definition BKE_mesh.h:300
bool BKE_mesh_vert_normals_are_dirty(const Mesh *mesh)
void BKE_lnor_space_add_loop(MLoopNorSpaceArray *lnors_spacearr, MLoopNorSpace *lnor_space, int corner, void *bm_loop, bool is_single)
void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr)
void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, int numLoops, char data_type)
bool BKE_mesh_face_normals_are_dirty(const Mesh *mesh)
void BKE_lnor_space_define(MLoopNorSpace *lnor_space, const float lnor[3], const float vec_ref[3], const float vec_other[3], blender::Span< blender::float3 > edge_vectors)
void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr)
void BKE_lnor_spacearr_tls_join(MLoopNorSpaceArray *lnors_spacearr, MLoopNorSpaceArray *lnors_spacearr_tls)
void BKE_lnor_spacearr_tls_init(MLoopNorSpaceArray *lnors_spacearr, MLoopNorSpaceArray *lnors_spacearr_tls)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_NOINLINE
#define BLI_ASSERT_UNIT_V3(v)
#define M_PI
float normal_quad_v3(float n[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3])
Definition math_geom.cc:58
#define MINLINE
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_newell_cross_v3_v3v3(float n[3], const float v_prev[3], const float v_curr[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2_short(short r[2], const short a[2])
MINLINE bool compare_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float normalize_v3(float n[3])
void BLI_memarena_merge(MemArena *ma_dst, MemArena *ma_src)
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void * BLI_memarena_calloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
#define POINTER_FROM_INT(i)
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
BMesh const char void * data
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void reinitialize(const int64_t new_size)
bool is() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr void fill_indices(Span< IndexT > indices, const T &value) const
Definition BLI_span.hh:526
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t index_of_or_add(const Key &key)
void reserve(const int64_t n)
void append(const T &value)
IndexRange index_range() const
MutableSpan< T > as_mutable_span()
void append_unchecked(const T &value)
void reserve(const int64_t min_capacity)
void fill(const bool value)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GAttributeReader lookup(const StringRef attribute_id) const
std::optional< AttributeMetaData > lookup_meta_data(StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
void store_varray(const VArray< float3 > &data)
std::variant< UseTrueCache, Vector< float3 >, Span< float3 > > data
MutableSpan< float3 > ensure_vector_size(const int size)
Span< float3 > get_span() const
void store_vector(Vector< float3 > &&data)
std::optional< bke::AttrDomain > result_domain
Definition BKE_mesh.hh:263
void add_domain(bke::AttrDomain domain)
void add_free_normals(bke::AttrDomain domain)
void add_no_custom_normals(bke::MeshNormalDomain domain)
nullptr float
static float normals[][3]
uint nor
#define printf(...)
VecBase< int, 2 > int2
VecBase< short, 2 > short2
ccl_device_inline float beta(const float x, const float y)
Definition math_base.h:661
static char faces[256]
#define G(x, y, z)
#define INDEX_INVALID
MINLINE short unit_float_to_short(const float val)
#define INDEX_UNSET
#define LNOR_SPACE_TRIGO_THRESHOLD
MINLINE float unit_short_to_float(const short val)
#define IS_EDGE_SHARP(_e2l)
void gather_to_groups(const OffsetIndices< int > dst_offsets, const IndexMask &src_selection, const Span< T > src, MutableSpan< T > dst)
BooleanMix booleans_mix_calc(const VArray< bool > &varray, IndexRange range_to_check)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
GAttributeReader lookup(const void *owner, const StringRef name)
std::monostate EdgeUninitialized
int edge_other_vert(const int2 edge, const int vert)
Definition BKE_mesh.hh:370
static CornerNormalSpace corner_fan_space_define(const float3 &lnor, const float3 &vec_ref, const float3 &vec_other, const Span< float3 > edge_vectors)
static float3 normal_calc_ngon(const Span< float3 > vert_positions, const Span< int > face_verts)
static void mesh_normals_corner_custom_set(const Span< float3 > positions, const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_edges, const GroupedSpan< int > vert_to_face_map, const Span< float3 > vert_normals, const Span< float3 > face_normals, const Span< bool > sharp_faces, const bool use_vertices, MutableSpan< float3 > r_custom_corner_normals, MutableSpan< bool > sharp_edges, MutableSpan< short2 > r_clnors_data)
static float3 corner_space_custom_data_to_normal(const CornerNormalSpace &lnor_space, const short2 clnor_data)
constexpr AttributeMetaData CORNER_FAN_META_DATA
float3 face_normal_calc(Span< float3 > vert_positions, Span< int > face_verts)
static void calc_edge_directions(const Span< float3 > vert_positions, const Span< int > local_edge_by_vert, const float3 &vert_position, MutableSpan< float3 > edge_dirs)
static void mix_normals_vert_to_face(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< float3 > vert_normals, MutableSpan< float3 > face_normals)
int face_corner_prev(const IndexRange face, const int corner)
Definition BKE_mesh.hh:306
static void mix_normals_corner_to_vert(const Span< float3 > vert_positions, const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > vert_to_face_map, const Span< float3 > corner_normals, MutableSpan< float3 > vert_normals)
int face_find_corner_from_vert(const IndexRange face, const Span< int > corner_verts, const int vert)
Definition BKE_mesh.hh:327
static void calc_local_edge_indices(MutableSpan< VertCornerInfo > corner_infos, LocalEdgeVectorSet &r_other_vert_to_edge)
static void mesh_set_custom_normals(Mesh &mesh, MutableSpan< float3 > r_custom_nors, const bool use_vertices)
static void traverse_fan_local_corners(const Span< VertCornerInfo > corner_infos, const Span< VertEdgeInfo > edge_infos, const int start_local_corner, Vector< int, 16 > &result_fan)
static float3 accumulate_fan_normal(const Span< VertCornerInfo > corner_infos, const Span< float3 > edge_dirs, const Span< float3 > face_normals, const Span< int > local_corners_in_fan)
static void calc_connecting_edge_info(const Span< int > corner_edges, const Span< bool > sharp_edges, const Span< bool > sharp_faces, const Span< VertCornerInfo > corner_infos, MutableSpan< VertEdgeInfo > edge_infos)
Array< int > build_corner_to_face_map(OffsetIndices< int > faces)
static void add_corner_to_edge(const Span< int > corner_edges, const Span< bool > sharp_edges, const int local_corner, const int corner, const int other_corner, const bool winding_torwards_vert, VertEdgeInfo &info)
static void mix_normals_corner_to_face(const OffsetIndices< int > faces, const Span< float3 > corner_normals, MutableSpan< float3 > face_normals)
bool is_corner_fan_normals(const AttributeMetaData &meta_data)
void normals_calc_corners(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< int > corner_edges, GroupedSpan< int > vert_to_face_map, Span< float3 > face_normals, Span< bool > sharp_edges, Span< bool > sharp_faces, Span< short2 > custom_normals, CornerNormalSpaceArray *r_fan_spaces, MutableSpan< float3 > r_corner_normals)
void normals_corner_custom_set_from_verts(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< int > corner_edges, GroupedSpan< int > vert_to_face_map, Span< float3 > vert_normals, Span< float3 > face_normals, Span< bool > sharp_faces, MutableSpan< bool > sharp_edges, MutableSpan< float3 > r_custom_vert_normals, MutableSpan< short2 > r_clnors_data)
static bke::AttrDomain normal_domain_to_domain(bke::MeshNormalDomain domain)
VectorSet< int, 16, DefaultProbingStrategy, DefaultHash< int >, DefaultEquality< int >, SimpleVectorSetSlot< int, int >, GuardedAllocator > LocalEdgeVectorSet
static void collect_corner_info(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > vert_faces, const int vert, MutableSpan< VertCornerInfo > r_corner_infos)
short2 corner_space_custom_normal_to_data(const CornerNormalSpace &lnor_space, const float3 &custom_lnor)
void edges_sharp_from_angle_set(OffsetIndices< int > faces, Span< int > corner_verts, Span< int > corner_edges, Span< float3 > face_normals, Span< int > corner_to_face, Span< bool > sharp_faces, const float split_angle, MutableSpan< bool > sharp_edges)
int face_corner_next(const IndexRange face, const int corner)
Definition BKE_mesh.hh:315
void normals_calc_verts(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, GroupedSpan< int > vert_to_face_map, Span< float3 > face_normals, MutableSpan< float3 > vert_normals)
static void mesh_edges_sharp_tag(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_edges, const Span< int > corner_to_face_map, const Span< float3 > face_normals, const Span< bool > sharp_faces, const Span< bool > sharp_edges, const float split_angle, MutableSpan< int2 > edge_to_corners, MutableSpan< bool > r_sharp_edges)
static BLI_NOINLINE void handle_fan_result_and_custom_normals(const Span< short2 > custom_normals, const Span< VertCornerInfo > corner_infos, const Span< float3 > edge_dirs, const Span< int > local_corners_in_fan, float3 &fan_normal, CornerNormalSpaceArray *r_fan_spaces, Vector< CornerSpaceGroup, 0 > *r_local_space_groups)
void normals_corner_custom_set(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< int > corner_edges, GroupedSpan< int > vert_to_face_map, Span< float3 > vert_normals, Span< float3 > face_normals, Span< bool > sharp_faces, MutableSpan< bool > sharp_edges, MutableSpan< float3 > r_custom_corner_normals, MutableSpan< short2 > r_clnors_data)
std::variant< EdgeUninitialized, EdgeOneCorner, EdgeTwoCorners, EdgeSharp > VertEdgeInfo
void normals_calc_faces(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, MutableSpan< float3 > face_normals)
static void normalize_vecs(MutableSpan< float3 > normals)
void mesh_set_custom_normals_normalized(Mesh &mesh, MutableSpan< float3 > corner_normals)
void mesh_set_custom_normals_from_verts(Mesh &mesh, MutableSpan< float3 > vert_normals)
void mesh_vert_normals_assign(Mesh &mesh, Span< float3 > vert_normals)
void mesh_set_custom_normals_from_verts_normalized(Mesh &mesh, MutableSpan< float3 > vert_normals)
void mesh_set_custom_normals(Mesh &mesh, MutableSpan< float3 > corner_normals)
VecBase< T, 3 > normal_tri(const VecBase< T, 3 > &v1, const VecBase< T, 3 > &v2, const VecBase< T, 3 > &v3)
float safe_acos_approx(float x)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
bool is_zero(const T &a)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VecBase< int32_t, 2 > int2
PythonProbingStrategy<> DefaultProbingStrategy
VecBase< float, 3 > float3
blender::VecBase< int16_t, 2 > short2
#define floorf
#define sinf
#define cosf
struct LinkNode * loops_pool
Definition BKE_mesh.h:288
struct MemArena * mem
Definition BKE_mesh.h:293
MLoopNorSpace ** lspacearr
Definition BKE_mesh.h:286
float ref_alpha
Definition BKE_mesh.h:256
float vec_ortho[3]
Definition BKE_mesh.h:248
float ref_beta
Definition BKE_mesh.h:265
float vec_ref[3]
Definition BKE_mesh.h:246
float vec_lnor[3]
Definition BKE_mesh.h:241
struct LinkNode * loops
Definition BKE_mesh.h:271
MeshRuntimeHandle * runtime
Vector< Array< int > > corners_by_space
Definition BKE_mesh.hh:177
Vector< CornerNormalSpace > spaces
Definition BKE_mesh.hh:165
i
Definition text_draw.cc:230