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
This commit is contained in:
Hans Goudey 2021-03-09 13:39:05 -05:00
parent 996586860b
commit 80f7f1070f
Notes: blender-bot 2023-02-14 10:43:47 +01:00
Referenced by issue #84297, Expose more built-in data as attributes
1 changed files with 167 additions and 0 deletions

View File

@ -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<typename T>
static void adapt_mesh_domain_corner_to_polygon_impl(const Mesh &mesh,
Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
attribute_math::DefaultMixer<T> 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<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_corner_to_polygon_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
}
});
return new_attribute;
}
template<typename T>
void adapt_mesh_domain_polygon_to_point_impl(const Mesh &mesh,
Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
attribute_math::DefaultMixer<T> 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<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
adapt_mesh_domain_polygon_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
std::move(values));
}
});
return new_attribute;
}
template<typename T>
void adapt_mesh_domain_polygon_to_corner_impl(const Mesh &mesh,
const Span<T> old_values,
MutableSpan<T> 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<T> 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<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
adapt_mesh_domain_polygon_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(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<typename T>
static void adapt_mesh_domain_point_to_polygon_impl(const Mesh &mesh,
const Span<T> old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
attribute_math::DefaultMixer<T> 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<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_point_to_polygon_impl<T>(mesh, attribute->get_span<T>(), values);
new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(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;