Mesh: Reduce custom normal calculation memory usage #107592
|
@ -68,7 +68,6 @@ struct NormalFanSpace {
|
||||||
float ref_alpha;
|
float ref_alpha;
|
||||||
/** Reference angle, around vec_lnor, in [0, 2pi] range (0.0 marks that space as invalid). */
|
/** Reference angle, around vec_lnor, in [0, 2pi] range (0.0 marks that space as invalid). */
|
||||||
float ref_beta;
|
float ref_beta;
|
||||||
Vector<int> corners;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,20 +76,14 @@ struct NormalFanSpace {
|
||||||
struct MeshNormalFanSpaces {
|
struct MeshNormalFanSpaces {
|
||||||
/**
|
/**
|
||||||
* The normal coordinate spaces, potentially shared between multiple face corners in a smooth fan
|
* The normal coordinate spaces, potentially shared between multiple face corners in a smooth fan
|
||||||
* connected to a vertex. Depending on the geometry (the amount of sharing / size of each fan),
|
* connected to a vertex. Depending on the mesh (the amount of sharing / number of sharp edges /
|
||||||
* there may be many fewer spaces than face corners, so they are stored in a separate array.
|
* size of each fan), there may be many fewer spaces than face corners, so they are stored in a
|
||||||
|
* separate array.
|
||||||
*/
|
*/
|
||||||
Array<NormalFanSpace> spaces;
|
Array<NormalFanSpace> spaces;
|
||||||
|
|
||||||
/** The index in #spaces for each face corner. */
|
/** The index of the data in the #spaces array for each face corner. */
|
||||||
Array<int> corner_space_indices;
|
Array<int> corner_space_indices;
|
||||||
|
|
||||||
/**
|
|
||||||
* An index-based linked list of the face corners in each smooth fan, starting at each corner.
|
|
||||||
* Each index stores the index of the next corner in the fan, or -1 if it is the last. "Single"
|
|
||||||
* fans with only a single corner store -1 at that corner index directly.
|
|
||||||
*/
|
|
||||||
Array<int> corner_group_lists;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space,
|
void fan_space_custom_normal_to_data(const NormalFanSpace *lnor_space,
|
||||||
|
|
|
@ -879,17 +879,6 @@ 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,
|
static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
|
||||||
const int ml_curr_index,
|
const int ml_curr_index,
|
||||||
const int space_index)
|
const int space_index)
|
||||||
|
@ -925,8 +914,7 @@ static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
|
||||||
|
|
||||||
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
|
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
|
||||||
normal_fan_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, {});
|
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. */
|
lnors_spacearr->corner_space_indices[ml_curr_index] = space_index;
|
||||||
fan_space_add_corners(*lnors_spacearr, {ml_curr_index}, space_index);
|
|
||||||
|
|
||||||
if (!clnors_data.is_empty()) {
|
if (!clnors_data.is_empty()) {
|
||||||
fan_space_custom_data_to_normal(lnor_space,
|
fan_space_custom_data_to_normal(lnor_space,
|
||||||
|
@ -1078,16 +1066,16 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
|
||||||
* and optionally compute final lnor from custom data too!
|
* and optionally compute final lnor from custom data too!
|
||||||
*/
|
*/
|
||||||
if (lnors_spacearr) {
|
if (lnors_spacearr) {
|
||||||
fan_space_add_corners(*lnors_spacearr, processed_corners, space_index);
|
|
||||||
if (UNLIKELY(length == 0.0f)) {
|
if (UNLIKELY(length == 0.0f)) {
|
||||||
/* Use vertex normal as fallback! */
|
/* Use vertex normal as fallback! */
|
||||||
copy_v3_v3(lnor, loop_normals[mlfan_vert_index]);
|
lnor = loop_normals[mlfan_vert_index];
|
||||||
length = 1.0f;
|
length = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
|
NormalFanSpace *lnor_space = &lnors_spacearr->spaces[space_index];
|
||||||
normal_fan_space_define(lnor_space, lnor, vec_org, vec_curr, *edge_vectors);
|
normal_fan_space_define(lnor_space, lnor, vec_org, vec_curr, *edge_vectors);
|
||||||
lnor_space->corners = std::move(processed_corners);
|
lnors_spacearr->corner_space_indices.as_mutable_span().fill_indices(
|
||||||
|
processed_corners.as_span(), space_index);
|
||||||
edge_vectors->clear();
|
edge_vectors->clear();
|
||||||
|
|
||||||
if (!clnors_data.is_empty()) {
|
if (!clnors_data.is_empty()) {
|
||||||
|
@ -1098,11 +1086,14 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
|
||||||
if (G.debug & G_DEBUG) {
|
if (G.debug & G_DEBUG) {
|
||||||
printf("Invalid clnors in this fan!\n");
|
printf("Invalid clnors in this fan!\n");
|
||||||
}
|
}
|
||||||
/* Extra bonus: since small-stack is local to this function,
|
clnors_data.fill_indices(processed_corners.as_span(),
|
||||||
* no more need to empty it at all cost! */
|
short2(clnors_avg[0], clnors_avg[1]));
|
||||||
|
// print_v2("new clnors", clnors_avg);
|
||||||
fan_space_custom_data_to_normal(lnor_space, lnor, *clnor_ref, lnor);
|
|
||||||
}
|
}
|
||||||
|
/* Extra bonus: since small-stack is local to this function,
|
||||||
|
* no more need to empty it at all cost! */
|
||||||
|
|
||||||
|
fan_space_custom_data_to_normal(lnor_space, lnor, *clnor_ref, lnor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1387,7 +1378,6 @@ void normals_calc_loop(const Span<float3> vert_positions,
|
||||||
|
|
||||||
if (r_lnors_spacearr) {
|
if (r_lnors_spacearr) {
|
||||||
r_lnors_spacearr->spaces.reinitialize(single_corners.size() + fan_corners.size());
|
r_lnors_spacearr->spaces.reinitialize(single_corners.size() + fan_corners.size());
|
||||||
r_lnors_spacearr->corner_group_lists.reinitialize(corner_verts.size());
|
|
||||||
r_lnors_spacearr->corner_space_indices = Array<int>(corner_verts.size(), -1);
|
r_lnors_spacearr->corner_space_indices = Array<int>(corner_verts.size(), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1418,13 +1408,13 @@ void normals_calc_loop(const Span<float3> vert_positions,
|
||||||
std::cout << " vec_ortho: " << space.vec_ortho << '\n';
|
std::cout << " vec_ortho: " << space.vec_ortho << '\n';
|
||||||
std::cout << " ref_alpha: " << space.ref_alpha << '\n';
|
std::cout << " ref_alpha: " << space.ref_alpha << '\n';
|
||||||
std::cout << " ref_beta: " << space.ref_beta << '\n';
|
std::cout << " ref_beta: " << space.ref_beta << '\n';
|
||||||
std::cout << " fan indices: (";
|
// std::cout << " fan indices: (";
|
||||||
|
|||||||
int iter = r_lnors_spacearr->corner_group_lists[corner];
|
// int iter = r_lnors_spacearr->corner_group_lists[corner];
|
||||||
while (iter != -1) {
|
// while (iter != -1) {
|
||||||
std::cout << iter << ", ";
|
// std::cout << iter << ", ";
|
||||||
iter = r_lnors_spacearr->corner_group_lists[iter];
|
// iter = r_lnors_spacearr->corner_group_lists[iter];
|
||||||
}
|
// }
|
||||||
std::cout << ")\n";
|
// std::cout << ")\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1433,6 +1423,24 @@ void normals_calc_loop(const Span<float3> vert_positions,
|
||||||
#undef INDEX_INVALID
|
#undef INDEX_INVALID
|
||||||
#undef IS_EDGE_SHARP
|
#undef IS_EDGE_SHARP
|
||||||
|
|
||||||
|
static void reverse_space_index_array(const MeshNormalFanSpaces &spaces,
|
||||||
|
Array<int> &corner_offset_indices,
|
||||||
|
Array<int> &space_corner_indices)
|
||||||
|
{
|
||||||
|
corner_offset_indices = Array<int>(spaces.spaces.size() + 1, 0);
|
||||||
|
for (const int index : spaces.corner_space_indices) {
|
||||||
|
corner_offset_indices[index]++;
|
||||||
|
}
|
||||||
|
offset_indices::accumulate_counts_to_offsets(corner_offset_indices);
|
||||||
|
|
||||||
|
space_corner_indices.reinitialize(corner_offset_indices.last());
|
||||||
|
Array<int> count(spaces.spaces.size(), 0);
|
||||||
|
for (const int corner : spaces.corner_space_indices.index_range()) {
|
||||||
|
const int space = spaces.corner_space_indices[corner];
|
||||||
|
space_corner_indices[corner_offset_indices[space] + count[space]] = corner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute internal representation of given custom normals (as an array of float[2]).
|
* Compute internal representation of given custom normals (as an array of float[2]).
|
||||||
* It also makes sure the mesh matches those custom normals, by setting sharp edges flag as needed
|
* It also makes sure the mesh matches those custom normals, by setting sharp edges flag as needed
|
||||||
|
@ -1513,6 +1521,11 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
done_loops.fill(true);
|
done_loops.fill(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Array<int> space_corner_offset_indices;
|
||||||
|
Array<int> space_corner_indices;
|
||||||
|
reverse_space_index_array(lnors_spacearr, space_corner_offset_indices, space_corner_indices);
|
||||||
|
const OffsetIndices<int> space_corner_offsets(space_corner_offset_indices);
|
||||||
|
|
||||||
for (const int i : corner_verts.index_range()) {
|
for (const int i : corner_verts.index_range()) {
|
||||||
if (lnors_spacearr.corner_space_indices[i] == -1) {
|
if (lnors_spacearr.corner_space_indices[i] == -1) {
|
||||||
/* This should not happen in theory, but in some rare case (probably ugly geometry)
|
/* This should not happen in theory, but in some rare case (probably ugly geometry)
|
||||||
|
@ -1528,6 +1541,10 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int space_index = lnors_spacearr.corner_space_indices[i];
|
||||||
|
const Span<int> space_corners = space_corner_indices.as_span().slice(
|
||||||
|
space_corner_offsets[space_index]);
|
||||||
|
|
||||||
/* Notes:
|
/* Notes:
|
||||||
* - In case of mono-loop smooth fan, we have nothing to do.
|
* - In case of mono-loop smooth fan, we have nothing to do.
|
||||||
* - Loops in this linklist are ordered (in reversed order compared to how they were
|
* - Loops in this linklist are ordered (in reversed order compared to how they were
|
||||||
|
@ -1537,8 +1554,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
* - In smooth fan case, we compare each clnor against a ref one,
|
* - 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!
|
* to avoid small differences adding up into a real big one in the end!
|
||||||
*/
|
*/
|
||||||
int loop_link = lnors_spacearr.corner_group_lists[i];
|
if (space_corners.is_empty()) {
|
||||||
if (loop_link == -1) {
|
|
||||||
done_loops[i].set();
|
done_loops[i].set();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1546,8 +1562,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
int prev_corner = -1;
|
int prev_corner = -1;
|
||||||
const float *org_nor = nullptr;
|
const float *org_nor = nullptr;
|
||||||
|
|
||||||
while (loop_link != -1) {
|
for (const int lidx : space_corners) {
|
||||||
const int lidx = loop_link;
|
|
||||||
float *nor = r_custom_loop_normals[lidx];
|
float *nor = r_custom_loop_normals[lidx];
|
||||||
|
|
||||||
if (!org_nor) {
|
if (!org_nor) {
|
||||||
|
@ -1569,7 +1584,6 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_corner = lidx;
|
prev_corner = lidx;
|
||||||
loop_link = lnors_spacearr.corner_group_lists[loop_link];
|
|
||||||
done_loops[lidx].set();
|
done_loops[lidx].set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1612,7 +1626,10 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
loop_normals);
|
loop_normals);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<int> processed_corners;
|
Array<int> space_corner_offset_indices;
|
||||||
|
Array<int> space_corner_indices;
|
||||||
|
reverse_space_index_array(lnors_spacearr, space_corner_offset_indices, space_corner_indices);
|
||||||
|
const OffsetIndices<int> space_corner_offsets(space_corner_offset_indices);
|
||||||
|
|
||||||
/* And we just have to convert plain object-space custom normals to our
|
/* And we just have to convert plain object-space custom normals to our
|
||||||
* lnor space-encoded ones. */
|
* lnor space-encoded ones. */
|
||||||
|
@ -1626,6 +1643,8 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int space_index = lnors_spacearr.corner_space_indices[i];
|
||||||
|
|
||||||
if (done_loops[i]) {
|
if (done_loops[i]) {
|
||||||
/* Note we accumulate and average all custom normals in current smooth fan,
|
/* Note we accumulate and average all custom normals in current smooth fan,
|
||||||
* to avoid getting different clnors data (tiny differences in plain custom normals can
|
* to avoid getting different clnors data (tiny differences in plain custom normals can
|
||||||
|
@ -1650,7 +1669,6 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
|
|
||||||
avg_nor_count++;
|
avg_nor_count++;
|
||||||
add_v3_v3(avg_nor, nor);
|
add_v3_v3(avg_nor, nor);
|
||||||
processed_corners.append(lidx);
|
|
||||||
|
|
||||||
loop_link = lnors_spacearr.corner_group_lists[loop_link];
|
loop_link = lnors_spacearr.corner_group_lists[loop_link];
|
||||||
done_loops[lidx].reset();
|
done_loops[lidx].reset();
|
||||||
|
@ -1658,7 +1676,6 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
|
||||||
|
|
||||||
mul_v3_fl(avg_nor, 1.0f / float(avg_nor_count));
|
mul_v3_fl(avg_nor, 1.0f / float(avg_nor_count));
|
||||||
short2 clnor_data_tmp;
|
short2 clnor_data_tmp;
|
||||||
const int space_index = lnors_spacearr.corner_space_indices[i];
|
|
||||||
fan_space_custom_normal_to_data(
|
fan_space_custom_normal_to_data(
|
||||||
&lnors_spacearr.spaces[space_index], loop_normals[i], avg_nor, clnor_data_tmp);
|
&lnors_spacearr.spaces[space_index], loop_normals[i], avg_nor, clnor_data_tmp);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
It's not obvious what
r_offsets
&r_reverse_indices
represent, which should be stated in plan text (in this doc-string).Or, wrap
reverse_index_array
in a function which takeslnors_spacearr
and include a description of what the values represent there.Either way noting that
fan_corners_data
&fan_corner_offset_indices
represent inline isn't practical as they're declared twice.