Mesh: Reduce custom normal calculation memory usage #107592

Merged
Hans Goudey merged 32 commits from HooglyBoogly/blender:corner-normals-refactor-storage into main 2023-05-10 14:41:16 +02:00
3 changed files with 145 additions and 156 deletions
Showing only changes of commit b69e744e97 - Show all commits

View File

@ -92,9 +92,10 @@ struct MeshNormalFanSpaces {
Array<int> corner_group_lists;
};
short2 fan_space_custom_normal_to_data(const NormalFanSpace *space,
float3 corner_normal_no_custom,
float3 custom_lnor);
void fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space,
const float3 lnor_no_custom,
const float custom_lnor[3],
short r_clnor_data[2]);
/**
* Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
@ -104,8 +105,8 @@ short2 fan_space_custom_normal_to_data(const NormalFanSpace *space,
* \param loop_to_poly_map: Optional pre-created map from corners to their polygon.

The names NormalFanSpaces & NormalFanSpace are too similar, the old suffix "Array" wasn't exactly accurate either (as it's not just an array) but at least it was easily distinguishable.

The names `NormalFanSpaces` & `NormalFanSpace` are too similar, the old suffix "Array" wasn't exactly accurate either (as it's not just an array) but at least it was easily distinguishable.
* \param sharp_edges: Optional array of sharp edge tags, used to split the evaluated normals on
* each side of the edge.
* \param r_lnors_spacearr: Optional return data filled with information about the custom normals
* spaces for each grouped fan of face corners.
* \param r_lnors_spacearr: Optional return data filled with information about the custom
* normals spaces for each grouped fan of face corners.
*/
void normals_calc_loop(Span<float3> vert_positions,
Span<int2> edges,

Worth mentioning this array isn't aligned to geometry and must be accessed via corner_space_indices.

Worth mentioning this array isn't aligned to geometry and must be accessed via `corner_space_indices`.

View File

@ -432,10 +432,10 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
mul_m4_v3(mtx_nor, loop_normals[mirrorj]);
const int space_index = lnors_spacearr.corner_space_indices[mirrorj];
const blender::bke::mesh::NormalFanSpace &space = lnors_spacearr.spaces[space_index];
clnors[mirrorj] = blender::bke::mesh::fan_space_custom_normal_to_data(
&space, loop_normals[mirrorj], loop_normals[mirrorj]);
blender::bke::mesh::fan_space_custom_normal_to_data(&lnors_spacearr.spaces[space_index],
loop_normals[mirrorj],
loop_normals[mirrorj],
clnors[mirrorj]);
}
}
}

View File

@ -468,12 +468,12 @@ MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr)
#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
namespace blender::bke::mesh {
static NormalFanSpace normal_fan_space_define(const float lnor[3],
float vec_ref[3],
float vec_other[3],
const Span<float3> edge_vectors)
static void normal_fan_space_define(NormalFanSpace *lnor_space,
const float lnor[3],
float vec_ref[3],
float vec_other[3],
const blender::Span<blender::float3> edge_vectors)
{
NormalFanSpace lnor_space{};
const float pi2 = float(M_PI) * 2.0f;
float tvec[3], dtp;
const float dtp_ref = dot_v3v3(vec_ref, lnor);
@ -483,14 +483,14 @@ static NormalFanSpace normal_fan_space_define(const float lnor[3],
fabsf(dtp_other) >= LNOR_SPACE_TRIGO_THRESHOLD)) {
/* If vec_ref or vec_other are too much aligned with lnor, we can't build lnor space,
* tag it as invalid and abort. */
lnor_space.ref_alpha = lnor_space.ref_beta = 0.0f;
return lnor_space;
lnor_space->ref_alpha = lnor_space->ref_beta = 0.0f;
return;
}
/* Compute ref alpha, average angle of all available edge vectors to lnor. */
if (!edge_vectors.is_empty()) {
float alpha = 0.0f;
for (const float3 &vec : edge_vectors) {
for (const blender::float3 &vec : edge_vectors) {
alpha += saacosf(dot_v3v3(vec, lnor));
}
/* This piece of code shall only be called for more than one loop. */
@ -499,21 +499,21 @@ static NormalFanSpace normal_fan_space_define(const float lnor[3],
* a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.).
*/
BLI_assert(edge_vectors.size() >= 2);
lnor_space.ref_alpha = alpha / float(edge_vectors.size());
lnor_space->ref_alpha = alpha / float(edge_vectors.size());
}
else {
lnor_space.ref_alpha = (saacosf(dot_v3v3(vec_ref, lnor)) +
saacosf(dot_v3v3(vec_other, lnor))) /
2.0f;
lnor_space->ref_alpha = (saacosf(dot_v3v3(vec_ref, lnor)) +
saacosf(dot_v3v3(vec_other, lnor))) /
2.0f;
}
/* Project vec_ref on lnor's ortho plane. */
mul_v3_v3fl(tvec, lnor, dtp_ref);
sub_v3_v3(vec_ref, tvec);
normalize_v3_v3(lnor_space.vec_ref, vec_ref);
normalize_v3_v3(lnor_space->vec_ref, vec_ref);
cross_v3_v3v3(tvec, lnor, lnor_space.vec_ref);
normalize_v3_v3(lnor_space.vec_ortho, tvec);
cross_v3_v3v3(tvec, lnor, lnor_space->vec_ref);
normalize_v3_v3(lnor_space->vec_ortho, tvec);
/* Project vec_other on lnor's ortho plane. */
mul_v3_v3fl(tvec, lnor, dtp_other);
@ -521,16 +521,14 @@ static NormalFanSpace normal_fan_space_define(const float lnor[3],
normalize_v3(vec_other);
/* Beta is angle between ref_vec and other_vec, around lnor. */
dtp = dot_v3v3(lnor_space.vec_ref, vec_other);
dtp = dot_v3v3(lnor_space->vec_ref, vec_other);
if (LIKELY(dtp < LNOR_SPACE_TRIGO_THRESHOLD)) {
const float beta = saacos(dtp);
lnor_space.ref_beta = (dot_v3v3(lnor_space.vec_ortho, vec_other) < 0.0f) ? pi2 - beta : beta;
lnor_space->ref_beta = (dot_v3v3(lnor_space->vec_ortho, vec_other) < 0.0f) ? pi2 - beta : beta;
}
else {
lnor_space.ref_beta = pi2;
lnor_space->ref_beta = pi2;
}
return lnor_space;
}
} // namespace blender::bke::mesh
@ -541,7 +539,8 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space,
const blender::Span<blender::float3> edge_vectors)
{
using namespace blender::bke::mesh;
const NormalFanSpace space = normal_fan_space_define(lnor, vec_ref, vec_other, edge_vectors);
NormalFanSpace space{};
normal_fan_space_define(&space, lnor, vec_ref, vec_other, edge_vectors);
copy_v3_v3(lnor_space->vec_lnor, lnor);
copy_v3_v3(lnor_space->vec_ref, space.vec_ref);
copy_v3_v3(lnor_space->vec_ortho, space.vec_ortho);
@ -549,16 +548,6 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space,
lnor_space->ref_beta = space.ref_beta;
}
namespace blender::bke::mesh {
static void fan_space_add_corners(MeshNormalFanSpaces &fan_spaces, const Span<int> corners)
{
for (const int i : corners.index_range().drop_back(1)) {
fan_spaces.corner_group_lists[corners[i]] = corners[i + 1];
}
fan_spaces.corner_group_lists[corners.last()] = -1;
}
} // namespace blender::bke::mesh
void BKE_lnor_space_add_loop(MLoopNorSpaceArray *lnors_spacearr,
MLoopNorSpace *lnor_space,
const int ml_index,
@ -595,41 +584,40 @@ MINLINE short unit_float_to_short(const float val)
}
namespace blender::bke::mesh {
static float3 fan_space_custom_data_to_normal(const NormalFanSpace *lnor_space,
const float3 lnor_no_custom,
const short2 clnor_data)
static void fan_space_custom_data_to_normal(const NormalFanSpace *lnor_space,
const float3 lnor_no_custom,

Prefer old name (can remove the BKE prefix).

Prefer old name (can remove the BKE prefix).
const short clnor_data[2],
float r_custom_lnor[3])
{
/* NOP custom normal data or invalid lnor space, return. */
if (clnor_data[0] == 0 || lnor_space->ref_alpha == 0.0f || lnor_space->ref_beta == 0.0f) {
return lnor_no_custom;
copy_v3_v3(r_custom_lnor, lnor_no_custom);
return;
}
float3 r_custom_lnor(0.0f);
{
/* TODO: Check whether using #sincosf() gives any noticeable benefit
* (could not even get it working under linux though)! */
const float pi2 = float(M_PI * 2.0);
const float alphafac = unit_short_to_float(clnor_data[0]);
const float alpha = (alphafac > 0.0f ? lnor_space->ref_alpha : pi2 - lnor_space->ref_alpha) *
alphafac;
const float betafac = unit_short_to_float(clnor_data[1]);
/* TODO: Check whether using #sincosf() gives any noticeable benefit
* (could not even get it working under linux though)! */
const float pi2 = float(M_PI * 2.0);
const float alphafac = unit_short_to_float(clnor_data[0]);
const float alpha = (alphafac > 0.0f ? lnor_space->ref_alpha : pi2 - lnor_space->ref_alpha) *
alphafac;
const float betafac = unit_short_to_float(clnor_data[1]);
mul_v3_v3fl(r_custom_lnor, lnor_no_custom, cosf(alpha));
mul_v3_v3fl(r_custom_lnor, lnor_no_custom, cosf(alpha));
if (betafac == 0.0f) {
madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinf(alpha));
if (betafac == 0.0f) {
madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinf(alpha));
}
else {
const float sinalpha = sinf(alpha);
const float beta = (betafac > 0.0f ? lnor_space->ref_beta : pi2 - lnor_space->ref_beta) *
betafac;
madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinalpha * cosf(beta));
madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ortho, sinalpha * sinf(beta));
}
}
else {
const float sinalpha = sinf(alpha);
const float beta = (betafac > 0.0f ? lnor_space->ref_beta : pi2 - lnor_space->ref_beta) *
betafac;
madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinalpha * cosf(beta));
madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ortho, sinalpha * sinf(beta));
}
return r_custom_lnor;
}
} // namespace blender::bke::mesh
void BKE_lnor_space_custom_data_to_normal(const MLoopNorSpace *lnor_space,
@ -642,63 +630,62 @@ void BKE_lnor_space_custom_data_to_normal(const MLoopNorSpace *lnor_space,
space.vec_ortho = lnor_space->vec_ortho;
space.ref_alpha = lnor_space->ref_alpha;
space.ref_beta = lnor_space->ref_beta;
copy_v3_v3(r_custom_lnor,
fan_space_custom_data_to_normal(&space, lnor_space->vec_lnor, clnor_data));
fan_space_custom_data_to_normal(&space, lnor_space->vec_lnor, clnor_data, r_custom_lnor);
}
namespace blender::bke::mesh {
short2 fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space,
const float3 lnor_no_custom,
const float3 custom_lnor)
void fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space,
const float3 lnor_no_custom,
const float custom_lnor[3],
short r_clnor_data[2])
{
/* We use nullptr vector as NOP custom normal (can be simpler than giving auto-computed `lnor`).
*/
if (is_zero_v3(custom_lnor) || compare_v3v3(lnor_no_custom, custom_lnor, 1e-4f)) {
return short2(0, 0);
r_clnor_data[0] = r_clnor_data[1] = 0;
return;
}
const float pi2 = float(M_PI * 2.0);
const float cos_alpha = dot_v3v3(lnor_no_custom, custom_lnor);
float vec[3], cos_beta;
float alpha;
{
const float pi2 = float(M_PI * 2.0);
const float cos_alpha = dot_v3v3(lnor_no_custom, custom_lnor);
float vec[3], cos_beta;
float alpha;
short2 r_clnor_data;
alpha = saacosf(cos_alpha);
if (alpha > lnor_space->ref_alpha) {
/* Note we could stick to [0, pi] range here,
* but makes decoding more complex, not worth it. */
r_clnor_data[0] = unit_float_to_short(-(pi2 - alpha) / (pi2 - lnor_space->ref_alpha));
}
else {
r_clnor_data[0] = unit_float_to_short(alpha / lnor_space->ref_alpha);
}
/* Project custom lnor on (vec_ref, vec_ortho) plane. */
mul_v3_v3fl(vec, lnor_no_custom, -cos_alpha);
add_v3_v3(vec, custom_lnor);
normalize_v3(vec);
cos_beta = dot_v3v3(lnor_space->vec_ref, vec);
if (cos_beta < LNOR_SPACE_TRIGO_THRESHOLD) {
float beta = saacosf(cos_beta);
if (dot_v3v3(lnor_space->vec_ortho, vec) < 0.0f) {
beta = pi2 - beta;
}
if (beta > lnor_space->ref_beta) {
r_clnor_data[1] = unit_float_to_short(-(pi2 - beta) / (pi2 - lnor_space->ref_beta));
alpha = saacosf(cos_alpha);
if (alpha > lnor_space->ref_alpha) {
/* Note we could stick to [0, pi] range here,
* but makes decoding more complex, not worth it. */
r_clnor_data[0] = unit_float_to_short(-(pi2 - alpha) / (pi2 - lnor_space->ref_alpha));
}
else {
r_clnor_data[1] = unit_float_to_short(beta / lnor_space->ref_beta);
r_clnor_data[0] = unit_float_to_short(alpha / lnor_space->ref_alpha);
}
/* Project custom lnor on (vec_ref, vec_ortho) plane. */
mul_v3_v3fl(vec, lnor_no_custom, -cos_alpha);
add_v3_v3(vec, custom_lnor);
normalize_v3(vec);
cos_beta = dot_v3v3(lnor_space->vec_ref, vec);
if (cos_beta < LNOR_SPACE_TRIGO_THRESHOLD) {
float beta = saacosf(cos_beta);
if (dot_v3v3(lnor_space->vec_ortho, vec) < 0.0f) {
beta = pi2 - beta;
}
if (beta > lnor_space->ref_beta) {
r_clnor_data[1] = unit_float_to_short(-(pi2 - beta) / (pi2 - lnor_space->ref_beta));
}
else {
r_clnor_data[1] = unit_float_to_short(beta / lnor_space->ref_beta);
}
}
else {
r_clnor_data[1] = 0;
}
}
else {
r_clnor_data[1] = 0;
}
return r_clnor_data;
}
} // namespace blender::bke::mesh
@ -712,8 +699,7 @@ void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space,
space.vec_ortho = lnor_space->vec_ortho;
space.ref_alpha = lnor_space->ref_alpha;
space.ref_beta = lnor_space->ref_beta;
copy_v2_v2_short(r_clnor_data,
fan_space_custom_normal_to_data(&space, lnor_space->vec_lnor, custom_lnor));
fan_space_custom_normal_to_data(&space, lnor_space->vec_lnor, custom_lnor, r_clnor_data);
}
namespace blender::bke::mesh {
@ -890,9 +876,20 @@ static void loop_manifold_fan_around_vert_next(const Span<int> corner_verts,
}
}
static void fan_space_add_corners(MeshNormalFanSpaces &fan_spaces,
const Span<int> corners,
const int space_index)
{
for (const int i : corners.index_range().drop_back(1)) {
fan_spaces.corner_group_lists[corners[i]] = corners[i + 1];
}
fan_spaces.corner_group_lists[corners.last()] = -1;
fan_spaces.corner_space_indices.as_mutable_span().fill_indices(corners, space_index);
}
static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
const int ml_curr_index,
NormalFanSpace *lnor_space)
const int space_index)
{
const Span<int> loop_to_poly = common_data->loop_to_poly;
const Span<float3> poly_normals = common_data->poly_normals;
@ -900,7 +897,7 @@ static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
loop_normals[ml_curr_index] = poly_normals[loop_to_poly[ml_curr_index]];
if (lnor_space) {
if (MeshNormalFanSpaces *lnors_spacearr = common_data->lnors_spacearr) {
const Span<float3> positions = common_data->positions;
const Span<int2> edges = common_data->edges;
const OffsetIndices polys = common_data->polys;
@ -922,20 +919,23 @@ static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
sub_v3_v3v3(vec_prev, positions[vert_3], positions[vert_pivot]);
normalize_v3(vec_prev);
*lnor_space = normal_fan_space_define(loop_normals[ml_curr_index], vec_curr, vec_prev, {});
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
normal_fan_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, {});
/* We know there is only one loop in this space, no need to create a link-list in this case. */
fan_space_add_corners(*lnors_spacearr, {ml_curr_index}, space_index);
if (!clnors_data.is_empty()) {
loop_normals[ml_curr_index] = fan_space_custom_data_to_normal(
lnor_space, loop_normals[ml_curr_index], clnors_data[ml_curr_index]);
fan_space_custom_data_to_normal(lnor_space,
loop_normals[ml_curr_index],
clnors_data[ml_curr_index],
loop_normals[ml_curr_index]);
}
}
}
static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
const int ml_curr_index,
Vector<int, 8> &processed_corners,
NormalFanSpace *lnor_space,
const int space_index,
Vector<float3> *edge_vectors)
{
MeshNormalFanSpaces *lnors_spacearr = common_data->lnors_spacearr;
@ -974,6 +974,8 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
int clnors_count = 0;
bool clnors_invalid = false;
Vector<int, 8> processed_corners;
/* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex!
*/
int mlfan_curr_index = ml_prev_index;
@ -1071,15 +1073,15 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
* and optionally compute final lnor from custom data too!
*/
if (lnors_spacearr) {
processed_corners.as_mutable_span().reverse();
fan_space_add_corners(*lnors_spacearr, processed_corners);
fan_space_add_corners(*lnors_spacearr, processed_corners, space_index);
if (UNLIKELY(lnor_len == 0.0f)) {
/* Use vertex normal as fallback! */
copy_v3_v3(lnor, loop_normals[mlfan_vert_index]);
lnor_len = 1.0f;
}
*lnor_space = normal_fan_space_define(lnor, vec_org, vec_curr, *edge_vectors);
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
normal_fan_space_define(lnor_space, lnor, vec_org, vec_curr, *edge_vectors);
edge_vectors->clear();
if (!clnors_data.is_empty()) {
@ -1097,7 +1099,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
/* Extra bonus: since small-stack is local to this function,
* no more need to empty it at all cost! */
lnor = fan_space_custom_data_to_normal(lnor_space, lnor, *clnor_ref);
fan_space_custom_data_to_normal(lnor_space, lnor, *clnor_ref, lnor);
}
}
@ -1388,31 +1390,16 @@ void normals_calc_loop(const Span<float3> vert_positions,
threading::parallel_for(single_corners.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
const int corner = single_corners[i];
lnor_space_for_single_fan(
&common_data, corner, r_lnors_spacearr ? &r_lnors_spacearr->spaces[i] : nullptr);
if (r_lnors_spacearr) {
r_lnors_spacearr->corner_space_indices[corner] = i;
r_lnors_spacearr->corner_group_lists[corner] = -1;
}
lnor_space_for_single_fan(&common_data, corner, i);
}
});
threading::parallel_for(fan_corners.index_range(), 1024, [&](const IndexRange range) {
Vector<int, 8> processed_corners;
Vector<float3> edge_vectors;
for (const int i : range) {
const int corner = fan_corners[i];
const int space_index = single_corners.size() + i;
split_loop_nor_fan_do(&common_data,
corner,
processed_corners,
r_lnors_spacearr ? &r_lnors_spacearr->spaces[space_index] : nullptr,
&edge_vectors);
if (r_lnors_spacearr) {
r_lnors_spacearr->corner_space_indices.as_mutable_span().fill_indices(
processed_corners.as_span(), space_index);
}
processed_corners.clear();
split_loop_nor_fan_do(&common_data, corner, space_index, &edge_vectors);
}
});
@ -1539,8 +1526,8 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
* - In smooth fan case, we compare each clnor against a ref one,
* to avoid small differences adding up into a real big one in the end!
*/
int corner_link = lnors_spacearr.corner_group_lists[i];
if (corner_link == -1) {
int loop_link = lnors_spacearr.corner_group_lists[i];
if (loop_link == -1) {
done_loops[i].set();
continue;
}
@ -1548,8 +1535,8 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
int prev_corner = -1;
const float *org_nor = nullptr;
while (corner_link != -1) {
const int lidx = corner_link;
while (loop_link != -1) {
const int lidx = loop_link;
float *nor = r_custom_loop_normals[lidx];
if (!org_nor) {
@ -1571,7 +1558,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
}
prev_corner = lidx;
corner_link = lnors_spacearr.corner_group_lists[corner_link];
loop_link = lnors_spacearr.corner_group_lists[loop_link];
done_loops[lidx].set();
}
@ -1579,9 +1566,9 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
* otherwise we may miss some sharp edges here!
* This is just a simplified version of above while loop.
* See #45984. */
corner_link = lnors_spacearr.corner_group_lists[i];
if (corner_link != -1 && org_nor) {
const int lidx = corner_link;
loop_link = lnors_spacearr.corner_group_lists[i];
if (loop_link != -1 && org_nor) {
const int lidx = loop_link;
float *nor = r_custom_loop_normals[lidx];
if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
@ -1632,21 +1619,21 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
/* Note we accumulate and average all custom normals in current smooth fan,

These names read as generic face-corner information when they are quite spesific and only useful when used together.

That these values are referenced by the space index would make this clearer, e.g.
fan_corners_from_space_data
fan_corners_from_space_offset_indices

These names read as generic face-corner information when they are quite spesific and only useful when used together. That these values are referenced by the space index would make this clearer, e.g. `fan_corners_from_space_data` `fan_corners_from_space_offset_indices`
* to avoid getting different clnors data (tiny differences in plain custom normals can
* give rather huge differences in computed 2D factors). */
int corner_link = lnors_spacearr.corner_group_lists[i];
if (corner_link == -1) {
int loop_link = lnors_spacearr.corner_group_lists[i];
if (loop_link == -1) {
const int nidx = use_vertices ? corner_verts[i] : i;
float *nor = r_custom_loop_normals[nidx];
const int space_index = lnors_spacearr.corner_space_indices[i];
r_clnors_data[i] = fan_space_custom_normal_to_data(
&lnors_spacearr.spaces[space_index], loop_normals[i], nor);
fan_space_custom_normal_to_data(
&lnors_spacearr.spaces[space_index], loop_normals[i], nor, r_clnors_data[i]);
done_loops[i].reset();
}
else {
int avg_nor_count = 0;
float3 avg_nor(0.0f);
while (corner_link != -1) {
const int lidx = corner_link;
while (loop_link != -1) {
const int lidx = loop_link;
const int nidx = use_vertices ? corner_verts[lidx] : lidx;
float *nor = r_custom_loop_normals[nidx];
@ -1654,14 +1641,15 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
add_v3_v3(avg_nor, nor);
processed_corners.append(lidx);
corner_link = lnors_spacearr.corner_group_lists[corner_link];
loop_link = lnors_spacearr.corner_group_lists[loop_link];
done_loops[lidx].reset();
}
mul_v3_fl(avg_nor, 1.0f / float(avg_nor_count));
short2 clnor_data_tmp;
const int space_index = lnors_spacearr.corner_space_indices[i];
const short2 clnor_data_tmp = fan_space_custom_normal_to_data(
&lnors_spacearr.spaces[space_index], loop_normals[i], avg_nor);
fan_space_custom_normal_to_data(
&lnors_spacearr.spaces[space_index], loop_normals[i], avg_nor, clnor_data_tmp);
r_clnors_data.fill_indices(processed_corners.as_span(), clnor_data_tmp);
processed_corners.clear();