Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/geometry_component_mesh.cc
| Show First 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | |||||
| namespace blender::bke { | namespace blender::bke { | ||||
| template<typename T> | template<typename T> | ||||
| static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, | static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, | ||||
| const VArray<T> &old_values, | const VArray<T> &old_values, | ||||
| MutableSpan<T> r_values) | MutableSpan<T> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totvert); | BLI_assert(r_values.size() == mesh.totvert); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_verts = mesh.corner_verts(); | ||||
| attribute_math::DefaultMixer<T> mixer(r_values); | attribute_math::DefaultMixer<T> mixer(r_values); | ||||
| for (const int corner : IndexRange(mesh.totloop)) { | |||||
| for (const int loop_index : IndexRange(mesh.totloop)) { | mixer.mix_in(corner_verts[corner], old_values[corner]); | ||||
| const T value = old_values[loop_index]; | |||||
| const MLoop &loop = loops[loop_index]; | |||||
| const int point_index = loop.v; | |||||
| mixer.mix_in(point_index, value); | |||||
| } | } | ||||
| mixer.finalize(); | mixer.finalize(); | ||||
| } | } | ||||
| /* A vertex is selected if all connected face corners were selected and it is not loose. */ | /* A vertex is selected if all connected face corners were selected and it is not loose. */ | ||||
| template<> | template<> | ||||
| void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, | void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, | ||||
| const VArray<bool> &old_values, | const VArray<bool> &old_values, | ||||
| MutableSpan<bool> r_values) | MutableSpan<bool> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totvert); | BLI_assert(r_values.size() == mesh.totvert); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_verts = mesh.corner_verts(); | ||||
| Array<bool> loose_verts(mesh.totvert, true); | Array<bool> loose_verts(mesh.totvert, true); | ||||
| r_values.fill(true); | r_values.fill(true); | ||||
| for (const int loop_index : IndexRange(mesh.totloop)) { | for (const int corner : IndexRange(mesh.totloop)) { | ||||
| const MLoop &loop = loops[loop_index]; | const int point_index = corner_verts[corner]; | ||||
| const int point_index = loop.v; | |||||
| loose_verts[point_index] = false; | loose_verts[point_index] = false; | ||||
| if (!old_values[loop_index]) { | if (!old_values[corner]) { | ||||
| r_values[point_index] = false; | r_values[point_index] = false; | ||||
| } | } | ||||
| } | } | ||||
| /* Deselect loose vertices without corners that are still selected from the 'true' default. */ | /* Deselect loose vertices without corners that are still selected from the 'true' default. */ | ||||
| /* The record fact says that the value is true. | /* The record fact says that the value is true. | ||||
| *Writing to the array from different threads is okay because each thread sets the same value. */ | *Writing to the array from different threads is okay because each thread sets the same value. */ | ||||
| threading::parallel_for(loose_verts.index_range(), 2048, [&](const IndexRange range) { | threading::parallel_for(loose_verts.index_range(), 2048, [&](const IndexRange range) { | ||||
| for (const int vert_index : range) { | for (const int vert_index : range) { | ||||
| if (loose_verts[vert_index]) { | if (loose_verts[vert_index]) { | ||||
| r_values[vert_index] = false; | r_values[vert_index] = false; | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray &varray) | static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray &varray) | ||||
| { | { | ||||
| GArray<> values(varray.type(), mesh.totvert); | GArray<> values(varray.type(), mesh.totvert); | ||||
| attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | ||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||
| if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { | if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { | ||||
| /* We compute all interpolated values at once, because for this interpolation, one has to | /* We compute all interpolated values at once, because for this interpolation, one has to | ||||
| * iterate over all loops anyway. */ | * iterate over all corners anyway. */ | ||||
| adapt_mesh_domain_corner_to_point_impl<T>( | adapt_mesh_domain_corner_to_point_impl<T>( | ||||
| mesh, varray.typed<T>(), values.as_mutable_span().typed<T>()); | mesh, varray.typed<T>(), values.as_mutable_span().typed<T>()); | ||||
| } | } | ||||
| }); | }); | ||||
| return GVArray::ForGArray(std::move(values)); | return GVArray::ForGArray(std::move(values)); | ||||
| } | } | ||||
| /** | /** | ||||
| * Each corner's value is simply a copy of the value at its vertex. | * Each corner's value is simply a copy of the value at its vertex. | ||||
| */ | */ | ||||
| static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray &varray) | static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray &varray) | ||||
| { | { | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_verts = mesh.corner_verts(); | ||||
| GVArray new_varray; | GVArray new_varray; | ||||
| attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | ||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||
| new_varray = VArray<T>::ForFunc(mesh.totloop, | new_varray = VArray<T>::ForFunc( | ||||
| [loops, varray = varray.typed<T>()](const int64_t loop_index) { | mesh.totloop, [corner_verts, varray = varray.typed<T>()](const int64_t corner) { | ||||
| const int vertex_index = loops[loop_index].v; | return varray[corner_verts[corner]]; | ||||
| return varray[vertex_index]; | }); | ||||
| }); | |||||
| }); | }); | ||||
| return new_varray; | return new_varray; | ||||
| } | } | ||||
| static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray &varray) | static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray &varray) | ||||
| { | { | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| Show All 35 Lines | |||||
| template<typename T> | template<typename T> | ||||
| static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, | static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, | ||||
| const VArray<T> &old_values, | const VArray<T> &old_values, | ||||
| MutableSpan<T> r_values) | MutableSpan<T> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totedge); | BLI_assert(r_values.size() == mesh.totedge); | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_edges = mesh.corner_edges(); | ||||
| attribute_math::DefaultMixer<T> mixer(r_values); | attribute_math::DefaultMixer<T> mixer(r_values); | ||||
| for (const int poly_index : polys.index_range()) { | for (const int poly_index : polys.index_range()) { | ||||
| const MPoly &poly = polys[poly_index]; | const MPoly &poly = polys[poly_index]; | ||||
| /* For every edge, mix values from the two adjacent corners (the current and next corner). */ | /* For every edge, mix values from the two adjacent corners (the current and next corner). */ | ||||
| for (const int i : IndexRange(poly.totloop)) { | for (const int i : IndexRange(poly.totloop)) { | ||||
| const int next_i = (i + 1) % poly.totloop; | const int next_i = (i + 1) % poly.totloop; | ||||
| const int loop_i = poly.loopstart + i; | const int loop_i = poly.loopstart + i; | ||||
| const int next_loop_i = poly.loopstart + next_i; | const int next_loop_i = poly.loopstart + next_i; | ||||
| const MLoop &loop = loops[loop_i]; | const int edge_index = corner_edges[loop_i]; | ||||
| const int edge_index = loop.e; | |||||
| mixer.mix_in(edge_index, old_values[loop_i]); | mixer.mix_in(edge_index, old_values[loop_i]); | ||||
| mixer.mix_in(edge_index, old_values[next_loop_i]); | mixer.mix_in(edge_index, old_values[next_loop_i]); | ||||
| } | } | ||||
| } | } | ||||
| mixer.finalize(); | mixer.finalize(); | ||||
| } | } | ||||
| /* An edge is selected if all corners on adjacent faces were selected. */ | /* An edge is selected if all corners on adjacent faces were selected. */ | ||||
| template<> | template<> | ||||
| void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, | void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, | ||||
| const VArray<bool> &old_values, | const VArray<bool> &old_values, | ||||
| MutableSpan<bool> r_values) | MutableSpan<bool> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totedge); | BLI_assert(r_values.size() == mesh.totedge); | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_edges = mesh.corner_edges(); | ||||
| r_values.fill(true); | r_values.fill(true); | ||||
| for (const int poly_index : polys.index_range()) { | for (const int poly_index : polys.index_range()) { | ||||
| const MPoly &poly = polys[poly_index]; | const MPoly &poly = polys[poly_index]; | ||||
| for (const int i : IndexRange(poly.totloop)) { | for (const int i : IndexRange(poly.totloop)) { | ||||
| const int next_i = (i + 1) % poly.totloop; | const int next_i = (i + 1) % poly.totloop; | ||||
| const int loop_i = poly.loopstart + i; | const int loop_i = poly.loopstart + i; | ||||
| const int next_loop_i = poly.loopstart + next_i; | const int next_loop_i = poly.loopstart + next_i; | ||||
| const MLoop &loop = loops[loop_i]; | const int edge_index = corner_edges[loop_i]; | ||||
| const int edge_index = loop.e; | |||||
| if (!old_values[loop_i] || !old_values[next_loop_i]) { | if (!old_values[loop_i] || !old_values[next_loop_i]) { | ||||
| r_values[edge_index] = false; | r_values[edge_index] = false; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| const bke::LooseEdgeCache &loose_edges = mesh.loose_edges(); | const bke::LooseEdgeCache &loose_edges = mesh.loose_edges(); | ||||
| Show All 24 Lines | |||||
| template<typename T> | template<typename T> | ||||
| void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, | void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, | ||||
| const VArray<T> &old_values, | const VArray<T> &old_values, | ||||
| MutableSpan<T> r_values) | MutableSpan<T> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totvert); | BLI_assert(r_values.size() == mesh.totvert); | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_verts = mesh.corner_verts(); | ||||
| attribute_math::DefaultMixer<T> mixer(r_values); | attribute_math::DefaultMixer<T> mixer(r_values); | ||||
| for (const int poly_index : polys.index_range()) { | for (const int poly_index : polys.index_range()) { | ||||
| const MPoly &poly = polys[poly_index]; | const MPoly &poly = polys[poly_index]; | ||||
| const T value = old_values[poly_index]; | const T value = old_values[poly_index]; | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) { | ||||
| const MLoop &loop = loops[loop_index]; | mixer.mix_in(vert, value); | ||||
| const int point_index = loop.v; | |||||
| mixer.mix_in(point_index, value); | |||||
| } | } | ||||
| } | } | ||||
| mixer.finalize(); | mixer.finalize(); | ||||
| } | } | ||||
| /* A vertex is selected if any of the connected faces were selected. */ | /* A vertex is selected if any of the connected faces were selected. */ | ||||
| template<> | template<> | ||||
| void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, | void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, | ||||
| const VArray<bool> &old_values, | const VArray<bool> &old_values, | ||||
| MutableSpan<bool> r_values) | MutableSpan<bool> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totvert); | BLI_assert(r_values.size() == mesh.totvert); | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_verts = mesh.corner_verts(); | ||||
| r_values.fill(false); | r_values.fill(false); | ||||
| threading::parallel_for(polys.index_range(), 2048, [&](const IndexRange range) { | threading::parallel_for(polys.index_range(), 2048, [&](const IndexRange range) { | ||||
| for (const int poly_index : range) { | for (const int poly_index : range) { | ||||
| if (old_values[poly_index]) { | if (old_values[poly_index]) { | ||||
| const MPoly &poly = polys[poly_index]; | const MPoly &poly = polys[poly_index]; | ||||
| for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { | for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) { | ||||
| r_values[loop.v] = true; | r_values[vert] = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| static GVArray adapt_mesh_domain_face_to_point(const Mesh &mesh, const GVArray &varray) | static GVArray adapt_mesh_domain_face_to_point(const Mesh &mesh, const GVArray &varray) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
| template<typename T> | template<typename T> | ||||
| void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, | void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, | ||||
| const VArray<T> &old_values, | const VArray<T> &old_values, | ||||
| MutableSpan<T> r_values) | MutableSpan<T> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totedge); | BLI_assert(r_values.size() == mesh.totedge); | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_edges = mesh.corner_edges(); | ||||
| attribute_math::DefaultMixer<T> mixer(r_values); | attribute_math::DefaultMixer<T> mixer(r_values); | ||||
| for (const int poly_index : polys.index_range()) { | for (const int poly_index : polys.index_range()) { | ||||
| const MPoly &poly = polys[poly_index]; | const MPoly &poly = polys[poly_index]; | ||||
| const T value = old_values[poly_index]; | const T value = old_values[poly_index]; | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int edge : corner_edges.slice(poly.loopstart, poly.totloop)) { | ||||
| const MLoop &loop = loops[loop_index]; | mixer.mix_in(edge, value); | ||||
| mixer.mix_in(loop.e, value); | |||||
| } | } | ||||
| } | } | ||||
| mixer.finalize(); | mixer.finalize(); | ||||
| } | } | ||||
| /* An edge is selected if any connected face was selected. */ | /* An edge is selected if any connected face was selected. */ | ||||
| template<> | template<> | ||||
| void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, | void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, | ||||
| const VArray<bool> &old_values, | const VArray<bool> &old_values, | ||||
| MutableSpan<bool> r_values) | MutableSpan<bool> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totedge); | BLI_assert(r_values.size() == mesh.totedge); | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_edges = mesh.corner_edges(); | ||||
| r_values.fill(false); | r_values.fill(false); | ||||
| threading::parallel_for(polys.index_range(), 2048, [&](const IndexRange range) { | threading::parallel_for(polys.index_range(), 2048, [&](const IndexRange range) { | ||||
| for (const int poly_index : range) { | for (const int poly_index : range) { | ||||
| if (old_values[poly_index]) { | if (old_values[poly_index]) { | ||||
| const MPoly &poly = polys[poly_index]; | const MPoly &poly = polys[poly_index]; | ||||
| for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { | for (const int edge : corner_edges.slice(poly.loopstart, poly.totloop)) { | ||||
| r_values[loop.e] = true; | r_values[edge] = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| static GVArray adapt_mesh_domain_face_to_edge(const Mesh &mesh, const GVArray &varray) | static GVArray adapt_mesh_domain_face_to_edge(const Mesh &mesh, const GVArray &varray) | ||||
| { | { | ||||
| GArray<> values(varray.type(), mesh.totedge); | GArray<> values(varray.type(), mesh.totedge); | ||||
| attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | ||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||
| if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { | if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { | ||||
| adapt_mesh_domain_face_to_edge_impl<T>( | adapt_mesh_domain_face_to_edge_impl<T>( | ||||
| mesh, varray.typed<T>(), values.as_mutable_span().typed<T>()); | mesh, varray.typed<T>(), values.as_mutable_span().typed<T>()); | ||||
| } | } | ||||
| }); | }); | ||||
| return GVArray::ForGArray(std::move(values)); | return GVArray::ForGArray(std::move(values)); | ||||
| } | } | ||||
| static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &varray) | static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &varray) | ||||
| { | { | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_verts = mesh.corner_verts(); | ||||
| GVArray new_varray; | GVArray new_varray; | ||||
| attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | ||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||
| if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { | if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { | ||||
| if constexpr (std::is_same_v<T, bool>) { | if constexpr (std::is_same_v<T, bool>) { | ||||
| new_varray = VArray<T>::ForFunc( | new_varray = VArray<T>::ForFunc( | ||||
| mesh.totpoly, [loops, polys, varray = varray.typed<bool>()](const int face_index) { | mesh.totpoly, | ||||
| [corner_verts, polys, varray = varray.typed<bool>()](const int face_index) { | |||||
| /* A face is selected if all of its vertices were selected. */ | /* A face is selected if all of its vertices were selected. */ | ||||
| const MPoly &poly = polys[face_index]; | const MPoly &poly = polys[face_index]; | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) { | ||||
| const MLoop &loop = loops[loop_index]; | if (!varray[vert]) { | ||||
| if (!varray[loop.v]) { | |||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| return true; | return true; | ||||
| }); | }); | ||||
| } | } | ||||
| else { | else { | ||||
| new_varray = VArray<T>::ForFunc( | new_varray = VArray<T>::ForFunc( | ||||
| mesh.totpoly, [loops, polys, varray = varray.typed<T>()](const int face_index) { | mesh.totpoly, [corner_verts, polys, varray = varray.typed<T>()](const int face_index) { | ||||
| T return_value; | T return_value; | ||||
| attribute_math::DefaultMixer<T> mixer({&return_value, 1}); | attribute_math::DefaultMixer<T> mixer({&return_value, 1}); | ||||
| const MPoly &poly = polys[face_index]; | const MPoly &poly = polys[face_index]; | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) { | ||||
| const MLoop &loop = loops[loop_index]; | mixer.mix_in(0, varray[vert]); | ||||
| const T value = varray[loop.v]; | |||||
| mixer.mix_in(0, value); | |||||
| } | } | ||||
| mixer.finalize(); | mixer.finalize(); | ||||
| return return_value; | return return_value; | ||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| return new_varray; | return new_varray; | ||||
| Show All 34 Lines | |||||
| template<typename T> | template<typename T> | ||||
| void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, | void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, | ||||
| const VArray<T> &old_values, | const VArray<T> &old_values, | ||||
| MutableSpan<T> r_values) | MutableSpan<T> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totloop); | BLI_assert(r_values.size() == mesh.totloop); | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_edges = mesh.corner_edges(); | ||||
| attribute_math::DefaultMixer<T> mixer(r_values); | attribute_math::DefaultMixer<T> mixer(r_values); | ||||
| for (const int poly_index : polys.index_range()) { | for (const int poly_index : polys.index_range()) { | ||||
| const MPoly &poly = polys[poly_index]; | const MPoly &poly = polys[poly_index]; | ||||
| /* For every corner, mix the values from the adjacent edges on the face. */ | /* For every corner, mix the values from the adjacent edges on the face. */ | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | ||||
| const int loop_index_prev = mesh_topology::poly_loop_prev(poly, loop_index); | const int loop_index_prev = mesh_topology::poly_loop_prev(poly, loop_index); | ||||
| const MLoop &loop = loops[loop_index]; | const int edge = corner_edges[loop_index]; | ||||
| const MLoop &loop_prev = loops[loop_index_prev]; | const int edge_prev = corner_edges[loop_index_prev]; | ||||
| mixer.mix_in(loop_index, old_values[loop.e]); | mixer.mix_in(loop_index, old_values[edge]); | ||||
| mixer.mix_in(loop_index, old_values[loop_prev.e]); | mixer.mix_in(loop_index, old_values[edge_prev]); | ||||
| } | } | ||||
| } | } | ||||
| mixer.finalize(); | mixer.finalize(); | ||||
| } | } | ||||
| /* A corner is selected if its two adjacent edges were selected. */ | /* A corner is selected if its two adjacent edges were selected. */ | ||||
| template<> | template<> | ||||
| void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, | void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, | ||||
| const VArray<bool> &old_values, | const VArray<bool> &old_values, | ||||
| MutableSpan<bool> r_values) | MutableSpan<bool> r_values) | ||||
| { | { | ||||
| BLI_assert(r_values.size() == mesh.totloop); | BLI_assert(r_values.size() == mesh.totloop); | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_edges = mesh.corner_edges(); | ||||
| r_values.fill(false); | r_values.fill(false); | ||||
| threading::parallel_for(polys.index_range(), 2048, [&](const IndexRange range) { | threading::parallel_for(polys.index_range(), 2048, [&](const IndexRange range) { | ||||
| for (const int poly_index : range) { | for (const int poly_index : range) { | ||||
| const MPoly &poly = polys[poly_index]; | const MPoly &poly = polys[poly_index]; | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | ||||
| const int loop_index_prev = mesh_topology::poly_loop_prev(poly, loop_index); | const int loop_index_prev = mesh_topology::poly_loop_prev(poly, loop_index); | ||||
| const MLoop &loop = loops[loop_index]; | const int edge = corner_edges[loop_index]; | ||||
| const MLoop &loop_prev = loops[loop_index_prev]; | const int edge_prev = corner_edges[loop_index_prev]; | ||||
| if (old_values[loop.e] && old_values[loop_prev.e]) { | if (old_values[edge] && old_values[edge_prev]) { | ||||
| r_values[loop_index] = true; | r_values[loop_index] = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| static GVArray adapt_mesh_domain_edge_to_corner(const Mesh &mesh, const GVArray &varray) | static GVArray adapt_mesh_domain_edge_to_corner(const Mesh &mesh, const GVArray &varray) | ||||
| ▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| }); | }); | ||||
| return GVArray::ForGArray(std::move(values)); | return GVArray::ForGArray(std::move(values)); | ||||
| } | } | ||||
| static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &varray) | static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &varray) | ||||
| { | { | ||||
| const Span<MPoly> polys = mesh.polys(); | const Span<MPoly> polys = mesh.polys(); | ||||
| const Span<MLoop> loops = mesh.loops(); | const Span<int> corner_edges = mesh.corner_edges(); | ||||
| GVArray new_varray; | GVArray new_varray; | ||||
| attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { | ||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||
| if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { | if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { | ||||
| if constexpr (std::is_same_v<T, bool>) { | if constexpr (std::is_same_v<T, bool>) { | ||||
| /* A face is selected if all of its edges are selected. */ | /* A face is selected if all of its edges are selected. */ | ||||
| new_varray = VArray<bool>::ForFunc( | new_varray = VArray<bool>::ForFunc( | ||||
| polys.size(), [loops, polys, varray = varray.typed<T>()](const int face_index) { | polys.size(), [corner_edges, polys, varray = varray.typed<T>()](const int face_index) { | ||||
| const MPoly &poly = polys[face_index]; | const MPoly &poly = polys[face_index]; | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int edge : corner_edges.slice(poly.loopstart, poly.totloop)) { | ||||
| const MLoop &loop = loops[loop_index]; | if (!varray[edge]) { | ||||
| if (!varray[loop.e]) { | |||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| return true; | return true; | ||||
| }); | }); | ||||
| } | } | ||||
| else { | else { | ||||
| new_varray = VArray<T>::ForFunc( | new_varray = VArray<T>::ForFunc( | ||||
| polys.size(), [loops, polys, varray = varray.typed<T>()](const int face_index) { | polys.size(), [corner_edges, polys, varray = varray.typed<T>()](const int face_index) { | ||||
| T return_value; | T return_value; | ||||
| attribute_math::DefaultMixer<T> mixer({&return_value, 1}); | attribute_math::DefaultMixer<T> mixer({&return_value, 1}); | ||||
| const MPoly &poly = polys[face_index]; | const MPoly &poly = polys[face_index]; | ||||
| for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { | for (const int edge : corner_edges.slice(poly.loopstart, poly.totloop)) { | ||||
| const MLoop &loop = loops[loop_index]; | mixer.mix_in(0, varray[edge]); | ||||
| const T value = varray[loop.e]; | |||||
| mixer.mix_in(0, value); | |||||
| } | } | ||||
| mixer.finalize(); | mixer.finalize(); | ||||
| return return_value; | return return_value; | ||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| return new_varray; | return new_varray; | ||||
| ▲ Show 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | |||||
| BuiltinAttributeProvider::Writable, | BuiltinAttributeProvider::Writable, | ||||
| BuiltinAttributeProvider::Deletable, | BuiltinAttributeProvider::Deletable, | ||||
| face_access, | face_access, | ||||
| make_array_read_attribute<int>, | make_array_read_attribute<int>, | ||||
| make_array_write_attribute<int>, | make_array_write_attribute<int>, | ||||
| nullptr, | nullptr, | ||||
| AttributeValidator{&material_index_clamp}); | AttributeValidator{&material_index_clamp}); | ||||
| /* Note: This clamping is more of a last resort, since it's quite easy to make an | |||||
| * invalid mesh that will crash Blender by arbitrarily editing this attribute. */ | |||||
| static const auto int_index_clamp = mf::build::SI1_SO<int, int>( | |||||
| "Index Validate", | |||||
| [](int value) { return std::max(value, 0); }, | |||||
| mf::build::exec_presets::AllSpanOrSingle()); | |||||
| static BuiltinCustomDataLayerProvider corner_vert(".corner_vert", | |||||
| ATTR_DOMAIN_CORNER, | |||||
| CD_PROP_INT32, | |||||
| CD_PROP_INT32, | |||||
| BuiltinAttributeProvider::NonCreatable, | |||||
| BuiltinAttributeProvider::Writable, | |||||
| BuiltinAttributeProvider::NonDeletable, | |||||
| corner_access, | |||||
| make_array_read_attribute<int>, | |||||
| make_array_write_attribute<int>, | |||||
| nullptr, | |||||
| AttributeValidator{&int_index_clamp}); | |||||
| static BuiltinCustomDataLayerProvider corner_edge(".corner_edge", | |||||
| ATTR_DOMAIN_CORNER, | |||||
| CD_PROP_INT32, | |||||
| CD_PROP_INT32, | |||||
| BuiltinAttributeProvider::NonCreatable, | |||||
| BuiltinAttributeProvider::Writable, | |||||
| BuiltinAttributeProvider::NonDeletable, | |||||
| corner_access, | |||||
| make_array_read_attribute<int>, | |||||
| make_array_write_attribute<int>, | |||||
| nullptr, | |||||
| AttributeValidator{&int_index_clamp}); | |||||
| static BuiltinCustomDataLayerProvider shade_smooth( | static BuiltinCustomDataLayerProvider shade_smooth( | ||||
| "shade_smooth", | "shade_smooth", | ||||
| ATTR_DOMAIN_FACE, | ATTR_DOMAIN_FACE, | ||||
| CD_PROP_BOOL, | CD_PROP_BOOL, | ||||
| CD_MPOLY, | CD_MPOLY, | ||||
| BuiltinAttributeProvider::NonCreatable, | BuiltinAttributeProvider::NonCreatable, | ||||
| BuiltinAttributeProvider::Writable, | BuiltinAttributeProvider::Writable, | ||||
| BuiltinAttributeProvider::NonDeletable, | BuiltinAttributeProvider::NonDeletable, | ||||
| Show All 28 Lines | |||||
| nullptr); | nullptr); | ||||
| static VertexGroupsAttributeProvider vertex_groups; | static VertexGroupsAttributeProvider vertex_groups; | ||||
| static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); | static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); | ||||
| static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); | static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); | ||||
| static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access); | static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access); | ||||
| static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access); | static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access); | ||||
| return ComponentAttributeProviders( | return ComponentAttributeProviders({&position, | ||||
| {&position, &id, &material_index, &shade_smooth, &sharp_edge, &normal, &crease}, | &corner_vert, | ||||
| {&corner_custom_data, | &corner_edge, | ||||
| &vertex_groups, | &id, | ||||
| &point_custom_data, | &material_index, | ||||
| &edge_custom_data, | &shade_smooth, | ||||
| &face_custom_data}); | &sharp_edge, | ||||
| &normal, | |||||
| &crease}, | |||||
| {&corner_custom_data, | |||||
| &vertex_groups, | |||||
| &point_custom_data, | |||||
| &edge_custom_data, | |||||
| &face_custom_data}); | |||||
| } | } | ||||
| static AttributeAccessorFunctions get_mesh_accessor_functions() | static AttributeAccessorFunctions get_mesh_accessor_functions() | ||||
| { | { | ||||
| static const ComponentAttributeProviders providers = create_attribute_providers_for_mesh(); | static const ComponentAttributeProviders providers = create_attribute_providers_for_mesh(); | ||||
| AttributeAccessorFunctions fn = | AttributeAccessorFunctions fn = | ||||
| attribute_accessor_functions::accessor_functions_for_providers<providers>(); | attribute_accessor_functions::accessor_functions_for_providers<providers>(); | ||||
| fn.domain_size = [](const void *owner, const eAttrDomain domain) { | fn.domain_size = [](const void *owner, const eAttrDomain domain) { | ||||
| ▲ Show 20 Lines • Show All 65 Lines • Show Last 20 Lines | |||||