Commit: a16659efb69570fa376b7a2fa926e18bdbdc6b5a Author: Hans Goudey Date: Sat Sep 4 14:54:42 2021 -0500 Branches: temp-vert-normals-cleanup https://developer.blender.org/rBa16659efb69570fa376b7a2fa926e18bdbdc6b5a
Cleanup: Use C++ types and lambdas for mesh normal calculation - Use `threading::parallel_for` for multithreading, for a simpler API, more readable and concise code. - Use `Span` and `Array` (only internally, the public API is still C) for safer, more automatic memory management. - Since code is much less verbose, combine the callbacks into the main function. Note that the accumulation code could be more concise with `float3`, I just wanted to keep these changes minimal. Differential Revision: https://developer.blender.org/D12402 =================================================================== M source/blender/blenkernel/intern/mesh_normals.cc =================================================================== diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 9a761c6fa11..e6df3161fae 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -35,12 +35,16 @@ #include "BLI_alloca.h" #include "BLI_bitmap.h" +#include "BLI_array.hh" +#include "BLI_float3.hh" #include "BLI_linklist.h" #include "BLI_linklist_stack.h" #include "BLI_math.h" #include "BLI_memarena.h" +#include "BLI_span.hh" #include "BLI_stack.h" #include "BLI_task.h" +#include "BLI_task.hh" #include "BLI_utildefines.h" #include "BKE_customdata.h" @@ -50,6 +54,13 @@ #include "atomic_ops.h" +using blender::Array; +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; +using namespace blender::threading; + // #define DEBUG_TIME #ifdef DEBUG_TIME @@ -115,24 +126,6 @@ void BKE_mesh_normals_tag_dirty(Mesh *mesh) /** \name Mesh Normal Calculation (Polygons) * \{ */ -struct MeshCalcNormalsData_Poly { - const MVert *mvert; - const MLoop *mloop; - const MPoly *mpoly; - - /** Polygon normal output. */ - float (*pnors)[3]; -}; - -static void mesh_calc_normals_poly_fn(void *__restrict userdata, - const int pidx, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - const MeshCalcNormalsData_Poly *data = (MeshCalcNormalsData_Poly *)userdata; - const MPoly *mp = &data->mpoly[pidx]; - BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mvert, data->pnors[pidx]); -} - void BKE_mesh_calc_normals_poly(const MVert *mvert, int UNUSED(mvert_len), const MLoop *mloop, @@ -141,19 +134,14 @@ void BKE_mesh_calc_normals_poly(const MVert *mvert, int mpoly_len, float (*r_poly_normals)[3]) { - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.min_iter_per_thread = 1024; - BLI_assert((r_poly_normals != nullptr) || (mpoly_len == 0)); - MeshCalcNormalsData_Poly data = {}; - data.mpoly = mpoly; - data.mloop = mloop; - data.mvert = mvert; - data.pnors = r_poly_normals; - - BLI_task_parallel_range(0, mpoly_len, &data, mesh_calc_normals_poly_fn, &settings); + blender::threading::parallel_for(IndexRange(mpoly_len), 1024, [&](IndexRange range) { + for (const int i : range) { + const MPoly &poly = mpoly[i]; + BKE_mesh_calc_poly_normal(&poly, &mloop[poly.loopstart], mvert, r_poly_normals[i]); + } + }); } /** \} */ @@ -167,139 +155,108 @@ void BKE_mesh_calc_normals_poly(const MVert *mvert, * meshes can slow down high-poly meshes. For details on performance, see D11993. * \{ */ -struct MeshCalcNormalsData_PolyAndVertex { - /** Write into vertex normals #MVert.no. */ - MVert *mvert; - const MLoop *mloop; - const MPoly *mpoly; - - /** Polygon normal output. */ - float (*pnors)[3]; - /** Vertex normal output (may be freed, copied into #MVert.no). */ - float (*vnors)[3]; -}; - -static void mesh_calc_normals_poly_and_vertex_accum_fn( - void *__restrict userdata, const int pidx, const TaskParallelTLS *__restrict UNUSED(tls)) +static void mesh_calc_normals_poly_and_vertex(MutableSpan<MVert> mverts, + Span<MLoop> loops, + Span<MPoly> polys, + MutableSpan<float3> poly_normals, + MutableSpan<float3> vert_normals) { - const MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata; - const MPoly *mp = &data->mpoly[pidx]; - const MLoop *ml = &data->mloop[mp->loopstart]; - const MVert *mverts = data->mvert; - float(*vnors)[3] = data->vnors; - - float pnor_temp[3]; - float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp; + Array<float3> vert_normals_internal; + if (vert_normals.is_empty()) { + vert_normals_internal.reinitialize(mverts.size()); + vert_normals = vert_normals_internal; + } + + vert_normals.fill(float3(0)); + + /* Compute poly normals, accumulating them into vertex normals. */ + blender::threading::parallel_for(polys.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + const MPoly &poly = polys[i]; + Span<MLoop> ml = loops.slice(poly.loopstart, poly.totloop); + + float3 pnor_temp; + float3 &pnor = poly_normals.is_empty() ? pnor_temp : poly_normals[i]; + + const int i_end = poly.totloop - 1; + + /* Polygon Normal and edge-vector. */ + /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */ + { + zero_v3(pnor); + /* Newell's Method */ + const float *v_curr = mverts[ml[i_end].v].co; + for (int i_next = 0; i_next <= i_end; i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + add_newell_cross_v3_v3v3(pnor, v_curr, v_next); + v_curr = v_next; + } + if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { + pnor[2] = 1.0f; /* Other axes set to zero. */ + } + } - const int i_end = mp->totloop - 1; + /* Accumulate angle weighted face normal into the vertex normal. */ + /* Inline version of #accumulate_vertex_normals_poly_v3. */ + { + float edvec_prev[3], edvec_next[3], edvec_end[3]; + const float *v_curr = mverts[ml[i_end].v].co; + sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr); + normalize_v3(edvec_prev); + copy_v3_v3(edvec_end, edvec_prev); + + for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + + /* Skip an extra normalization by reusing the first calculated edge. */ + if (i_next != i_end) { + sub_v3_v3v3(edvec_next, v_curr, v_next); + normalize_v3(edvec_next); + } + else { + copy_v3_v3(edvec_next, edvec_end); + } - /* Polygon Normal and edge-vector. */ - /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */ - { - zero_v3(pnor); - /* Newell's Method */ - const float *v_curr = mverts[ml[i_end].v].co; - for (int i_next = 0; i_next <= i_end; i_next++) { - const float *v_next = mverts[ml[i_next].v].co; - add_newell_cross_v3_v3v3(pnor, v_curr, v_next); - v_curr = v_next; - } - if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { - pnor[2] = 1.0f; /* Other axes set to zero. */ - } - } + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); + const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; - /* Accumulate angle weighted face normal into the vertex normal. */ - /* Inline version of #accumulate_vertex_normals_poly_v3. */ - { - float edvec_prev[3], edvec_next[3], edvec_end[3]; - const float *v_curr = mverts[ml[i_end].v].co; - sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr); - normalize_v3(edvec_prev); - copy_v3_v3(edvec_end, edvec_prev); - - for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { - const float *v_next = mverts[ml[i_next].v].co; - - /* Skip an extra normalization by reusing the first calculated edge. */ - if (i_next != i_end) { - sub_v3_v3v3(edvec_next, v_curr, v_next); - normalize_v3(edvec_next); + add_v3_v3_atomic(vert_normals[ml[i_curr].v], vnor_add); + v_curr = v_next; + copy_v3_v3(edvec_prev, edvec_next); + } } - else { - copy_v3_v3(edvec_next, edvec_end); + } + }); + + /* Normalize and validate computed vertex normals. */ + blender::threading::parallel_for(mverts.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + MVert &vert = mverts[i]; + float3 &no = vert_normals[i]; + if (UNLIKELY(normalize_v3(no) == 0.0f)) { + /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */ + normalize_v3_v3(no, vert.co); } - - /* Calculate angle between the two poly edges incident on this vertex. */ - const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); - const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; - - add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); - v_curr = v_next; - copy_v3_v3(edvec_prev, edvec_next); + normal_float_to_short_v3(vert.no, no); } - } -} - -static void mesh_calc_normals_poly_and_vertex_finalize_fn( - void *__restrict userdata, const int vidx, const TaskParallelTLS *__restrict UNUSED(tls)) -{ - MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata; - - MVert *mv = &data->mvert[vidx]; - float *no = data->vnors[vidx]; - - if (UNLIKELY(normalize_v3(no) == 0.0f)) { - /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */ - normalize_v3_v3(no, mv->co); - } - - normal_float_to_short_v3(mv->no, no); + }); } void BKE_mesh_calc_normals_poly_and_vertex(MVert *mvert, const int mvert_len, const MLoop *mloop, - const int UNUSED(mloop_len), + const int mloop_len, const MPoly *mpoly, const int mpoly_len, float (*r_poly_normals)[3], float (*r_vert_normals)[3]) { - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.min_iter_per_thread = 1024; - - float(*vnors)[3] = r_vert_normals; - bool free_vnors = false; - - /* First go through and calculate normals for all the polys. */ - if (vnors == nullptr) { - vnors = (floa @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs