Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-06-14 23:13:43 +0300
committerHans Goudey <h.goudey@me.com>2021-06-14 23:13:43 +0300
commit61fdc450342e28007faea2e1696afc2ff034d6b9 (patch)
treea5e104a0da07402e916af1be05cfb8f0fe1044a2
parentbf7f918a0ef7144f1015905fae22203528cc9100 (diff)
Geometry Nodes: Join dynamic curve attributes in the join geometry node
This commit lets the join geometry node transfer dynamic attributes to the result, the same way that point cloud and mesh attributes are joined. The implementation is different though, because of an optimization implemented for curves to avoid copying splines. The result attribute is added with the highest priority domain (points over splines), and the highest complexity data type. If one curve had the attribute on the spline domain but not others, the point domain values will be used. Generally this is a bit lower level than I would have liked this code to be, but should be efficient, and it's really not too complicated. Differential Revision: https://developer.blender.org/D11491
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc139
1 files changed, 132 insertions, 7 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 66199769586..8bcd7d69bc5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -23,8 +23,12 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "NOD_type_conversions.hh"
+
#include "node_geometry_util.hh"
+using blender::fn::GVArray_For_GSpan;
+
static bNodeSocketTemplate geo_node_join_geometry_in[] = {
{SOCK_GEOMETRY,
N_("Geometry"),
@@ -298,6 +302,127 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
+/**
+ * \note This takes advantage of the fact that creating attributes on joined curves never
+ * changes a point attribute into a spline attribute; it is always the other way around.
+ */
+static void ensure_control_point_attribute(const StringRef name,
+ const CustomDataType data_type,
+ Span<CurveComponent *> src_components,
+ CurveEval &result)
+{
+ MutableSpan<SplinePtr> splines = result.splines();
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ /* In order to fill point attributes with spline domain attribute values where necessary, keep
+ * track of the curve each spline came from while iterating over the splines in the result. */
+ int src_component_index = 0;
+ int spline_index_in_component = 0;
+ const CurveEval *current_curve = src_components[src_component_index]->get_for_read();
+
+ for (SplinePtr &spline : splines) {
+ std::optional<GSpan> attribute = spline->attributes.get_for_read(name);
+
+ if (attribute) {
+ if (attribute->type() != type) {
+ /* In this case, the attribute exists, but it has the wrong type. So create a buffer
+ * for the converted values, do the conversion, and then replace the attribute. */
+ void *converted_buffer = MEM_mallocN_aligned(
+ spline->size() * type.size(), type.alignment(), __func__);
+
+ const DataTypeConversions &conversions = blender::nodes::get_implicit_type_conversions();
+ conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), type)
+ ->materialize(converted_buffer);
+
+ spline->attributes.remove(name);
+ spline->attributes.create_by_move(name, data_type, converted_buffer);
+ }
+ }
+ else {
+ spline->attributes.create(name, data_type);
+
+ if (current_curve->attributes.get_for_read(name)) {
+ /* In this case the attribute did not exist, but there is a spline domain attribute
+ * we can retrieve a value from, as a spline to point domain conversion. So fill the
+ * new attribute with the value for this spline. */
+ GVArrayPtr current_curve_attribute = current_curve->attributes.get_for_read(
+ name, data_type, nullptr);
+
+ BLI_assert(spline->attributes.get_for_read(name));
+ std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(name);
+
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+ current_curve_attribute->get(spline_index_in_component, buffer);
+ type.fill_initialized(buffer, new_attribute->data(), new_attribute->size());
+ }
+ }
+
+ /* Move to the next spline and maybe the next input component. */
+ spline_index_in_component++;
+ if (spline != splines.last() && spline_index_in_component >= current_curve->splines().size()) {
+ src_component_index++;
+ spline_index_in_component = 0;
+
+ current_curve = src_components[src_component_index]->get_for_read();
+ }
+ }
+}
+
+/**
+ * Fill data for an attribute on the new curve based on all source curves.
+ */
+static void ensure_spline_attribute(const StringRef name,
+ const CustomDataType data_type,
+ Span<CurveComponent *> src_components,
+ CurveEval &result)
+{
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ result.attributes.create(name, data_type);
+ GMutableSpan result_attribute = *result.attributes.get_for_write(name);
+
+ int offset = 0;
+ for (const CurveComponent *component : src_components) {
+ const CurveEval &curve = *component->get_for_read();
+ const int size = curve.splines().size();
+ if (size == 0) {
+ continue;
+ }
+ GVArrayPtr read_attribute = curve.attributes.get_for_read(name, data_type, nullptr);
+ GVArray_GSpan src_span{*read_attribute};
+
+ const void *src_buffer = src_span.data();
+ type.copy_to_initialized_n(src_buffer, result_attribute[offset], size);
+
+ offset += size;
+ }
+}
+
+/**
+ * Special handling for copying spline attributes. This is necessary because we move the splines
+ * out of the source components instead of copying them, meaning we can no longer access point
+ * domain attributes on the source components.
+ *
+ * \warning Splines have been moved out of the source components at this point, so it
+ * is important to only read curve-level data (spline domain attributes) from them.
+ */
+static void join_curve_attributes(const Map<std::string, AttributeMetaData> &info,
+ Span<CurveComponent *> src_components,
+ CurveEval &result)
+{
+ for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) {
+ const StringRef name = item.key;
+ const AttributeMetaData meta_data = item.value;
+
+ if (meta_data.domain == ATTR_DOMAIN_CURVE) {
+ ensure_spline_attribute(name, meta_data.data_type, src_components, result);
+ }
+ else {
+ ensure_control_point_attribute(name, meta_data.data_type, src_components, result);
+ }
+ }
+}
+
static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
{
Vector<CurveComponent *> src_components;
@@ -320,6 +445,11 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge
return;
}
+ /* Retrieve attribute info before moving the splines out of the input components. */
+ const Map<std::string, AttributeMetaData> info = get_final_attribute_info(
+ {(const GeometryComponent **)src_components.data(), src_components.size()},
+ {"position", "radius", "tilt", "cyclic", "resolution"});
+
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
CurveEval *dst_curve = new CurveEval();
for (CurveComponent *component : src_components) {
@@ -328,14 +458,9 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge
dst_curve->add_spline(std::move(spline));
}
}
-
- /* For now, remove all custom attributes, since they might have different types,
- * or an attribute might not exist on all splines. */
dst_curve->attributes.reallocate(dst_curve->splines().size());
- CustomData_reset(&dst_curve->attributes.data);
- for (SplinePtr &spline : dst_curve->splines()) {
- CustomData_reset(&spline->attributes.data);
- }
+
+ join_curve_attributes(info, src_components, *dst_curve);
dst_component.replace(dst_curve);
}