From 80f7f1070f177ae543f2fa35dd5d458e87ff30c1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 9 Mar 2021 13:39:05 -0500 Subject: Geometry Nodes: Add Attribute interpolation for polygon domains This commit adds interpolation to and from attribute on the polygon domain. Interpolation is done automatically when a node uses attributes on two different domains. The following are the new interpolations and corresponding simple test cases: - **Point to Polygon**: Painting the shade smooth attribute in weight paint mode - **Polygon to Point**: Moving points along a normal based on the material index - **Polygon to Corner**: Scaling a UV map with the material index before sampling a texture {F9881516} This is also necessary for an improved implementation of the `normal` attribute. Differential Revision: https://developer.blender.org/D10393 --- .../blenkernel/intern/geometry_component_mesh.cc | 167 +++++++++++++++++++++ 1 file changed, 167 insertions(+) (limited to 'source') diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 31809b1ffec..53defc89b7e 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -256,6 +256,157 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, return new_attribute; } +/** + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template +static void adapt_mesh_domain_corner_to_polygon_impl(const Mesh &mesh, + Span old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + attribute_math::DefaultMixer mixer(r_values); + + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const T value = old_values[loop_index]; + mixer.mix_in(poly_index, value); + } + } + + mixer.finalize(); +} + +static ReadAttributePtr adapt_mesh_domain_corner_to_polygon(const Mesh &mesh, + ReadAttributePtr attribute) +{ + ReadAttributePtr new_attribute; + const CustomDataType data_type = attribute->custom_data_type(); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v>) { + Array values(mesh.totpoly); + adapt_mesh_domain_corner_to_polygon_impl(mesh, attribute->get_span(), values); + new_attribute = std::make_unique>(ATTR_DOMAIN_POINT, + std::move(values)); + } + }); + return new_attribute; +} + +template +void adapt_mesh_domain_polygon_to_point_impl(const Mesh &mesh, + Span old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + attribute_math::DefaultMixer mixer(r_values); + + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + const T value = old_values[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int point_index = loop.v; + mixer.mix_in(point_index, value); + } + } + + mixer.finalize(); +} + +static ReadAttributePtr adapt_mesh_domain_polygon_to_point(const Mesh &mesh, + ReadAttributePtr attribute) +{ + ReadAttributePtr new_attribute; + const CustomDataType data_type = attribute->custom_data_type(); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v>) { + Array values(mesh.totvert); + adapt_mesh_domain_polygon_to_point_impl(mesh, attribute->get_span(), values); + new_attribute = std::make_unique>(ATTR_DOMAIN_POINT, + std::move(values)); + } + }); + return new_attribute; +} + +template +void adapt_mesh_domain_polygon_to_corner_impl(const Mesh &mesh, + const Span old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totloop); + + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + MutableSpan poly_corner_values = r_values.slice(poly.loopstart, poly.totloop); + poly_corner_values.fill(old_values[poly_index]); + } +} + +static ReadAttributePtr adapt_mesh_domain_polygon_to_corner(const Mesh &mesh, + ReadAttributePtr attribute) +{ + ReadAttributePtr new_attribute; + const CustomDataType data_type = attribute->custom_data_type(); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v>) { + Array values(mesh.totloop); + adapt_mesh_domain_polygon_to_corner_impl(mesh, attribute->get_span(), values); + new_attribute = std::make_unique>(ATTR_DOMAIN_POINT, + std::move(values)); + } + }); + return new_attribute; +} + +/** + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template +static void adapt_mesh_domain_point_to_polygon_impl(const Mesh &mesh, + const Span old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + attribute_math::DefaultMixer mixer(r_values); + + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + MLoop &loop = mesh.mloop[loop_index]; + const int point_index = loop.v; + mixer.mix_in(poly_index, old_values[point_index]); + } + } + mixer.finalize(); +} + +static ReadAttributePtr adapt_mesh_domain_point_to_polygon(const Mesh &mesh, + ReadAttributePtr attribute) +{ + ReadAttributePtr new_attribute; + const CustomDataType data_type = attribute->custom_data_type(); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v>) { + Array values(mesh.totpoly); + adapt_mesh_domain_point_to_polygon_impl(mesh, attribute->get_span(), values); + new_attribute = std::make_unique>(ATTR_DOMAIN_POINT, + std::move(values)); + } + }); + return new_attribute; +} + } // namespace blender::bke ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute, @@ -277,6 +428,8 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr switch (new_domain) { case ATTR_DOMAIN_POINT: return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute)); + case ATTR_DOMAIN_POLYGON: + return blender::bke::adapt_mesh_domain_corner_to_polygon(*mesh_, std::move(attribute)); default: break; } @@ -286,9 +439,23 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr switch (new_domain) { case ATTR_DOMAIN_CORNER: return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute)); + case ATTR_DOMAIN_POLYGON: + return blender::bke::adapt_mesh_domain_point_to_polygon(*mesh_, std::move(attribute)); + default: + break; + } + break; + } + case ATTR_DOMAIN_POLYGON: { + switch (new_domain) { + case ATTR_DOMAIN_POINT: + return blender::bke::adapt_mesh_domain_polygon_to_point(*mesh_, std::move(attribute)); + case ATTR_DOMAIN_CORNER: + return blender::bke::adapt_mesh_domain_polygon_to_corner(*mesh_, std::move(attribute)); default: break; } + break; } default: break; -- cgit v1.2.3