Cleanup: Simplify handling of loop to poly map in normal calculation

A Loop to poly map was passed as an optional output to the loop normal
calculation. That meant it was often recalculated more than necessary.
Instead, treat it as an optional argument. This also helps relieve
unnecessary responsibilities from the already-complicated loop normal
calculation code.
This commit is contained in:
Hans Goudey 2022-11-11 22:56:44 -06:00
parent d63ada602d
commit 1a8516163f
10 changed files with 61 additions and 63 deletions

View File

@ -621,6 +621,8 @@ void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space,
* Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
* Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry
* (splitting edges).
*
* \param loop_to_poly_map: Optional pre-created map from loops to their polygon.
*/
void BKE_mesh_normals_loop_split(const struct MVert *mverts,
const float (*vert_normals)[3],
@ -635,9 +637,9 @@ void BKE_mesh_normals_loop_split(const struct MVert *mverts,
int numPolys,
bool use_split_normals,
float split_angle,
const int *loop_to_poly_map,
MLoopNorSpaceArray *r_lnors_spacearr,
short (*clnors_data)[2],
int *r_loop_to_poly);
short (*clnors_data)[2]);
void BKE_mesh_normals_loop_custom_set(const struct MVert *mverts,
const float (*vert_normals)[3],

View File

@ -300,8 +300,8 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src,
use_split_nors_dst,
split_angle_dst,
NULL,
custom_nors_dst,
NULL);
NULL,
custom_nors_dst);
}
}
}

View File

@ -2296,8 +2296,8 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb,
(mesh->flag & ME_AUTOSMOOTH) != 0,
mesh->smoothresh,
NULL,
clnors,
NULL);
NULL,
clnors);
}
if (free_vert_normals) {

View File

@ -1829,9 +1829,9 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh,
polys.size(),
use_split_normals,
split_angle,
nullptr,
r_lnors_spacearr,
clnors,
nullptr);
clnors);
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
}

View File

@ -418,9 +418,9 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
totpoly,
true,
mesh->smoothresh,
NULL,
&lnors_spacearr,
clnors,
NULL);
clnors);
/* mirroring has to account for loops being reversed in polys in second half */
MPoly *result_polys = BKE_mesh_polys_for_write(result);

View File

@ -33,6 +33,7 @@
#include "BKE_editmesh_cache.h"
#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "atomic_ops.h"
@ -816,7 +817,7 @@ struct LoopSplitTaskDataCommon {
Span<MLoop> loops;
Span<MPoly> polys;
int (*edge_to_loops)[2];
MutableSpan<int> loop_to_poly;
Span<int> loop_to_poly;
Span<float3> polynors;
Span<float3> vert_normals;
};
@ -834,12 +835,12 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data,
MutableSpan<MEdge> edges = data->edges;
const Span<MPoly> polys = data->polys;
const Span<MLoop> loops = data->loops;
const Span<int> loop_to_poly = data->loop_to_poly;
MutableSpan<float3> loopnors = data->loopnors; /* NOTE: loopnors may be empty here. */
const Span<float3> polynors = data->polynors;
int(*edge_to_loops)[2] = data->edge_to_loops;
MutableSpan<int> loop_to_poly = data->loop_to_poly;
BitVector sharp_edges;
if (do_sharp_edges_tag) {
@ -859,8 +860,6 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data,
for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++) {
e2l = edge_to_loops[ml_curr->e];
loop_to_poly[ml_curr_index] = mp_index;
/* Pre-populate all loop normals as if their verts were all-smooth,
* this way we don't have to compute those later!
*/
@ -935,6 +934,8 @@ void BKE_edges_sharp_from_angle_set(const MVert *mverts,
const int numPolys,
const float split_angle)
{
using namespace blender;
using namespace blender::bke;
if (split_angle >= float(M_PI)) {
/* Nothing to do! */
return;
@ -945,7 +946,8 @@ void BKE_edges_sharp_from_angle_set(const MVert *mverts,
size_t(numEdges), sizeof(*edge_to_loops), __func__);
/* Simple mapping from a loop to its polygon index. */
int *loop_to_poly = (int *)MEM_malloc_arrayN(size_t(numLoops), sizeof(*loop_to_poly), __func__);
const Array<int> loop_to_poly = mesh_topology::build_loop_to_poly_map({mpolys, numPolys},
numLoops);
LoopSplitTaskDataCommon common_data = {};
common_data.verts = {mverts, numVerts};
@ -953,13 +955,12 @@ void BKE_edges_sharp_from_angle_set(const MVert *mverts,
common_data.polys = {mpolys, numPolys};
common_data.loops = {mloops, numLoops};
common_data.edge_to_loops = edge_to_loops;
common_data.loop_to_poly = {loop_to_poly, numLoops};
common_data.loop_to_poly = loop_to_poly;
common_data.polynors = {reinterpret_cast<const float3 *>(polynors), numPolys};
mesh_edges_sharp_tag(&common_data, true, split_angle, true);
MEM_freeN(edge_to_loops);
MEM_freeN(loop_to_poly);
}
static void loop_manifold_fan_around_vert_next(const Span<MLoop> loops,
@ -1564,10 +1565,12 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
const int numPolys,
const bool use_split_normals,
const float split_angle,
const int *loop_to_poly_map,
MLoopNorSpaceArray *r_lnors_spacearr,
short (*clnors_data)[2],
int *r_loop_to_poly)
short (*clnors_data)[2])
{
using namespace blender;
using namespace blender::bke;
/* For now this is not supported.
* If we do not use split normals, we do not generate anything fancy! */
BLI_assert(use_split_normals || !(r_lnors_spacearr));
@ -1588,9 +1591,6 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
const bool is_poly_flat = ((mp->flag & ME_SMOOTH) == 0);
for (; ml_index < ml_index_end; ml_index++) {
if (r_loop_to_poly) {
r_loop_to_poly[ml_index] = mp_index;
}
if (is_poly_flat) {
copy_v3_v3(r_loopnors[ml_index], polynors[mp_index]);
}
@ -1620,9 +1620,15 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
size_t(numEdges), sizeof(*edge_to_loops), __func__);
/* Simple mapping from a loop to its polygon index. */
int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly :
(int *)MEM_malloc_arrayN(
size_t(numLoops), sizeof(*loop_to_poly), __func__);
Span<int> loop_to_poly;
Array<int> local_loop_to_poly_map;
if (loop_to_poly_map) {
loop_to_poly = {loop_to_poly_map, numLoops};
}
else {
local_loop_to_poly_map = mesh_topology::build_loop_to_poly_map({mpolys, numPolys}, numLoops);
loop_to_poly = local_loop_to_poly_map;
}
/* When using custom loop normals, disable the angle feature! */
const bool check_angle = (split_angle < float(M_PI)) && (clnors_data == nullptr);
@ -1651,7 +1657,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
common_data.polys = {mpolys, numPolys};
common_data.loops = {mloops, numLoops};
common_data.edge_to_loops = edge_to_loops;
common_data.loop_to_poly = {loop_to_poly, numLoops};
common_data.loop_to_poly = loop_to_poly;
common_data.polynors = {reinterpret_cast<const float3 *>(polynors), numPolys};
common_data.vert_normals = {reinterpret_cast<const float3 *>(vert_normals), numVerts};
@ -1673,9 +1679,6 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
}
MEM_freeN(edge_to_loops);
if (!r_loop_to_poly) {
MEM_freeN(loop_to_poly);
}
if (r_lnors_spacearr) {
if (r_lnors_spacearr == &_lnors_spacearr) {
@ -1711,6 +1714,8 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
short (*r_clnors_data)[2],
const bool use_vertices)
{
using namespace blender;
using namespace blender::bke;
/* We *may* make that poor #BKE_mesh_normals_loop_split() even more complex by making it handling
* that feature too, would probably be more efficient in absolute.
* However, this function *is not* performance-critical, since it is mostly expected to be called
@ -1720,7 +1725,8 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
MLoopNorSpaceArray lnors_spacearr = {nullptr};
BitVector<> done_loops(numLoops, false);
float(*lnors)[3] = (float(*)[3])MEM_calloc_arrayN(size_t(numLoops), sizeof(*lnors), __func__);
int *loop_to_poly = (int *)MEM_malloc_arrayN(size_t(numLoops), sizeof(int), __func__);
const Array<int> loop_to_poly = mesh_topology::build_loop_to_poly_map({mpolys, numPolys},
numLoops);
/* In this case we always consider split nors as ON,
* and do not want to use angle to define smooth fans! */
const bool use_split_normals = true;
@ -1742,9 +1748,9 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
numPolys,
use_split_normals,
split_angle,
loop_to_poly.data(),
&lnors_spacearr,
nullptr,
loop_to_poly);
nullptr);
/* Set all given zero vectors to their default value. */
if (use_vertices) {
@ -1865,9 +1871,9 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
numPolys,
use_split_normals,
split_angle,
loop_to_poly.data(),
&lnors_spacearr,
nullptr,
loop_to_poly);
nullptr);
}
else {
done_loops.fill(true);
@ -1929,7 +1935,6 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
}
MEM_freeN(lnors);
MEM_freeN(loop_to_poly);
BKE_lnor_spacearr_free(&lnors_spacearr);
}

View File

@ -1372,8 +1372,8 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
use_split_nors_dst,
split_angle_dst,
NULL,
custom_nors_dst,
NULL);
NULL,
custom_nors_dst);
}
}
if (need_pnors_src || need_lnors_src) {

View File

@ -376,8 +376,8 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_
is_auto_smooth,
split_angle,
nullptr,
clnors,
nullptr);
nullptr,
clnors);
}
}
else {

View File

@ -563,8 +563,8 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
true,
result->smoothresh,
nullptr,
clnors,
nullptr);
nullptr,
clnors);
}
if (clnors == nullptr) {

View File

@ -23,6 +23,7 @@
#include "BKE_deform.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_screen.h"
#include "UI_interface.h"
@ -76,6 +77,7 @@ struct WeightedNormalData {
MEdge *medge;
const MLoop *mloop;
blender::Span<int> loop_to_poly;
short (*clnors)[2];
bool has_clnors; /* True if clnors already existed, false if we had to create them. */
float split_angle;
@ -97,8 +99,6 @@ struct WeightedNormalData {
WeightedNormalDataAggregateItem *items_data;
ModePair *mode_pair;
int *loop_to_poly;
};
/**
@ -181,6 +181,7 @@ static void aggregate_item_normal(WeightedNormalModifierData *wnmd,
static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
WeightedNormalData *wn_data)
{
using namespace blender;
const int verts_num = wn_data->verts_num;
const int edges_num = wn_data->edges_num;
const int loops_num = wn_data->loops_num;
@ -191,7 +192,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
const MLoop *mloop = wn_data->mloop;
short(*clnors)[2] = wn_data->clnors;
int *loop_to_poly = wn_data->loop_to_poly;
const Span<int> loop_to_poly = wn_data->loop_to_poly;
const MPoly *mpoly = wn_data->mpoly;
const float(*polynors)[3] = wn_data->polynors;
@ -235,9 +236,9 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
polys_num,
true,
split_angle,
loop_to_poly.data(),
&lnors_spacearr,
has_clnors ? clnors : nullptr,
loop_to_poly);
has_clnors ? clnors : nullptr);
items_num = lnors_spacearr.spaces_num;
items_data = static_cast<WeightedNormalDataAggregateItem *>(
@ -315,8 +316,6 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
break;
case MOD_WEIGHTEDNORMAL_MODE_ANGLE:
case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE:
BLI_assert(loop_to_poly != nullptr);
for (int i = 0; i < loops_num; i++) {
const int ml_index = mode_pair[i].index;
const float ml_val = mode_pair[i].val;
@ -419,9 +418,9 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
polys_num,
true,
split_angle,
loop_to_poly.data(),
nullptr,
has_clnors ? clnors : nullptr,
loop_to_poly);
has_clnors ? clnors : nullptr);
for (int ml_index = 0; ml_index < loops_num; ml_index++) {
const int item_index = mloop[ml_index].v;
@ -489,9 +488,6 @@ static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData
const MPoly *mp;
int mp_index;
int *loop_to_poly = static_cast<int *>(
MEM_malloc_arrayN(size_t(loops_num), sizeof(*loop_to_poly), __func__));
ModePair *corner_angle = static_cast<ModePair *>(
MEM_malloc_arrayN(size_t(loops_num), sizeof(*corner_angle), __func__));
@ -508,15 +504,12 @@ static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData
ml_index++, c_angl++, angl++) {
c_angl->val = float(M_PI) - *angl;
c_angl->index = ml_index;
loop_to_poly[ml_index] = mp_index;
}
MEM_freeN(index_angle);
}
qsort(corner_angle, loops_num, sizeof(*corner_angle), modepair_cmp_by_val_inverse);
wn_data->loop_to_poly = loop_to_poly;
wn_data->mode_pair = corner_angle;
apply_weights_vertex_normal(wnmd, wn_data);
}
@ -533,9 +526,6 @@ static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalD
const MPoly *mp;
int mp_index;
int *loop_to_poly = static_cast<int *>(
MEM_malloc_arrayN(size_t(loops_num), sizeof(*loop_to_poly), __func__));
ModePair *combined = static_cast<ModePair *>(
MEM_malloc_arrayN(size_t(loops_num), sizeof(*combined), __func__));
@ -554,21 +544,19 @@ static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalD
/* In this case val is product of corner angle and face area. */
cmbnd->val = (float(M_PI) - *angl) * face_area;
cmbnd->index = ml_index;
loop_to_poly[ml_index] = mp_index;
}
MEM_freeN(index_angle);
}
qsort(combined, loops_num, sizeof(*combined), modepair_cmp_by_val_inverse);
wn_data->loop_to_poly = loop_to_poly;
wn_data->mode_pair = combined;
apply_weights_vertex_normal(wnmd, wn_data);
}
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
using namespace blender;
WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
Object *ob = ctx->object;
@ -633,6 +621,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
int defgrp_index;
MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index);
const Array<int> loop_to_poly_map = bke::mesh_topology::build_loop_to_poly_map(result->polys(),
result->totloop);
WeightedNormalData wn_data{};
wn_data.verts_num = verts_num;
wn_data.edges_num = edges_num;
@ -644,6 +635,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
wn_data.medge = medge;
wn_data.mloop = mloop;
wn_data.loop_to_poly = loop_to_poly_map;
wn_data.clnors = clnors;
wn_data.has_clnors = has_clnors;
wn_data.split_angle = split_angle;
@ -672,7 +664,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
break;
}
MEM_SAFE_FREE(wn_data.loop_to_poly);
MEM_SAFE_FREE(wn_data.mode_pair);
MEM_SAFE_FREE(wn_data.items_data);