Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/mesh_normals.cc
| Show First 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | |||||
| /* | /* | ||||
| * COMPUTE POLY NORMAL | * COMPUTE POLY NORMAL | ||||
| * | * | ||||
| * Computes the normal of a planar | * Computes the normal of a planar | ||||
| * polygon See Graphics Gems for | * polygon See Graphics Gems for | ||||
| * computing newell normal. | * computing newell normal. | ||||
| */ | */ | ||||
| static void mesh_calc_ngon_normal(const MPoly *mpoly, | static void mesh_calc_ngon_normal(const MPoly *mpoly, | ||||
| const MLoop *loopstart, | const int *poly_verts, | ||||
| const float (*positions)[3], | const float (*positions)[3], | ||||
| float r_normal[3]) | float r_normal[3]) | ||||
| { | { | ||||
| const int nverts = mpoly->totloop; | const int nverts = mpoly->totloop; | ||||
| const float *v_prev = positions[loopstart[nverts - 1].v]; | const float *v_prev = positions[poly_verts[nverts - 1]]; | ||||
| const float *v_curr; | const float *v_curr; | ||||
| zero_v3(r_normal); | zero_v3(r_normal); | ||||
| /* Newell's Method */ | /* Newell's Method */ | ||||
| for (int i = 0; i < nverts; i++) { | for (int i = 0; i < nverts; i++) { | ||||
| v_curr = positions[loopstart[i].v]; | v_curr = positions[poly_verts[i]]; | ||||
| add_newell_cross_v3_v3v3(r_normal, v_prev, v_curr); | add_newell_cross_v3_v3v3(r_normal, v_prev, v_curr); | ||||
| v_prev = v_curr; | v_prev = v_curr; | ||||
| } | } | ||||
| if (UNLIKELY(normalize_v3(r_normal) == 0.0f)) { | if (UNLIKELY(normalize_v3(r_normal) == 0.0f)) { | ||||
| r_normal[2] = 1.0f; /* other axis set to 0.0 */ | r_normal[2] = 1.0f; /* other axis set to 0.0 */ | ||||
| } | } | ||||
| } | } | ||||
| void BKE_mesh_calc_poly_normal(const MPoly *mpoly, | void BKE_mesh_calc_poly_normal(const MPoly *mpoly, | ||||
| const MLoop *loopstart, | const int *poly_verts, | ||||
| const float (*vert_positions)[3], | const float (*vert_positions)[3], | ||||
| float r_no[3]) | float r_no[3]) | ||||
| { | { | ||||
| if (mpoly->totloop > 4) { | if (mpoly->totloop > 4) { | ||||
| mesh_calc_ngon_normal(mpoly, loopstart, vert_positions, r_no); | mesh_calc_ngon_normal(mpoly, poly_verts, vert_positions, r_no); | ||||
| } | } | ||||
| else if (mpoly->totloop == 3) { | else if (mpoly->totloop == 3) { | ||||
| normal_tri_v3(r_no, | normal_tri_v3(r_no, | ||||
| vert_positions[loopstart[0].v], | vert_positions[poly_verts[0]], | ||||
| vert_positions[loopstart[1].v], | vert_positions[poly_verts[1]], | ||||
| vert_positions[loopstart[2].v]); | vert_positions[poly_verts[2]]); | ||||
| } | } | ||||
| else if (mpoly->totloop == 4) { | else if (mpoly->totloop == 4) { | ||||
| normal_quad_v3(r_no, | normal_quad_v3(r_no, | ||||
| vert_positions[loopstart[0].v], | vert_positions[poly_verts[0]], | ||||
| vert_positions[loopstart[1].v], | vert_positions[poly_verts[1]], | ||||
| vert_positions[loopstart[2].v], | vert_positions[poly_verts[2]], | ||||
| vert_positions[loopstart[3].v]); | vert_positions[poly_verts[3]]); | ||||
| } | } | ||||
| else { /* horrible, two sided face! */ | else { /* horrible, two sided face! */ | ||||
| r_no[0] = 0.0; | r_no[0] = 0.0; | ||||
| r_no[1] = 0.0; | r_no[1] = 0.0; | ||||
| r_no[2] = 1.0; | r_no[2] = 1.0; | ||||
| } | } | ||||
| } | } | ||||
| static void calculate_normals_poly(const Span<float3> positions, | static void calculate_normals_poly(const Span<float3> positions, | ||||
| const Span<MPoly> polys, | const Span<MPoly> polys, | ||||
| const Span<MLoop> loops, | const Span<int> corner_verts, | ||||
| MutableSpan<float3> poly_normals) | MutableSpan<float3> poly_normals) | ||||
| { | { | ||||
| using namespace blender; | using namespace blender; | ||||
| threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { | threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { | ||||
| for (const int poly_i : range) { | for (const int poly_i : range) { | ||||
| const MPoly &poly = polys[poly_i]; | const MPoly &poly = polys[poly_i]; | ||||
| BKE_mesh_calc_poly_normal(&poly, | BKE_mesh_calc_poly_normal(&poly, | ||||
| &loops[poly.loopstart], | &corner_verts[poly.loopstart], | ||||
| reinterpret_cast<const float(*)[3]>(positions.data()), | reinterpret_cast<const float(*)[3]>(positions.data()), | ||||
| poly_normals[poly_i]); | poly_normals[poly_i]); | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| void BKE_mesh_calc_normals_poly(const float (*vert_positions)[3], | void BKE_mesh_calc_normals_poly(const float (*vert_positions)[3], | ||||
| const int verts_num, | const int verts_num, | ||||
| const MLoop *mloop, | const int *corner_verts, | ||||
| const int mloop_len, | const int mloop_len, | ||||
| const MPoly *mpoly, | const MPoly *mpoly, | ||||
| int mpoly_len, | int mpoly_len, | ||||
| float (*r_poly_normals)[3]) | float (*r_poly_normals)[3]) | ||||
| { | { | ||||
| calculate_normals_poly({reinterpret_cast<const float3 *>(vert_positions), verts_num}, | calculate_normals_poly({reinterpret_cast<const float3 *>(vert_positions), verts_num}, | ||||
| {mpoly, mpoly_len}, | {mpoly, mpoly_len}, | ||||
| {mloop, mloop_len}, | {corner_verts, mloop_len}, | ||||
| {reinterpret_cast<float3 *>(r_poly_normals), mpoly_len}); | {reinterpret_cast<float3 *>(r_poly_normals), mpoly_len}); | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Mesh Normal Calculation (Polygons & Vertices) | /** \name Mesh Normal Calculation (Polygons & Vertices) | ||||
| * | * | ||||
| * Take care making optimizations to this function as improvements to low-poly | * Take care making optimizations to this function as improvements to low-poly | ||||
| * meshes can slow down high-poly meshes. For details on performance, see D11993. | * meshes can slow down high-poly meshes. For details on performance, see D11993. | ||||
| * \{ */ | * \{ */ | ||||
| static void calculate_normals_poly_and_vert(const Span<float3> positions, | static void calculate_normals_poly_and_vert(const Span<float3> positions, | ||||
| const Span<MPoly> polys, | const Span<MPoly> polys, | ||||
| const Span<MLoop> loops, | const Span<int> corner_verts, | ||||
| MutableSpan<float3> poly_normals, | MutableSpan<float3> poly_normals, | ||||
| MutableSpan<float3> vert_normals) | MutableSpan<float3> vert_normals) | ||||
| { | { | ||||
| using namespace blender; | using namespace blender; | ||||
| /* Zero the vertex normal array for accumulation. */ | /* Zero the vertex normal array for accumulation. */ | ||||
| { | { | ||||
| memset(vert_normals.data(), 0, vert_normals.as_span().size_in_bytes()); | memset(vert_normals.data(), 0, vert_normals.as_span().size_in_bytes()); | ||||
| } | } | ||||
| /* Compute poly normals, accumulating them into vertex normals. */ | /* Compute poly normals, accumulating them into vertex normals. */ | ||||
| { | { | ||||
| threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { | threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { | ||||
| for (const int poly_i : range) { | for (const int poly_i : range) { | ||||
| const MPoly &poly = polys[poly_i]; | const MPoly &poly = polys[poly_i]; | ||||
| const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); | const Span<int> poly_verts = corner_verts.slice(poly.loopstart, poly.totloop); | ||||
| float3 &pnor = poly_normals[poly_i]; | float3 &pnor = poly_normals[poly_i]; | ||||
| const int i_end = poly.totloop - 1; | const int i_end = poly.totloop - 1; | ||||
| /* Polygon Normal and edge-vector. */ | /* Polygon Normal and edge-vector. */ | ||||
| /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */ | /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */ | ||||
| { | { | ||||
| zero_v3(pnor); | zero_v3(pnor); | ||||
| /* Newell's Method */ | /* Newell's Method */ | ||||
| const float *v_curr = positions[poly_loops[i_end].v]; | const float *v_curr = positions[poly_verts[i_end]]; | ||||
| for (int i_next = 0; i_next <= i_end; i_next++) { | for (int i_next = 0; i_next <= i_end; i_next++) { | ||||
| const float *v_next = positions[poly_loops[i_next].v]; | const float *v_next = positions[poly_verts[i_next]]; | ||||
| add_newell_cross_v3_v3v3(pnor, v_curr, v_next); | add_newell_cross_v3_v3v3(pnor, v_curr, v_next); | ||||
| v_curr = v_next; | v_curr = v_next; | ||||
| } | } | ||||
| if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { | if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { | ||||
| pnor[2] = 1.0f; /* Other axes set to zero. */ | pnor[2] = 1.0f; /* Other axes set to zero. */ | ||||
| } | } | ||||
| } | } | ||||
| /* Accumulate angle weighted face normal into the vertex normal. */ | /* Accumulate angle weighted face normal into the vertex normal. */ | ||||
| /* Inline version of #accumulate_vertex_normals_poly_v3. */ | /* Inline version of #accumulate_vertex_normals_poly_v3. */ | ||||
| { | { | ||||
| float edvec_prev[3], edvec_next[3], edvec_end[3]; | float edvec_prev[3], edvec_next[3], edvec_end[3]; | ||||
| const float *v_curr = positions[poly_loops[i_end].v]; | const float *v_curr = positions[poly_verts[i_end]]; | ||||
| sub_v3_v3v3(edvec_prev, positions[poly_loops[i_end - 1].v], v_curr); | sub_v3_v3v3(edvec_prev, positions[poly_verts[i_end - 1]], v_curr); | ||||
| normalize_v3(edvec_prev); | normalize_v3(edvec_prev); | ||||
| copy_v3_v3(edvec_end, 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++) { | for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { | ||||
| const float *v_next = positions[poly_loops[i_next].v]; | const float *v_next = positions[poly_verts[i_next]]; | ||||
| /* Skip an extra normalization by reusing the first calculated edge. */ | /* Skip an extra normalization by reusing the first calculated edge. */ | ||||
| if (i_next != i_end) { | if (i_next != i_end) { | ||||
| sub_v3_v3v3(edvec_next, v_curr, v_next); | sub_v3_v3v3(edvec_next, v_curr, v_next); | ||||
| normalize_v3(edvec_next); | normalize_v3(edvec_next); | ||||
| } | } | ||||
| else { | else { | ||||
| copy_v3_v3(edvec_next, edvec_end); | copy_v3_v3(edvec_next, edvec_end); | ||||
| } | } | ||||
| /* Calculate angle between the two poly edges incident on this vertex. */ | /* Calculate angle between the two poly edges incident on this vertex. */ | ||||
| const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); | const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); | ||||
| const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; | const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; | ||||
| float *vnor = vert_normals[poly_loops[i_curr].v]; | float *vnor = vert_normals[poly_verts[i_curr]]; | ||||
| add_v3_v3_atomic(vnor, vnor_add); | add_v3_v3_atomic(vnor, vnor_add); | ||||
| v_curr = v_next; | v_curr = v_next; | ||||
| copy_v3_v3(edvec_prev, edvec_next); | copy_v3_v3(edvec_prev, edvec_next); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| Show All 10 Lines | |||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| void BKE_mesh_calc_normals_poly_and_vertex(const float (*vert_positions)[3], | void BKE_mesh_calc_normals_poly_and_vertex(const float (*vert_positions)[3], | ||||
| const int mvert_len, | const int mvert_len, | ||||
| const MLoop *mloop, | const int *corner_verts, | ||||
| const int mloop_len, | const int mloop_len, | ||||
| const MPoly *mpoly, | const MPoly *mpoly, | ||||
| const int mpoly_len, | const int mpoly_len, | ||||
| float (*r_poly_normals)[3], | float (*r_poly_normals)[3], | ||||
| float (*r_vert_normals)[3]) | float (*r_vert_normals)[3]) | ||||
| { | { | ||||
| calculate_normals_poly_and_vert({reinterpret_cast<const float3 *>(vert_positions), mvert_len}, | calculate_normals_poly_and_vert({reinterpret_cast<const float3 *>(vert_positions), mvert_len}, | ||||
| {mpoly, mpoly_len}, | {mpoly, mpoly_len}, | ||||
| {mloop, mloop_len}, | {corner_verts, mloop_len}, | ||||
| {reinterpret_cast<float3 *>(r_poly_normals), mpoly_len}, | {reinterpret_cast<float3 *>(r_poly_normals), mpoly_len}, | ||||
| {reinterpret_cast<float3 *>(r_vert_normals), mvert_len}); | {reinterpret_cast<float3 *>(r_vert_normals), mvert_len}); | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Mesh Normal Calculation | /** \name Mesh Normal Calculation | ||||
| Show All 19 Lines | |||||
| float(*vert_normals)[3]; | float(*vert_normals)[3]; | ||||
| float(*poly_normals)[3]; | float(*poly_normals)[3]; | ||||
| /* Isolate task because a mutex is locked and computing normals is multi-threaded. */ | /* Isolate task because a mutex is locked and computing normals is multi-threaded. */ | ||||
| blender::threading::isolate_task([&]() { | blender::threading::isolate_task([&]() { | ||||
| Mesh &mesh_mutable = *const_cast<Mesh *>(mesh); | Mesh &mesh_mutable = *const_cast<Mesh *>(mesh); | ||||
| const Span<float3> positions = mesh_mutable.vert_positions(); | const Span<float3> positions = mesh_mutable.vert_positions(); | ||||
| const Span<MPoly> polys = mesh_mutable.polys(); | const Span<MPoly> polys = mesh_mutable.polys(); | ||||
| const Span<MLoop> loops = mesh_mutable.loops(); | const Span<int> corner_verts = mesh_mutable.corner_verts(); | ||||
| vert_normals = BKE_mesh_vertex_normals_for_write(&mesh_mutable); | vert_normals = BKE_mesh_vertex_normals_for_write(&mesh_mutable); | ||||
| poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable); | poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable); | ||||
| BKE_mesh_calc_normals_poly_and_vertex(reinterpret_cast<const float(*)[3]>(positions.data()), | BKE_mesh_calc_normals_poly_and_vertex(reinterpret_cast<const float(*)[3]>(positions.data()), | ||||
| positions.size(), | positions.size(), | ||||
| loops.data(), | corner_verts.data(), | ||||
| loops.size(), | corner_verts.size(), | ||||
| polys.data(), | polys.data(), | ||||
| polys.size(), | polys.size(), | ||||
| poly_normals, | poly_normals, | ||||
| vert_normals); | vert_normals); | ||||
| BKE_mesh_vertex_normals_clear_dirty(&mesh_mutable); | BKE_mesh_vertex_normals_clear_dirty(&mesh_mutable); | ||||
| BKE_mesh_poly_normals_clear_dirty(&mesh_mutable); | BKE_mesh_poly_normals_clear_dirty(&mesh_mutable); | ||||
| }); | }); | ||||
| Show All 20 Lines | |||||
| float(*poly_normals)[3]; | float(*poly_normals)[3]; | ||||
| /* Isolate task because a mutex is locked and computing normals is multi-threaded. */ | /* Isolate task because a mutex is locked and computing normals is multi-threaded. */ | ||||
| blender::threading::isolate_task([&]() { | blender::threading::isolate_task([&]() { | ||||
| Mesh &mesh_mutable = *const_cast<Mesh *>(mesh); | Mesh &mesh_mutable = *const_cast<Mesh *>(mesh); | ||||
| const Span<float3> positions = mesh_mutable.vert_positions(); | const Span<float3> positions = mesh_mutable.vert_positions(); | ||||
| const Span<MPoly> polys = mesh_mutable.polys(); | const Span<MPoly> polys = mesh_mutable.polys(); | ||||
| const Span<MLoop> loops = mesh_mutable.loops(); | const Span<int> corner_verts = mesh_mutable.corner_verts(); | ||||
| poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable); | poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable); | ||||
| BKE_mesh_calc_normals_poly(reinterpret_cast<const float(*)[3]>(positions.data()), | BKE_mesh_calc_normals_poly(reinterpret_cast<const float(*)[3]>(positions.data()), | ||||
| positions.size(), | positions.size(), | ||||
| loops.data(), | corner_verts.data(), | ||||
| loops.size(), | corner_verts.size(), | ||||
| polys.data(), | polys.data(), | ||||
| polys.size(), | polys.size(), | ||||
| poly_normals); | poly_normals); | ||||
| BKE_mesh_poly_normals_clear_dirty(&mesh_mutable); | BKE_mesh_poly_normals_clear_dirty(&mesh_mutable); | ||||
| }); | }); | ||||
| return poly_normals; | return poly_normals; | ||||
| ▲ Show 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | |||||
| * different elements in the arrays. */ | * different elements in the arrays. */ | ||||
| MLoopNorSpaceArray *lnors_spacearr; | MLoopNorSpaceArray *lnors_spacearr; | ||||
| MutableSpan<float3> loop_normals; | MutableSpan<float3> loop_normals; | ||||
| MutableSpan<short2> clnors_data; | MutableSpan<short2> clnors_data; | ||||
| /* Read-only. */ | /* Read-only. */ | ||||
| Span<float3> positions; | Span<float3> positions; | ||||
| Span<MEdge> edges; | Span<MEdge> edges; | ||||
| Span<MLoop> loops; | Span<int> corner_verts; | ||||
| Span<int> corner_edges; | |||||
| Span<MPoly> polys; | Span<MPoly> polys; | ||||
| Span<int2> edge_to_loops; | Span<int2> edge_to_loops; | ||||
| Span<int> loop_to_poly; | Span<int> loop_to_poly; | ||||
| Span<float3> poly_normals; | Span<float3> poly_normals; | ||||
| Span<float3> vert_normals; | Span<float3> vert_normals; | ||||
| }; | }; | ||||
| #define INDEX_UNSET INT_MIN | #define INDEX_UNSET INT_MIN | ||||
| #define INDEX_INVALID -1 | #define INDEX_INVALID -1 | ||||
| /* See comment about edge_to_loops below. */ | /* See comment about edge_to_loops below. */ | ||||
| #define IS_EDGE_SHARP(_e2l) ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID) | #define IS_EDGE_SHARP(_e2l) ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID) | ||||
| static void mesh_edges_sharp_tag(const Span<MPoly> polys, | static void mesh_edges_sharp_tag(const Span<MPoly> polys, | ||||
| const Span<MLoop> loops, | const Span<int> corner_verts, | ||||
| const Span<int> corner_edges, | |||||
| const Span<int> loop_to_poly_map, | const Span<int> loop_to_poly_map, | ||||
| const Span<float3> poly_normals, | const Span<float3> poly_normals, | ||||
| const Span<bool> sharp_edges, | const Span<bool> sharp_edges, | ||||
| const bool check_angle, | const bool check_angle, | ||||
| const float split_angle, | const float split_angle, | ||||
| MutableSpan<int2> edge_to_loops, | MutableSpan<int2> edge_to_loops, | ||||
| MutableSpan<bool> r_sharp_edges) | MutableSpan<bool> r_sharp_edges) | ||||
| { | { | ||||
| using namespace blender; | using namespace blender; | ||||
| const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; | const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; | ||||
| for (const int poly_i : polys.index_range()) { | for (const int poly_i : polys.index_range()) { | ||||
| const MPoly &poly = polys[poly_i]; | const MPoly &poly = polys[poly_i]; | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | ||||
| const int vert_i = loops[loop_index].v; | const int vert_i = corner_verts[loop_index]; | ||||
| const int edge_i = loops[loop_index].e; | const int edge_i = corner_edges[loop_index]; | ||||
| int2 &e2l = edge_to_loops[edge_i]; | int2 &e2l = edge_to_loops[edge_i]; | ||||
| /* Check whether current edge might be smooth or sharp */ | /* Check whether current edge might be smooth or sharp */ | ||||
| if ((e2l[0] | e2l[1]) == 0) { | if ((e2l[0] | e2l[1]) == 0) { | ||||
| /* 'Empty' edge until now, set e2l[0] (and e2l[1] to INDEX_UNSET to tag it as unset). */ | /* 'Empty' edge until now, set e2l[0] (and e2l[1] to INDEX_UNSET to tag it as unset). */ | ||||
| e2l[0] = loop_index; | e2l[0] = loop_index; | ||||
| /* We have to check this here too, else we might miss some flat faces!!! */ | /* We have to check this here too, else we might miss some flat faces!!! */ | ||||
| e2l[1] = (poly.flag & ME_SMOOTH) ? INDEX_UNSET : INDEX_INVALID; | e2l[1] = (poly.flag & ME_SMOOTH) ? INDEX_UNSET : INDEX_INVALID; | ||||
| } | } | ||||
| else if (e2l[1] == INDEX_UNSET) { | else if (e2l[1] == INDEX_UNSET) { | ||||
| const bool is_angle_sharp = (check_angle && | const bool is_angle_sharp = (check_angle && | ||||
| dot_v3v3(poly_normals[loop_to_poly_map[e2l[0]]], | dot_v3v3(poly_normals[loop_to_poly_map[e2l[0]]], | ||||
| poly_normals[poly_i]) < split_angle_cos); | poly_normals[poly_i]) < split_angle_cos); | ||||
| /* Second loop using this edge, time to test its sharpness. | /* Second loop using this edge, time to test its sharpness. | ||||
| * An edge is sharp if it is tagged as such, or its face is not smooth, | * An edge is sharp if it is tagged as such, or its face is not smooth, | ||||
| * or both poly have opposed (flipped) normals, i.e. both loops on the same edge share the | * or both poly have opposed (flipped) normals, i.e. both loops on the same edge share the | ||||
| * same vertex, or angle between both its polys' normals is above split_angle value. | * same vertex, or angle between both its polys' normals is above split_angle value. | ||||
| */ | */ | ||||
| if (!(poly.flag & ME_SMOOTH) || (!sharp_edges.is_empty() && sharp_edges[edge_i]) || | if (!(poly.flag & ME_SMOOTH) || (!sharp_edges.is_empty() && sharp_edges[edge_i]) || | ||||
| vert_i == loops[e2l[0]].v || is_angle_sharp) { | vert_i == corner_verts[e2l[0]] || is_angle_sharp) { | ||||
| /* NOTE: we are sure that loop != 0 here ;). */ | /* NOTE: we are sure that loop != 0 here ;). */ | ||||
| e2l[1] = INDEX_INVALID; | e2l[1] = INDEX_INVALID; | ||||
| /* We want to avoid tagging edges as sharp when it is already defined as such by | /* We want to avoid tagging edges as sharp when it is already defined as such by | ||||
| * other causes than angle threshold. */ | * other causes than angle threshold. */ | ||||
| if (!r_sharp_edges.is_empty() && is_angle_sharp) { | if (!r_sharp_edges.is_empty() && is_angle_sharp) { | ||||
| r_sharp_edges[edge_i] = true; | r_sharp_edges[edge_i] = true; | ||||
| } | } | ||||
| Show All 13 Lines | |||||
| } | } | ||||
| } | } | ||||
| /* Else, edge is already 'disqualified' (i.e. sharp)! */ | /* Else, edge is already 'disqualified' (i.e. sharp)! */ | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void BKE_edges_sharp_from_angle_set(const int numEdges, | void BKE_edges_sharp_from_angle_set(const int numEdges, | ||||
| const MLoop *mloops, | const int *corner_verts, | ||||
| const int *corner_edges, | |||||
| const int numLoops, | const int numLoops, | ||||
| const MPoly *mpolys, | const MPoly *mpolys, | ||||
| const float (*poly_normals)[3], | const float (*poly_normals)[3], | ||||
| const int numPolys, | const int numPolys, | ||||
| const float split_angle, | const float split_angle, | ||||
| bool *sharp_edges) | bool *sharp_edges) | ||||
| { | { | ||||
| using namespace blender; | using namespace blender; | ||||
| using namespace blender::bke; | using namespace blender::bke; | ||||
| if (split_angle >= float(M_PI)) { | if (split_angle >= float(M_PI)) { | ||||
| /* Nothing to do! */ | /* Nothing to do! */ | ||||
| return; | return; | ||||
| } | } | ||||
| /* Mapping edge -> loops. See #BKE_mesh_normals_loop_split for details. */ | /* Mapping edge -> loops. See #BKE_mesh_normals_loop_split for details. */ | ||||
| Array<int2> edge_to_loops(numEdges, int2(0)); | Array<int2> edge_to_loops(numEdges, int2(0)); | ||||
| /* Simple mapping from a loop to its polygon index. */ | /* Simple mapping from a loop to its polygon index. */ | ||||
| const Array<int> loop_to_poly = mesh_topology::build_loop_to_poly_map({mpolys, numPolys}, | const Array<int> loop_to_poly = mesh_topology::build_loop_to_poly_map({mpolys, numPolys}, | ||||
| numLoops); | numLoops); | ||||
| mesh_edges_sharp_tag({mpolys, numPolys}, | mesh_edges_sharp_tag({mpolys, numPolys}, | ||||
| {mloops, numLoops}, | {corner_verts, numLoops}, | ||||
| {corner_edges, numLoops}, | |||||
| loop_to_poly, | loop_to_poly, | ||||
| {reinterpret_cast<const float3 *>(poly_normals), numPolys}, | {reinterpret_cast<const float3 *>(poly_normals), numPolys}, | ||||
| Span<bool>(sharp_edges, numEdges), | Span<bool>(sharp_edges, numEdges), | ||||
| true, | true, | ||||
| split_angle, | split_angle, | ||||
| edge_to_loops, | edge_to_loops, | ||||
| {sharp_edges, numEdges}); | {sharp_edges, numEdges}); | ||||
| } | } | ||||
| static void loop_manifold_fan_around_vert_next(const Span<MLoop> loops, | static void loop_manifold_fan_around_vert_next(const Span<int> corner_verts, | ||||
| const Span<MPoly> polys, | const Span<MPoly> polys, | ||||
| const Span<int> loop_to_poly, | const Span<int> loop_to_poly, | ||||
| const int *e2lfan_curr, | const int *e2lfan_curr, | ||||
| const uint mv_pivot_index, | const uint mv_pivot_index, | ||||
| int *r_mlfan_curr_index, | int *r_mlfan_curr_index, | ||||
| int *r_mlfan_vert_index, | int *r_mlfan_vert_index, | ||||
| int *r_mpfan_curr_index) | int *r_mpfan_curr_index) | ||||
| { | { | ||||
| const int mlfan_curr_orig = *r_mlfan_curr_index; | const int mlfan_curr_orig = *r_mlfan_curr_index; | ||||
| const uint vert_fan_orig = loops[mlfan_curr_orig].v; | const uint vert_fan_orig = corner_verts[mlfan_curr_orig]; | ||||
| /* WARNING: This is rather complex! | /* WARNING: This is rather complex! | ||||
| * We have to find our next edge around the vertex (fan mode). | * We have to find our next edge around the vertex (fan mode). | ||||
| * First we find the next loop, which is either previous or next to mlfan_curr_index, depending | * First we find the next loop, which is either previous or next to mlfan_curr_index, depending | ||||
| * whether both loops using current edge are in the same direction or not, and whether | * whether both loops using current edge are in the same direction or not, and whether | ||||
| * mlfan_curr_index actually uses the vertex we are fanning around! | * mlfan_curr_index actually uses the vertex we are fanning around! | ||||
| * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one | * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one | ||||
| * (i.e. not the future `mlfan_curr`). */ | * (i.e. not the future `mlfan_curr`). */ | ||||
| *r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; | *r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; | ||||
| *r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index]; | *r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index]; | ||||
| BLI_assert(*r_mlfan_curr_index >= 0); | BLI_assert(*r_mlfan_curr_index >= 0); | ||||
| BLI_assert(*r_mpfan_curr_index >= 0); | BLI_assert(*r_mpfan_curr_index >= 0); | ||||
| const uint vert_fan_next = loops[*r_mlfan_curr_index].v; | const uint vert_fan_next = corner_verts[*r_mlfan_curr_index]; | ||||
| const MPoly &mpfan_next = polys[*r_mpfan_curr_index]; | const MPoly &mpfan_next = polys[*r_mpfan_curr_index]; | ||||
| if ((vert_fan_orig == vert_fan_next && vert_fan_orig == mv_pivot_index) || | if ((vert_fan_orig == vert_fan_next && vert_fan_orig == mv_pivot_index) || | ||||
| (!ELEM(vert_fan_orig, vert_fan_next, mv_pivot_index))) { | (!ELEM(vert_fan_orig, vert_fan_next, mv_pivot_index))) { | ||||
| /* We need the previous loop, but current one is our vertex's loop. */ | /* We need the previous loop, but current one is our vertex's loop. */ | ||||
| *r_mlfan_vert_index = *r_mlfan_curr_index; | *r_mlfan_vert_index = *r_mlfan_curr_index; | ||||
| if (--(*r_mlfan_curr_index) < mpfan_next.loopstart) { | if (--(*r_mlfan_curr_index) < mpfan_next.loopstart) { | ||||
| *r_mlfan_curr_index = mpfan_next.loopstart + mpfan_next.totloop - 1; | *r_mlfan_curr_index = mpfan_next.loopstart + mpfan_next.totloop - 1; | ||||
| } | } | ||||
| Show All 9 Lines | |||||
| static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) | static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) | ||||
| { | { | ||||
| MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; | MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; | ||||
| const Span<short2> clnors_data = common_data->clnors_data; | const Span<short2> clnors_data = common_data->clnors_data; | ||||
| const Span<float3> positions = common_data->positions; | const Span<float3> positions = common_data->positions; | ||||
| const Span<MEdge> edges = common_data->edges; | const Span<MEdge> edges = common_data->edges; | ||||
| const Span<MLoop> loops = common_data->loops; | const Span<int> corner_verts = common_data->corner_verts; | ||||
| const Span<int> corner_edges = common_data->corner_edges; | |||||
| const Span<float3> poly_normals = common_data->poly_normals; | const Span<float3> poly_normals = common_data->poly_normals; | ||||
| MutableSpan<float3> loop_normals = common_data->loop_normals; | MutableSpan<float3> loop_normals = common_data->loop_normals; | ||||
| MLoopNorSpace *lnor_space = data->lnor_space; | MLoopNorSpace *lnor_space = data->lnor_space; | ||||
| const int ml_curr_index = data->ml_curr_index; | const int ml_curr_index = data->ml_curr_index; | ||||
| const int ml_prev_index = data->ml_prev_index; | const int ml_prev_index = data->ml_prev_index; | ||||
| const int mp_index = data->mp_index; | const int mp_index = data->mp_index; | ||||
| Show All 9 Lines | |||||
| loops[ml_curr_index].v, | loops[ml_curr_index].v, | ||||
| mp_index); | mp_index); | ||||
| #endif | #endif | ||||
| /* If needed, generate this (simple!) lnor space. */ | /* If needed, generate this (simple!) lnor space. */ | ||||
| if (lnors_spacearr) { | if (lnors_spacearr) { | ||||
| float vec_curr[3], vec_prev[3]; | float vec_curr[3], vec_prev[3]; | ||||
| const uint mv_pivot_index = loops[ml_curr_index].v; /* The vertex we are "fanning" around! */ | const uint mv_pivot_index = | ||||
| const MEdge *me_curr = &edges[loops[ml_curr_index].e]; | corner_verts[ml_curr_index]; /* The vertex we are "fanning" around! */ | ||||
| const MEdge *me_curr = &edges[corner_edges[ml_curr_index]]; | |||||
| const int vert_2 = me_curr->v1 == mv_pivot_index ? me_curr->v2 : me_curr->v1; | const int vert_2 = me_curr->v1 == mv_pivot_index ? me_curr->v2 : me_curr->v1; | ||||
| const MEdge *me_prev = &edges[loops[ml_prev_index].e]; | const MEdge *me_prev = &edges[corner_edges[ml_prev_index]]; | ||||
| const int vert_3 = me_prev->v1 == mv_pivot_index ? me_prev->v2 : me_prev->v1; | const int vert_3 = me_prev->v1 == mv_pivot_index ? me_prev->v2 : me_prev->v1; | ||||
| sub_v3_v3v3(vec_curr, positions[vert_2], positions[mv_pivot_index]); | sub_v3_v3v3(vec_curr, positions[vert_2], positions[mv_pivot_index]); | ||||
| normalize_v3(vec_curr); | normalize_v3(vec_curr); | ||||
| sub_v3_v3v3(vec_prev, positions[vert_3], positions[mv_pivot_index]); | sub_v3_v3v3(vec_prev, positions[vert_3], positions[mv_pivot_index]); | ||||
| normalize_v3(vec_prev); | normalize_v3(vec_prev); | ||||
| BKE_lnor_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, nullptr); | BKE_lnor_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, nullptr); | ||||
| Show All 13 Lines | |||||
| { | { | ||||
| MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; | MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; | ||||
| MutableSpan<float3> loop_normals = common_data->loop_normals; | MutableSpan<float3> loop_normals = common_data->loop_normals; | ||||
| MutableSpan<short2> clnors_data = common_data->clnors_data; | MutableSpan<short2> clnors_data = common_data->clnors_data; | ||||
| const Span<float3> positions = common_data->positions; | const Span<float3> positions = common_data->positions; | ||||
| const Span<MEdge> edges = common_data->edges; | const Span<MEdge> edges = common_data->edges; | ||||
| const Span<MPoly> polys = common_data->polys; | const Span<MPoly> polys = common_data->polys; | ||||
| const Span<MLoop> loops = common_data->loops; | const Span<int> corner_verts = common_data->corner_verts; | ||||
| const Span<int> corner_edges = common_data->corner_edges; | |||||
| const Span<int2> edge_to_loops = common_data->edge_to_loops; | const Span<int2> edge_to_loops = common_data->edge_to_loops; | ||||
| const Span<int> loop_to_poly = common_data->loop_to_poly; | const Span<int> loop_to_poly = common_data->loop_to_poly; | ||||
| const Span<float3> poly_normals = common_data->poly_normals; | const Span<float3> poly_normals = common_data->poly_normals; | ||||
| MLoopNorSpace *lnor_space = data->lnor_space; | MLoopNorSpace *lnor_space = data->lnor_space; | ||||
| #if 0 /* Not needed for 'fan' loops. */ | #if 0 /* Not needed for 'fan' loops. */ | ||||
| float(*lnor)[3] = data->lnor; | float(*lnor)[3] = data->lnor; | ||||
| #endif | #endif | ||||
| const int ml_curr_index = data->ml_curr_index; | const int ml_curr_index = data->ml_curr_index; | ||||
| const int ml_prev_index = data->ml_prev_index; | const int ml_prev_index = data->ml_prev_index; | ||||
| const int mp_index = data->mp_index; | const int mp_index = data->mp_index; | ||||
| /* Sigh! we have to fan around current vertex, until we find the other non-smooth edge, | /* Sigh! we have to fan around current vertex, until we find the other non-smooth edge, | ||||
| * and accumulate face normals into the vertex! | * and accumulate face normals into the vertex! | ||||
| * Note in case this vertex has only one sharp edges, this is a waste because the normal is the | * Note in case this vertex has only one sharp edges, this is a waste because the normal is the | ||||
| * same as the vertex normal, but I do not see any easy way to detect that (would need to count | * same as the vertex normal, but I do not see any easy way to detect that (would need to count | ||||
| * number of sharp edges per vertex, I doubt the additional memory usage would be worth it, | * number of sharp edges per vertex, I doubt the additional memory usage would be worth it, | ||||
| * especially as it should not be a common case in real-life meshes anyway). */ | * especially as it should not be a common case in real-life meshes anyway). */ | ||||
| const uint mv_pivot_index = loops[ml_curr_index].v; /* The vertex we are "fanning" around! */ | const int mv_pivot_index = corner_verts[ml_curr_index]; /* The vertex we are "fanning" around! */ | ||||
| /* `ml_curr_index` would be mlfan_prev if we needed that one. */ | /* `ml_curr_index` would be mlfan_prev if we needed that one. */ | ||||
| const MEdge *me_org = &edges[loops[ml_curr_index].e]; | const MEdge *me_org = &edges[corner_edges[ml_curr_index]]; | ||||
| float vec_curr[3], vec_prev[3], vec_org[3]; | float vec_curr[3], vec_prev[3], vec_org[3]; | ||||
| float lnor[3] = {0.0f, 0.0f, 0.0f}; | float lnor[3] = {0.0f, 0.0f, 0.0f}; | ||||
| /* We validate clnors data on the fly - cheapest way to do! */ | /* We validate clnors data on the fly - cheapest way to do! */ | ||||
| int clnors_avg[2] = {0, 0}; | int clnors_avg[2] = {0, 0}; | ||||
| short2 *clnor_ref = nullptr; | short2 *clnor_ref = nullptr; | ||||
| int clnors_count = 0; | int clnors_count = 0; | ||||
| Show All 26 Lines | |||||
| if (lnors_spacearr) { | if (lnors_spacearr) { | ||||
| BLI_stack_push(edge_vectors, vec_org); | BLI_stack_push(edge_vectors, vec_org); | ||||
| } | } | ||||
| } | } | ||||
| // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); | // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); | ||||
| while (true) { | while (true) { | ||||
| const MEdge *me_curr = &edges[loops[mlfan_curr_index].e]; | const MEdge *me_curr = &edges[corner_edges[mlfan_curr_index]]; | ||||
| /* Compute edge vectors. | /* Compute edge vectors. | ||||
| * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing | * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing | ||||
| * them twice (or more) here. However, time gained is not worth memory and time lost, | * them twice (or more) here. However, time gained is not worth memory and time lost, | ||||
| * given the fact that this code should not be called that much in real-life meshes. | * given the fact that this code should not be called that much in real-life meshes. | ||||
| */ | */ | ||||
| { | { | ||||
| const float3 &mv_2 = (me_curr->v1 == mv_pivot_index) ? positions[me_curr->v2] : | const float3 &mv_2 = (me_curr->v1 == mv_pivot_index) ? positions[me_curr->v2] : | ||||
| positions[me_curr->v1]; | positions[me_curr->v1]; | ||||
| sub_v3_v3v3(vec_curr, mv_2, positions[mv_pivot_index]); | sub_v3_v3v3(vec_curr, mv_2, positions[mv_pivot_index]); | ||||
| normalize_v3(vec_curr); | normalize_v3(vec_curr); | ||||
| } | } | ||||
| // printf("\thandling edge %d / loop %d\n", loops[mlfan_curr_index].e, mlfan_curr_index); | // printf("\thandling edge %d / loop %d\n", corner_edges[mlfan_curr_index], mlfan_curr_index); | ||||
| { | { | ||||
| /* Code similar to accumulate_vertex_normals_poly_v3. */ | /* Code similar to accumulate_vertex_normals_poly_v3. */ | ||||
| /* Calculate angle between the two poly edges incident on this vertex. */ | /* Calculate angle between the two poly edges incident on this vertex. */ | ||||
| const float fac = saacos(dot_v3v3(vec_curr, vec_prev)); | const float fac = saacos(dot_v3v3(vec_curr, vec_prev)); | ||||
| /* Accumulate */ | /* Accumulate */ | ||||
| madd_v3_v3fl(lnor, poly_normals[mpfan_curr_index], fac); | madd_v3_v3fl(lnor, poly_normals[mpfan_curr_index], fac); | ||||
| Show All 21 Lines | |||||
| /* Assign current lnor space to current 'vertex' loop. */ | /* Assign current lnor space to current 'vertex' loop. */ | ||||
| BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, mlfan_vert_index, nullptr, false); | BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, mlfan_vert_index, nullptr, false); | ||||
| if (me_curr != me_org) { | if (me_curr != me_org) { | ||||
| /* We store here all edges-normalized vectors processed. */ | /* We store here all edges-normalized vectors processed. */ | ||||
| BLI_stack_push(edge_vectors, vec_curr); | BLI_stack_push(edge_vectors, vec_curr); | ||||
| } | } | ||||
| } | } | ||||
| if (IS_EDGE_SHARP(edge_to_loops[loops[mlfan_curr_index].e]) || (me_curr == me_org)) { | if (IS_EDGE_SHARP(edge_to_loops[corner_edges[mlfan_curr_index]]) || (me_curr == me_org)) { | ||||
| /* Current edge is sharp and we have finished with this fan of faces around this vert, | /* Current edge is sharp and we have finished with this fan of faces around this vert, | ||||
| * or this vert is smooth, and we have completed a full turn around it. */ | * or this vert is smooth, and we have completed a full turn around it. */ | ||||
| // printf("FAN: Finished!\n"); | // printf("FAN: Finished!\n"); | ||||
| break; | break; | ||||
| } | } | ||||
| copy_v3_v3(vec_prev, vec_curr); | copy_v3_v3(vec_prev, vec_curr); | ||||
| /* Find next loop of the smooth fan. */ | /* Find next loop of the smooth fan. */ | ||||
| loop_manifold_fan_around_vert_next(loops, | loop_manifold_fan_around_vert_next(corner_verts, | ||||
| polys, | polys, | ||||
| loop_to_poly, | loop_to_poly, | ||||
| edge_to_loops[loops[mlfan_curr_index].e], | edge_to_loops[corner_edges[mlfan_curr_index]], | ||||
| mv_pivot_index, | mv_pivot_index, | ||||
| &mlfan_curr_index, | &mlfan_curr_index, | ||||
| &mlfan_vert_index, | &mlfan_vert_index, | ||||
| &mpfan_curr_index); | &mpfan_curr_index); | ||||
| } | } | ||||
| { | { | ||||
| float lnor_len = normalize_v3(lnor); | float lnor_len = normalize_v3(lnor); | ||||
| ▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
| BLI_Stack *edge_vectors = common_data->lnors_spacearr ? | BLI_Stack *edge_vectors = common_data->lnors_spacearr ? | ||||
| BLI_stack_new(sizeof(float[3]), __func__) : | BLI_stack_new(sizeof(float[3]), __func__) : | ||||
| nullptr; | nullptr; | ||||
| for (int i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) { | for (int i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) { | ||||
| if (data->flag == LoopSplitTaskData::Type::BlockEnd) { | if (data->flag == LoopSplitTaskData::Type::BlockEnd) { | ||||
| break; | break; | ||||
| } | } | ||||
| loop_split_worker_do(common_data, data, edge_vectors); | loop_split_worker_do(common_data, data, edge_vectors); | ||||
| } | } | ||||
| if (edge_vectors) { | if (edge_vectors) { | ||||
| BLI_stack_free(edge_vectors); | BLI_stack_free(edge_vectors); | ||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| * Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not. | * Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not. | ||||
| * Needed because cyclic smooth fans have no obvious 'entry point', | * Needed because cyclic smooth fans have no obvious 'entry point', | ||||
| * and yet we need to walk them once, and only once. | * and yet we need to walk them once, and only once. | ||||
| */ | */ | ||||
| static bool loop_split_generator_check_cyclic_smooth_fan(const Span<MLoop> mloops, | static bool loop_split_generator_check_cyclic_smooth_fan(const Span<int> corner_verts, | ||||
| const Span<int> corner_edges, | |||||
| const Span<MPoly> mpolys, | const Span<MPoly> mpolys, | ||||
| const Span<int2> edge_to_loops, | const Span<int2> edge_to_loops, | ||||
| const Span<int> loop_to_poly, | const Span<int> loop_to_poly, | ||||
| const int *e2l_prev, | const int *e2l_prev, | ||||
| BitVector<> &skip_loops, | BitVector<> &skip_loops, | ||||
| const int ml_curr_index, | const int ml_curr_index, | ||||
| const int ml_prev_index, | const int ml_prev_index, | ||||
| const int mp_curr_index) | const int mp_curr_index) | ||||
| { | { | ||||
| const uint mv_pivot_index = mloops[ml_curr_index].v; /* The vertex we are "fanning" around! */ | /* The vertex we are "fanning" around! */ | ||||
| const uint mv_pivot_index = corner_verts[ml_curr_index]; | |||||
| const int *e2lfan_curr = e2l_prev; | const int *e2lfan_curr = e2l_prev; | ||||
| if (IS_EDGE_SHARP(e2lfan_curr)) { | if (IS_EDGE_SHARP(e2lfan_curr)) { | ||||
| /* Sharp loop, so not a cyclic smooth fan. */ | /* Sharp loop, so not a cyclic smooth fan. */ | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex! | /* `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; | int mlfan_curr_index = ml_prev_index; | ||||
| int mlfan_vert_index = ml_curr_index; | int mlfan_vert_index = ml_curr_index; | ||||
| int mpfan_curr_index = mp_curr_index; | int mpfan_curr_index = mp_curr_index; | ||||
| BLI_assert(mlfan_curr_index >= 0); | BLI_assert(mlfan_curr_index >= 0); | ||||
| BLI_assert(mlfan_vert_index >= 0); | BLI_assert(mlfan_vert_index >= 0); | ||||
| BLI_assert(mpfan_curr_index >= 0); | BLI_assert(mpfan_curr_index >= 0); | ||||
| BLI_assert(!skip_loops[mlfan_vert_index]); | BLI_assert(!skip_loops[mlfan_vert_index]); | ||||
| skip_loops[mlfan_vert_index].set(); | skip_loops[mlfan_vert_index].set(); | ||||
| while (true) { | while (true) { | ||||
| /* Find next loop of the smooth fan. */ | /* Find next loop of the smooth fan. */ | ||||
| loop_manifold_fan_around_vert_next(mloops, | loop_manifold_fan_around_vert_next(corner_verts, | ||||
| mpolys, | mpolys, | ||||
| loop_to_poly, | loop_to_poly, | ||||
| e2lfan_curr, | e2lfan_curr, | ||||
| mv_pivot_index, | mv_pivot_index, | ||||
| &mlfan_curr_index, | &mlfan_curr_index, | ||||
| &mlfan_vert_index, | &mlfan_vert_index, | ||||
| &mpfan_curr_index); | &mpfan_curr_index); | ||||
| e2lfan_curr = edge_to_loops[mloops[mlfan_curr_index].e]; | e2lfan_curr = edge_to_loops[corner_edges[mlfan_curr_index]]; | ||||
| if (IS_EDGE_SHARP(e2lfan_curr)) { | if (IS_EDGE_SHARP(e2lfan_curr)) { | ||||
| /* Sharp loop/edge, so not a cyclic smooth fan. */ | /* Sharp loop/edge, so not a cyclic smooth fan. */ | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* Smooth loop/edge. */ | /* Smooth loop/edge. */ | ||||
| if (skip_loops[mlfan_vert_index]) { | if (skip_loops[mlfan_vert_index]) { | ||||
| if (mlfan_vert_index == ml_curr_index) { | if (mlfan_vert_index == ml_curr_index) { | ||||
| Show All 11 Lines | |||||
| } | } | ||||
| static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common_data) | static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common_data) | ||||
| { | { | ||||
| using namespace blender; | using namespace blender; | ||||
| using namespace blender::bke; | using namespace blender::bke; | ||||
| MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; | MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; | ||||
| const Span<MLoop> loops = common_data->loops; | const Span<int> corner_verts = common_data->corner_verts; | ||||
| const Span<int> corner_edges = common_data->corner_edges; | |||||
| const Span<MPoly> polys = common_data->polys; | const Span<MPoly> polys = common_data->polys; | ||||
| const Span<int> loop_to_poly = common_data->loop_to_poly; | const Span<int> loop_to_poly = common_data->loop_to_poly; | ||||
| const Span<int2> edge_to_loops = common_data->edge_to_loops; | const Span<int2> edge_to_loops = common_data->edge_to_loops; | ||||
| BitVector<> skip_loops(loops.size(), false); | BitVector<> skip_loops(corner_verts.size(), false); | ||||
| LoopSplitTaskData *data_buff = nullptr; | LoopSplitTaskData *data_buff = nullptr; | ||||
| int data_idx = 0; | int data_idx = 0; | ||||
| /* Temp edge vectors stack, only used when computing lnor spacearr | /* Temp edge vectors stack, only used when computing lnor spacearr | ||||
| * (and we are not multi-threading). */ | * (and we are not multi-threading). */ | ||||
| BLI_Stack *edge_vectors = nullptr; | BLI_Stack *edge_vectors = nullptr; | ||||
| Show All 30 Lines | |||||
| * as 'entry point', otherwise we can skip it. */ | * as 'entry point', otherwise we can skip it. */ | ||||
| /* NOTE: In theory, we could make #loop_split_generator_check_cyclic_smooth_fan() store | /* NOTE: In theory, we could make #loop_split_generator_check_cyclic_smooth_fan() store | ||||
| * mlfan_vert_index'es and edge indexes in two stacks, to avoid having to fan again around | * mlfan_vert_index'es and edge indexes in two stacks, to avoid having to fan again around | ||||
| * the vert during actual computation of `clnor` & `clnorspace`. | * the vert during actual computation of `clnor` & `clnorspace`. | ||||
| * However, this would complicate the code, add more memory usage, and despite its logical | * However, this would complicate the code, add more memory usage, and despite its logical | ||||
| * complexity, #loop_manifold_fan_around_vert_next() is quite cheap in term of CPU cycles, | * complexity, #loop_manifold_fan_around_vert_next() is quite cheap in term of CPU cycles, | ||||
| * so really think it's not worth it. */ | * so really think it's not worth it. */ | ||||
| if (!IS_EDGE_SHARP(edge_to_loops[loops[ml_curr_index].e]) && | if (!IS_EDGE_SHARP(edge_to_loops[corner_edges[ml_curr_index]]) && | ||||
| (skip_loops[ml_curr_index] || | (skip_loops[ml_curr_index] || !loop_split_generator_check_cyclic_smooth_fan( | ||||
| !loop_split_generator_check_cyclic_smooth_fan(loops, | corner_verts, | ||||
| polys, | corner_edges, | ||||
| edge_to_loops, | polys, | ||||
| loop_to_poly, | edge_to_loops, | ||||
| edge_to_loops[loops[ml_prev_index].e], | loop_to_poly, | ||||
| skip_loops, | edge_to_loops[corner_edges[ml_prev_index]], | ||||
| ml_curr_index, | skip_loops, | ||||
| ml_prev_index, | ml_curr_index, | ||||
| mp_index))) { | ml_prev_index, | ||||
| mp_index))) { | |||||
| // printf("SKIPPING!\n"); | // printf("SKIPPING!\n"); | ||||
| } | } | ||||
| else { | else { | ||||
| LoopSplitTaskData *data, data_local; | LoopSplitTaskData *data, data_local; | ||||
| // printf("PROCESSING!\n"); | // printf("PROCESSING!\n"); | ||||
| if (pool) { | if (pool) { | ||||
| if (data_idx == 0) { | if (data_idx == 0) { | ||||
| data_buff = (LoopSplitTaskData *)MEM_calloc_arrayN( | data_buff = (LoopSplitTaskData *)MEM_calloc_arrayN( | ||||
| LOOP_SPLIT_TASK_BLOCK_SIZE, sizeof(*data_buff), __func__); | LOOP_SPLIT_TASK_BLOCK_SIZE, sizeof(*data_buff), __func__); | ||||
| } | } | ||||
| data = &data_buff[data_idx]; | data = &data_buff[data_idx]; | ||||
| } | } | ||||
| else { | else { | ||||
| data = &data_local; | data = &data_local; | ||||
| memset(data, 0, sizeof(*data)); | memset(data, 0, sizeof(*data)); | ||||
| } | } | ||||
| if (IS_EDGE_SHARP(edge_to_loops[loops[ml_curr_index].e]) && | if (IS_EDGE_SHARP(edge_to_loops[corner_edges[ml_curr_index]]) && | ||||
| IS_EDGE_SHARP(edge_to_loops[loops[ml_prev_index].e])) { | IS_EDGE_SHARP(edge_to_loops[corner_edges[ml_prev_index]])) { | ||||
| data->ml_curr_index = ml_curr_index; | data->ml_curr_index = ml_curr_index; | ||||
| data->ml_prev_index = ml_prev_index; | data->ml_prev_index = ml_prev_index; | ||||
| data->flag = LoopSplitTaskData::Type::Single; | data->flag = LoopSplitTaskData::Type::Single; | ||||
| data->mp_index = mp_index; | data->mp_index = mp_index; | ||||
| if (lnors_spacearr) { | if (lnors_spacearr) { | ||||
| data->lnor_space = BKE_lnor_space_create(lnors_spacearr); | data->lnor_space = BKE_lnor_space_create(lnors_spacearr); | ||||
| } | } | ||||
| } | } | ||||
| Show All 37 Lines | |||||
| } | } | ||||
| } | } | ||||
| void BKE_mesh_normals_loop_split(const float (*vert_positions)[3], | void BKE_mesh_normals_loop_split(const float (*vert_positions)[3], | ||||
| const float (*vert_normals)[3], | const float (*vert_normals)[3], | ||||
| const int numVerts, | const int numVerts, | ||||
| const MEdge *medges, | const MEdge *medges, | ||||
| const int numEdges, | const int numEdges, | ||||
| const MLoop *mloops, | const int *corner_verts, | ||||
| const int *corner_edges, | |||||
| float (*r_loop_normals)[3], | float (*r_loop_normals)[3], | ||||
| const int numLoops, | const int numLoops, | ||||
| const MPoly *mpolys, | const MPoly *mpolys, | ||||
| const float (*poly_normals)[3], | const float (*poly_normals)[3], | ||||
| const int numPolys, | const int numPolys, | ||||
| const bool use_split_normals, | const bool use_split_normals, | ||||
| const float split_angle, | const float split_angle, | ||||
| const bool *sharp_edges, | const bool *sharp_edges, | ||||
| Show All 22 Lines | |||||
| const int ml_index_end = ml_index + mp->totloop; | const int ml_index_end = ml_index + mp->totloop; | ||||
| const bool is_poly_flat = ((mp->flag & ME_SMOOTH) == 0); | const bool is_poly_flat = ((mp->flag & ME_SMOOTH) == 0); | ||||
| for (; ml_index < ml_index_end; ml_index++) { | for (; ml_index < ml_index_end; ml_index++) { | ||||
| if (is_poly_flat) { | if (is_poly_flat) { | ||||
| copy_v3_v3(r_loop_normals[ml_index], poly_normals[mp_index]); | copy_v3_v3(r_loop_normals[ml_index], poly_normals[mp_index]); | ||||
| } | } | ||||
| else { | else { | ||||
| copy_v3_v3(r_loop_normals[ml_index], vert_normals[mloops[ml_index].v]); | copy_v3_v3(r_loop_normals[ml_index], vert_normals[corner_verts[ml_index]]); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return; | return; | ||||
| } | } | ||||
| /** | /** | ||||
| * Mapping edge -> loops. | * Mapping edge -> loops. | ||||
| Show All 35 Lines | |||||
| /* We need to compute lnor spacearr if some custom lnor data are given to us! */ | /* We need to compute lnor spacearr if some custom lnor data are given to us! */ | ||||
| r_lnors_spacearr = &_lnors_spacearr; | r_lnors_spacearr = &_lnors_spacearr; | ||||
| } | } | ||||
| if (r_lnors_spacearr) { | if (r_lnors_spacearr) { | ||||
| BKE_lnor_spacearr_init(r_lnors_spacearr, numLoops, MLNOR_SPACEARR_LOOP_INDEX); | BKE_lnor_spacearr_init(r_lnors_spacearr, numLoops, MLNOR_SPACEARR_LOOP_INDEX); | ||||
| } | } | ||||
| const Span<MPoly> polys(mpolys, numPolys); | const Span<MPoly> polys(mpolys, numPolys); | ||||
| const Span<MLoop> loops(mloops, numLoops); | |||||
| /* Init data common to all tasks. */ | /* Init data common to all tasks. */ | ||||
| LoopSplitTaskDataCommon common_data; | LoopSplitTaskDataCommon common_data; | ||||
| common_data.lnors_spacearr = r_lnors_spacearr; | common_data.lnors_spacearr = r_lnors_spacearr; | ||||
| common_data.loop_normals = {reinterpret_cast<float3 *>(r_loop_normals), numLoops}; | common_data.loop_normals = {reinterpret_cast<float3 *>(r_loop_normals), numLoops}; | ||||
| common_data.clnors_data = {reinterpret_cast<short2 *>(clnors_data), clnors_data ? numLoops : 0}; | common_data.clnors_data = {reinterpret_cast<short2 *>(clnors_data), clnors_data ? numLoops : 0}; | ||||
| common_data.positions = {reinterpret_cast<const float3 *>(vert_positions), numVerts}; | common_data.positions = {reinterpret_cast<const float3 *>(vert_positions), numVerts}; | ||||
| common_data.edges = {medges, numEdges}; | common_data.edges = {medges, numEdges}; | ||||
| common_data.polys = polys; | common_data.polys = polys; | ||||
| common_data.loops = loops; | common_data.corner_verts = {corner_verts, numLoops}; | ||||
| common_data.corner_edges = {corner_edges, numLoops}; | |||||
| common_data.edge_to_loops = edge_to_loops; | common_data.edge_to_loops = edge_to_loops; | ||||
| common_data.loop_to_poly = loop_to_poly; | common_data.loop_to_poly = loop_to_poly; | ||||
| common_data.poly_normals = {reinterpret_cast<const float3 *>(poly_normals), numPolys}; | common_data.poly_normals = {reinterpret_cast<const float3 *>(poly_normals), numPolys}; | ||||
| common_data.vert_normals = {reinterpret_cast<const float3 *>(vert_normals), numVerts}; | common_data.vert_normals = {reinterpret_cast<const float3 *>(vert_normals), numVerts}; | ||||
| /* Pre-populate all loop normals as if their verts were all smooth. | /* Pre-populate all loop normals as if their verts were all smooth. | ||||
| * This way we don't have to compute those later! */ | * This way we don't have to compute those later! */ | ||||
| threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { | threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { | ||||
| for (const int poly_i : range) { | for (const int poly_i : range) { | ||||
| const MPoly &poly = polys[poly_i]; | const MPoly &poly = polys[poly_i]; | ||||
| for (const int loop_i : IndexRange(poly.loopstart, poly.totloop)) { | for (const int loop_i : IndexRange(poly.loopstart, poly.totloop)) { | ||||
| copy_v3_v3(r_loop_normals[loop_i], vert_normals[loops[loop_i].v]); | copy_v3_v3(r_loop_normals[loop_i], vert_normals[corner_verts[loop_i]]); | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| /* This first loop check which edges are actually smooth, and compute edge vectors. */ | /* This first loop check which edges are actually smooth, and compute edge vectors. */ | ||||
| mesh_edges_sharp_tag(polys, | mesh_edges_sharp_tag(polys, | ||||
| loops, | {corner_verts, numLoops}, | ||||
| {corner_edges, numLoops}, | |||||
| loop_to_poly, | loop_to_poly, | ||||
| {reinterpret_cast<const float3 *>(poly_normals), numPolys}, | {reinterpret_cast<const float3 *>(poly_normals), numPolys}, | ||||
| Span<bool>(sharp_edges, sharp_edges ? numEdges : 0), | Span<bool>(sharp_edges, sharp_edges ? numEdges : 0), | ||||
| check_angle, | check_angle, | ||||
| split_angle, | split_angle, | ||||
| edge_to_loops, | edge_to_loops, | ||||
| {}); | {}); | ||||
| Show All 31 Lines | |||||
| * r_custom_loop_normals is expected to have normalized normals, or zero ones, | * r_custom_loop_normals is expected to have normalized normals, or zero ones, | ||||
| * in which case they will be replaced by default loop/vertex normal. | * in which case they will be replaced by default loop/vertex normal. | ||||
| */ | */ | ||||
| static void mesh_normals_loop_custom_set(const float (*positions)[3], | static void mesh_normals_loop_custom_set(const float (*positions)[3], | ||||
| const float (*vert_normals)[3], | const float (*vert_normals)[3], | ||||
| const int numVerts, | const int numVerts, | ||||
| const MEdge *medges, | const MEdge *medges, | ||||
| const int numEdges, | const int numEdges, | ||||
| const MLoop *mloops, | const int *corner_verts, | ||||
| const int *corner_edges, | |||||
| float (*r_custom_loop_normals)[3], | float (*r_custom_loop_normals)[3], | ||||
| const int numLoops, | const int numLoops, | ||||
| const MPoly *mpolys, | const MPoly *mpolys, | ||||
| const float (*poly_normals)[3], | const float (*poly_normals)[3], | ||||
| const int numPolys, | const int numPolys, | ||||
| MutableSpan<bool> sharp_edges, | MutableSpan<bool> sharp_edges, | ||||
| short (*r_clnors_data)[2], | short (*r_clnors_data)[2], | ||||
| const bool use_vertices) | const bool use_vertices) | ||||
| Show All 20 Lines | |||||
| BLI_SMALLSTACK_DECLARE(clnors_data, short *); | BLI_SMALLSTACK_DECLARE(clnors_data, short *); | ||||
| /* Compute current lnor spacearr. */ | /* Compute current lnor spacearr. */ | ||||
| BKE_mesh_normals_loop_split(positions, | BKE_mesh_normals_loop_split(positions, | ||||
| vert_normals, | vert_normals, | ||||
| numVerts, | numVerts, | ||||
| medges, | medges, | ||||
| numEdges, | numEdges, | ||||
| mloops, | corner_verts, | ||||
| corner_edges, | |||||
| loop_normals, | loop_normals, | ||||
| numLoops, | numLoops, | ||||
| mpolys, | mpolys, | ||||
| poly_normals, | poly_normals, | ||||
| numPolys, | numPolys, | ||||
| use_split_normals, | use_split_normals, | ||||
| split_angle, | split_angle, | ||||
| sharp_edges.data(), | sharp_edges.data(), | ||||
| ▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
| * 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! | ||||
| */ | */ | ||||
| if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { | if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { | ||||
| done_loops[i].set(); | done_loops[i].set(); | ||||
| continue; | continue; | ||||
| } | } | ||||
| LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; | LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; | ||||
| const MLoop *prev_ml = nullptr; | int corner_prev = -1; | ||||
| const float *org_nor = nullptr; | const float *org_nor = nullptr; | ||||
| while (loops) { | while (loops) { | ||||
| const int lidx = POINTER_AS_INT(loops->link); | const int lidx = POINTER_AS_INT(loops->link); | ||||
| const MLoop *ml = &mloops[lidx]; | float *nor = r_custom_loop_normals[lidx]; | ||||
| const int nidx = lidx; | |||||
| float *nor = r_custom_loop_normals[nidx]; | |||||
| if (!org_nor) { | if (!org_nor) { | ||||
| org_nor = nor; | org_nor = nor; | ||||
| } | } | ||||
| else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { | else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { | ||||
| /* Current normal differs too much from org one, we have to tag the edge between | /* Current normal differs too much from org one, we have to tag the edge between | ||||
| * previous loop's face and current's one as sharp. | * previous loop's face and current's one as sharp. | ||||
| * We know those two loops do not point to the same edge, | * We know those two loops do not point to the same edge, | ||||
| * since we do not allow reversed winding in a same smooth fan. */ | * since we do not allow reversed winding in a same smooth fan. */ | ||||
| const MPoly *mp = &mpolys[loop_to_poly[lidx]]; | const MPoly *mp = &mpolys[loop_to_poly[lidx]]; | ||||
| const MLoop *mlp = | const int mlp = (lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1; | ||||
| &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; | const int edge = corner_edges[lidx]; | ||||
| sharp_edges[(prev_ml->e == mlp->e) ? prev_ml->e : ml->e] = true; | const int edge_p = corner_edges[mlp]; | ||||
| const int prev_edge = corner_edges[corner_prev]; | |||||
| sharp_edges[prev_edge == edge_p ? prev_edge : edge] = true; | |||||
| org_nor = nor; | org_nor = nor; | ||||
| } | } | ||||
| prev_ml = ml; | corner_prev = lidx; | ||||
| loops = loops->next; | loops = loops->next; | ||||
| done_loops[lidx].set(); | done_loops[lidx].set(); | ||||
| } | } | ||||
| /* We also have to check between last and first loops, | /* We also have to check between last and first loops, | ||||
| * otherwise we may miss some sharp edges here! | * otherwise we may miss some sharp edges here! | ||||
| * This is just a simplified version of above while loop. | * This is just a simplified version of above while loop. | ||||
| * See T45984. */ | * See T45984. */ | ||||
| loops = lnors_spacearr.lspacearr[i]->loops; | loops = lnors_spacearr.lspacearr[i]->loops; | ||||
| if (loops && org_nor) { | if (loops && org_nor) { | ||||
| const int lidx = POINTER_AS_INT(loops->link); | const int lidx = POINTER_AS_INT(loops->link); | ||||
| const MLoop *ml = &mloops[lidx]; | float *nor = r_custom_loop_normals[lidx]; | ||||
| const int nidx = lidx; | |||||
| float *nor = r_custom_loop_normals[nidx]; | |||||
| if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { | if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { | ||||
| const MPoly *mp = &mpolys[loop_to_poly[lidx]]; | const MPoly *mp = &mpolys[loop_to_poly[lidx]]; | ||||
| const MLoop *mlp = | const int mlp = (lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1; | ||||
| &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; | const int edge = corner_edges[lidx]; | ||||
| sharp_edges[(prev_ml->e == mlp->e) ? prev_ml->e : ml->e] = true; | const int edge_p = corner_edges[mlp]; | ||||
| const int prev_edge = corner_edges[corner_prev]; | |||||
| sharp_edges[prev_edge == edge_p ? prev_edge : edge] = true; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* And now, recompute our new auto `loop_normals` and lnor spacearr! */ | /* And now, recompute our new auto `loop_normals` and lnor spacearr! */ | ||||
| BKE_lnor_spacearr_clear(&lnors_spacearr); | BKE_lnor_spacearr_clear(&lnors_spacearr); | ||||
| BKE_mesh_normals_loop_split(positions, | BKE_mesh_normals_loop_split(positions, | ||||
| vert_normals, | vert_normals, | ||||
| numVerts, | numVerts, | ||||
| medges, | medges, | ||||
| numEdges, | numEdges, | ||||
| mloops, | corner_verts, | ||||
| corner_edges, | |||||
| loop_normals, | loop_normals, | ||||
| numLoops, | numLoops, | ||||
| mpolys, | mpolys, | ||||
| poly_normals, | poly_normals, | ||||
| numPolys, | numPolys, | ||||
| use_split_normals, | use_split_normals, | ||||
| split_angle, | split_angle, | ||||
| sharp_edges.data(), | sharp_edges.data(), | ||||
| Show All 16 Lines | |||||
| 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 | ||||
| * give rather huge differences in computed 2D factors). */ | * give rather huge differences in computed 2D factors). */ | ||||
| LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; | LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; | ||||
| if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { | if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { | ||||
| BLI_assert(POINTER_AS_INT(loops) == i); | BLI_assert(POINTER_AS_INT(loops) == i); | ||||
| const int nidx = use_vertices ? int(mloops[i].v) : i; | const int nidx = use_vertices ? corner_verts[i] : i; | ||||
| float *nor = r_custom_loop_normals[nidx]; | float *nor = r_custom_loop_normals[nidx]; | ||||
| BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], nor, r_clnors_data[i]); | BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], nor, r_clnors_data[i]); | ||||
| done_loops[i].reset(); | done_loops[i].reset(); | ||||
| } | } | ||||
| else { | else { | ||||
| int avg_nor_count = 0; | int avg_nor_count = 0; | ||||
| float avg_nor[3]; | float avg_nor[3]; | ||||
| short clnor_data_tmp[2], *clnor_data; | short clnor_data_tmp[2], *clnor_data; | ||||
| zero_v3(avg_nor); | zero_v3(avg_nor); | ||||
| while (loops) { | while (loops) { | ||||
| const int lidx = POINTER_AS_INT(loops->link); | const int lidx = POINTER_AS_INT(loops->link); | ||||
| const int nidx = use_vertices ? int(mloops[lidx].v) : lidx; | const int nidx = use_vertices ? corner_verts[lidx] : lidx; | ||||
| float *nor = r_custom_loop_normals[nidx]; | float *nor = r_custom_loop_normals[nidx]; | ||||
| avg_nor_count++; | avg_nor_count++; | ||||
| add_v3_v3(avg_nor, nor); | add_v3_v3(avg_nor, nor); | ||||
| BLI_SMALLSTACK_PUSH(clnors_data, (short *)r_clnors_data[lidx]); | BLI_SMALLSTACK_PUSH(clnors_data, (short *)r_clnors_data[lidx]); | ||||
| loops = loops->next; | loops = loops->next; | ||||
| done_loops[lidx].reset(); | done_loops[lidx].reset(); | ||||
| Show All 14 Lines | |||||
| BKE_lnor_spacearr_free(&lnors_spacearr); | BKE_lnor_spacearr_free(&lnors_spacearr); | ||||
| } | } | ||||
| void BKE_mesh_normals_loop_custom_set(const float (*vert_positions)[3], | void BKE_mesh_normals_loop_custom_set(const float (*vert_positions)[3], | ||||
| const float (*vert_normals)[3], | const float (*vert_normals)[3], | ||||
| const int numVerts, | const int numVerts, | ||||
| const MEdge *medges, | const MEdge *medges, | ||||
| const int numEdges, | const int numEdges, | ||||
| const MLoop *mloops, | const int *corner_verts, | ||||
| const int *corner_edges, | |||||
| float (*r_custom_loop_normals)[3], | float (*r_custom_loop_normals)[3], | ||||
| const int numLoops, | const int numLoops, | ||||
| const MPoly *mpolys, | const MPoly *mpolys, | ||||
| const float (*poly_normals)[3], | const float (*poly_normals)[3], | ||||
| const int numPolys, | const int numPolys, | ||||
| bool *sharp_edges, | bool *sharp_edges, | ||||
| short (*r_clnors_data)[2]) | short (*r_clnors_data)[2]) | ||||
| { | { | ||||
| mesh_normals_loop_custom_set(vert_positions, | mesh_normals_loop_custom_set(vert_positions, | ||||
| vert_normals, | vert_normals, | ||||
| numVerts, | numVerts, | ||||
| medges, | medges, | ||||
| numEdges, | numEdges, | ||||
| mloops, | corner_verts, | ||||
| corner_edges, | |||||
| r_custom_loop_normals, | r_custom_loop_normals, | ||||
| numLoops, | numLoops, | ||||
| mpolys, | mpolys, | ||||
| poly_normals, | poly_normals, | ||||
| numPolys, | numPolys, | ||||
| {sharp_edges, numEdges}, | {sharp_edges, numEdges}, | ||||
| r_clnors_data, | r_clnors_data, | ||||
| false); | false); | ||||
| } | } | ||||
| void BKE_mesh_normals_loop_custom_from_verts_set(const float (*vert_positions)[3], | void BKE_mesh_normals_loop_custom_from_verts_set(const float (*vert_positions)[3], | ||||
| const float (*vert_normals)[3], | const float (*vert_normals)[3], | ||||
| float (*r_custom_vert_normals)[3], | float (*r_custom_vert_normals)[3], | ||||
| const int numVerts, | const int numVerts, | ||||
| const MEdge *medges, | const MEdge *medges, | ||||
| const int numEdges, | const int numEdges, | ||||
| const MLoop *mloops, | const int *corner_verts, | ||||
| const int *corner_edges, | |||||
| const int numLoops, | const int numLoops, | ||||
| const MPoly *mpolys, | const MPoly *mpolys, | ||||
| const float (*poly_normals)[3], | const float (*poly_normals)[3], | ||||
| const int numPolys, | const int numPolys, | ||||
| bool *sharp_edges, | bool *sharp_edges, | ||||
| short (*r_clnors_data)[2]) | short (*r_clnors_data)[2]) | ||||
| { | { | ||||
| mesh_normals_loop_custom_set(vert_positions, | mesh_normals_loop_custom_set(vert_positions, | ||||
| vert_normals, | vert_normals, | ||||
| numVerts, | numVerts, | ||||
| medges, | medges, | ||||
| numEdges, | numEdges, | ||||
| mloops, | corner_verts, | ||||
| corner_edges, | |||||
| r_custom_vert_normals, | r_custom_vert_normals, | ||||
| numLoops, | numLoops, | ||||
| mpolys, | mpolys, | ||||
| poly_normals, | poly_normals, | ||||
| numPolys, | numPolys, | ||||
| {sharp_edges, numEdges}, | {sharp_edges, numEdges}, | ||||
| r_clnors_data, | r_clnors_data, | ||||
| true); | true); | ||||
| Show All 13 Lines | |||||
| } | } | ||||
| else { | else { | ||||
| clnors = (short(*)[2])CustomData_add_layer( | clnors = (short(*)[2])CustomData_add_layer( | ||||
| &mesh->ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, nullptr, numloops); | &mesh->ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, nullptr, numloops); | ||||
| } | } | ||||
| const Span<float3> positions = mesh->vert_positions(); | const Span<float3> positions = mesh->vert_positions(); | ||||
| MutableSpan<MEdge> edges = mesh->edges_for_write(); | MutableSpan<MEdge> edges = mesh->edges_for_write(); | ||||
| const Span<MPoly> polys = mesh->polys(); | const Span<MPoly> polys = mesh->polys(); | ||||
| const Span<MLoop> loops = mesh->loops(); | |||||
| MutableAttributeAccessor attributes = mesh->attributes_for_write(); | MutableAttributeAccessor attributes = mesh->attributes_for_write(); | ||||
| SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>( | SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>( | ||||
| "sharp_edge", ATTR_DOMAIN_EDGE); | "sharp_edge", ATTR_DOMAIN_EDGE); | ||||
| mesh_normals_loop_custom_set(reinterpret_cast<const float(*)[3]>(positions.data()), | mesh_normals_loop_custom_set(reinterpret_cast<const float(*)[3]>(positions.data()), | ||||
| BKE_mesh_vertex_normals_ensure(mesh), | BKE_mesh_vertex_normals_ensure(mesh), | ||||
| positions.size(), | positions.size(), | ||||
| edges.data(), | edges.data(), | ||||
| edges.size(), | edges.size(), | ||||
| loops.data(), | mesh->corner_verts().data(), | ||||
| mesh->corner_edges().data(), | |||||
| r_custom_nors, | r_custom_nors, | ||||
| loops.size(), | mesh->totloop, | ||||
| polys.data(), | polys.data(), | ||||
| BKE_mesh_poly_normals_ensure(mesh), | BKE_mesh_poly_normals_ensure(mesh), | ||||
| polys.size(), | polys.size(), | ||||
| sharp_edges.span, | sharp_edges.span, | ||||
| clnors, | clnors, | ||||
| use_vertices); | use_vertices); | ||||
| sharp_edges.finish(); | sharp_edges.finish(); | ||||
| } | } | ||||
| void BKE_mesh_set_custom_normals(Mesh *mesh, float (*r_custom_loop_normals)[3]) | void BKE_mesh_set_custom_normals(Mesh *mesh, float (*r_custom_loop_normals)[3]) | ||||
| { | { | ||||
| mesh_set_custom_normals(mesh, r_custom_loop_normals, false); | mesh_set_custom_normals(mesh, r_custom_loop_normals, false); | ||||
| } | } | ||||
| void BKE_mesh_set_custom_normals_from_verts(Mesh *mesh, float (*r_custom_vert_normals)[3]) | void BKE_mesh_set_custom_normals_from_verts(Mesh *mesh, float (*r_custom_vert_normals)[3]) | ||||
| { | { | ||||
| mesh_set_custom_normals(mesh, r_custom_vert_normals, true); | mesh_set_custom_normals(mesh, r_custom_vert_normals, true); | ||||
| } | } | ||||
| void BKE_mesh_normals_loop_to_vertex(const int numVerts, | void BKE_mesh_normals_loop_to_vertex(const int numVerts, | ||||
| const MLoop *mloops, | const int *corner_verts, | ||||
| const int numLoops, | const int numLoops, | ||||
| const float (*clnors)[3], | const float (*clnors)[3], | ||||
| float (*r_vert_clnors)[3]) | float (*r_vert_clnors)[3]) | ||||
| { | { | ||||
| int *vert_loops_count = (int *)MEM_calloc_arrayN( | int *vert_loops_count = (int *)MEM_calloc_arrayN( | ||||
| size_t(numVerts), sizeof(*vert_loops_count), __func__); | size_t(numVerts), sizeof(*vert_loops_count), __func__); | ||||
| copy_vn_fl((float *)r_vert_clnors, 3 * numVerts, 0.0f); | copy_vn_fl((float *)r_vert_clnors, 3 * numVerts, 0.0f); | ||||
| int i; | int i; | ||||
| const MLoop *ml; | for (i = 0; i < numLoops; i++) { | ||||
| for (i = 0, ml = mloops; i < numLoops; i++, ml++) { | const int vert = corner_verts[i]; | ||||
| const uint v = ml->v; | add_v3_v3(r_vert_clnors[vert], clnors[i]); | ||||
| vert_loops_count[vert]++; | |||||
| add_v3_v3(r_vert_clnors[v], clnors[i]); | |||||
| vert_loops_count[v]++; | |||||
| } | } | ||||
| for (i = 0; i < numVerts; i++) { | for (i = 0; i < numVerts; i++) { | ||||
| mul_v3_fl(r_vert_clnors[i], 1.0f / float(vert_loops_count[i])); | mul_v3_fl(r_vert_clnors[i], 1.0f / float(vert_loops_count[i])); | ||||
| } | } | ||||
| MEM_freeN(vert_loops_count); | MEM_freeN(vert_loops_count); | ||||
| } | } | ||||
| #undef LNOR_SPACE_TRIGO_THRESHOLD | #undef LNOR_SPACE_TRIGO_THRESHOLD | ||||
| /** \} */ | /** \} */ | ||||
| Context not available. | |||||