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-09-29 23:29:29 +0300
committerHans Goudey <h.goudey@me.com>2021-09-29 23:29:29 +0300
commit81f552e9ad1ab5705ef69cf8e7ff7ee67575d45f (patch)
tree86b0af42effc17db26b41a7bd1df795f7d520e25
parentcd03f5b6e572640f6e182c4989fe20ced156effa (diff)
Geometry Nodes: Expose Bezier handle positions as an attribute
This commit exposes left and right bezier handles as an attribute. Interaction basically works like edit mode. If you move an aligned handle, it also moves the opposite handle of the control point. The difference is that you can't edit "Auto" or "Vector" handles, you have to first use the "Set Handle Type" node. That gives the handle types a bit more meaning in the node tree-- changing them in edit mod is more like a "UI override". The attributes are named `handle_start` and `handle_end`, which is the same name used in the curve RNA API. A new virtual array implementation is added which handles the case of splines that don't have these attributes, and it also calls two new functions on `BezierSpline` to set the handle position accounting for aligned handles. The virtual arrays and attribute providers will be refactored (probably templated) in the future, as a next step after the last built-in curve attribute provider has landed. Differential Revision: https://developer.blender.org/D12005
-rw-r--r--source/blender/blenkernel/BKE_spline.hh3
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc283
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc2
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc50
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc2
5 files changed, 332 insertions, 8 deletions
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 541ff19c1cd..97e0d8415a5 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -316,6 +316,9 @@ class BezierSpline final : public Spline {
void translate(const blender::float3 &translation) override;
void transform(const blender::float4x4 &matrix) override;
+ void set_handle_position_right(const int index, const blender::float3 &value);
+ void set_handle_position_left(const int index, const blender::float3 &value);
+
bool point_is_sharp(const int index) const;
void mark_cache_invalid() final;
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 7d0537178ef..73c628d3f0f 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -535,6 +535,9 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
* array implementations try to make it workable in common situations.
* \{ */
+/**
+ * Individual spans in \a data may be empty if that spline contains no data for the attribute.
+ */
template<typename T>
static void point_attribute_materialize(Span<Span<T>> data,
Span<int> offsets,
@@ -546,7 +549,15 @@ static void point_attribute_materialize(Span<Span<T>> data,
for (const int spline_index : data.index_range()) {
const int offset = offsets[spline_index];
const int next_offset = offsets[spline_index + 1];
- r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]);
+
+ Span<T> src = data[spline_index];
+ MutableSpan<T> dst = r_span.slice(offset, next_offset - offset);
+ if (src.is_empty()) {
+ dst.fill(T());
+ }
+ else {
+ dst.copy_from(src);
+ }
}
}
else {
@@ -557,11 +568,20 @@ static void point_attribute_materialize(Span<Span<T>> data,
}
const int index_in_spline = dst_index - offsets[spline_index];
- r_span[dst_index] = data[spline_index][index_in_spline];
+ Span<T> src = data[spline_index];
+ if (src.is_empty()) {
+ r_span[dst_index] = T();
+ }
+ else {
+ r_span[dst_index] = src[index_in_spline];
+ }
}
}
}
+/**
+ * Individual spans in \a data may be empty if that spline contains no data for the attribute.
+ */
template<typename T>
static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
Span<int> offsets,
@@ -574,7 +594,14 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
for (const int spline_index : data.index_range()) {
const int offset = offsets[spline_index];
const int next_offset = offsets[spline_index + 1];
- uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset);
+
+ Span<T> src = data[spline_index];
+ if (src.is_empty()) {
+ uninitialized_fill_n(dst + offset, next_offset - offset, T());
+ }
+ else {
+ uninitialized_copy_n(src.data(), next_offset - offset, dst + offset);
+ }
}
}
else {
@@ -585,7 +612,13 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
}
const int index_in_spline = dst_index - offsets[spline_index];
- new (dst + dst_index) T(data[spline_index][index_in_spline]);
+ Span<T> src = data[spline_index];
+ if (src.is_empty()) {
+ new (dst + dst_index) T();
+ }
+ else {
+ new (dst + dst_index) T(src[index_in_spline]);
+ }
}
}
}
@@ -769,6 +802,169 @@ class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
}
};
+class VArray_For_BezierHandle final : public VArray<float3> {
+ private:
+ Span<SplinePtr> splines_;
+ Array<int> offsets_;
+ bool is_right_;
+
+ public:
+ VArray_For_BezierHandle(Span<SplinePtr> splines, Array<int> offsets, const bool is_right)
+ : VArray<float3>(offsets.last()),
+ splines_(std::move(splines)),
+ offsets_(std::move(offsets)),
+ is_right_(is_right)
+ {
+ }
+
+ static float3 get_internal(const int64_t index,
+ Span<SplinePtr> splines,
+ Span<int> offsets,
+ const bool is_right)
+ {
+ const PointIndices indices = lookup_point_indices(offsets, index);
+ const Spline &spline = *splines[indices.spline_index];
+ if (spline.type() == Spline::Type::Bezier) {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
+ return is_right ? bezier_spline.handle_positions_right()[indices.point_index] :
+ bezier_spline.handle_positions_left()[indices.point_index];
+ }
+ return float3(0);
+ }
+
+ float3 get_impl(const int64_t index) const final
+ {
+ return get_internal(index, splines_, offsets_, is_right_);
+ }
+
+ /**
+ * Utility so we can pass handle positions to the materialize functions above.
+ *
+ * \note This relies on the ability of the materialize implementations to
+ * handle empty spans, since only Bezier splines have handles.
+ */
+ static Array<Span<float3>> get_handle_spans(Span<SplinePtr> splines, const bool is_right)
+ {
+ Array<Span<float3>> spans(splines.size());
+ for (const int i : spans.index_range()) {
+ if (splines[i]->type() == Spline::Type::Bezier) {
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]);
+ spans[i] = is_right ? bezier_spline.handle_positions_right() :
+ bezier_spline.handle_positions_left();
+ }
+ else {
+ spans[i] = {};
+ }
+ }
+ return spans;
+ }
+
+ static void materialize_internal(const IndexMask mask,
+ Span<SplinePtr> splines,
+ Span<int> offsets,
+ const bool is_right,
+ MutableSpan<float3> r_span)
+ {
+ Array<Span<float3>> spans = get_handle_spans(splines, is_right);
+ point_attribute_materialize(spans.as_span(), offsets, mask, r_span);
+ }
+
+ static void materialize_to_uninitialized_internal(const IndexMask mask,
+ Span<SplinePtr> splines,
+ Span<int> offsets,
+ const bool is_right,
+ MutableSpan<float3> r_span)
+ {
+ Array<Span<float3>> spans = get_handle_spans(splines, is_right);
+ point_attribute_materialize_to_uninitialized(spans.as_span(), offsets, mask, r_span);
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+ {
+ materialize_internal(mask, splines_, offsets_, is_right_, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask,
+ MutableSpan<float3> r_span) const final
+ {
+ materialize_to_uninitialized_internal(mask, splines_, offsets_, is_right_, r_span);
+ }
+};
+
+class VMutableArray_For_BezierHandles final : public VMutableArray<float3> {
+ private:
+ MutableSpan<SplinePtr> splines_;
+ Array<int> offsets_;
+ bool is_right_;
+
+ public:
+ VMutableArray_For_BezierHandles(MutableSpan<SplinePtr> splines,
+ Array<int> offsets,
+ const bool is_right)
+ : VMutableArray<float3>(offsets.last()),
+ splines_(splines),
+ offsets_(std::move(offsets)),
+ is_right_(is_right)
+ {
+ }
+
+ float3 get_impl(const int64_t index) const final
+ {
+ return VArray_For_BezierHandle::get_internal(index, splines_, offsets_, is_right_);
+ }
+
+ void set_impl(const int64_t index, float3 value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ Spline &spline = *splines_[indices.spline_index];
+ if (spline.type() == Spline::Type::Bezier) {
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
+ if (is_right_) {
+ bezier_spline.set_handle_position_right(indices.point_index, value);
+ }
+ else {
+ bezier_spline.set_handle_position_left(indices.point_index, value);
+ }
+ bezier_spline.mark_cache_invalid();
+ }
+ }
+
+ void set_all_impl(Span<float3> src) final
+ {
+ for (const int spline_index : splines_.index_range()) {
+ Spline &spline = *splines_[spline_index];
+ if (spline.type() == Spline::Type::Bezier) {
+ const int offset = offsets_[spline_index];
+
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
+ if (is_right_) {
+ for (const int i : IndexRange(bezier_spline.size())) {
+ bezier_spline.set_handle_position_right(i, src[offset + i]);
+ }
+ }
+ else {
+ for (const int i : IndexRange(bezier_spline.size())) {
+ bezier_spline.set_handle_position_left(i, src[offset + i]);
+ }
+ }
+ bezier_spline.mark_cache_invalid();
+ }
+ }
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+ {
+ VArray_For_BezierHandle::materialize_internal(mask, splines_, offsets_, is_right_, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask,
+ MutableSpan<float3> r_span) const final
+ {
+ VArray_For_BezierHandle::materialize_to_uninitialized_internal(
+ mask, splines_, offsets_, is_right_, r_span);
+ }
+};
+
/**
* Provider for any builtin control point attribute that doesn't need
* special handling like access to other arrays in the spline.
@@ -906,6 +1102,78 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
}
};
+class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
+ private:
+ bool is_right_;
+
+ public:
+ BezierHandleAttributeProvider(const bool is_right)
+ : BuiltinAttributeProvider(is_right ? "handle_right" : "handle_left",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable),
+ is_right_(is_right)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const override
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
+ return {};
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ return std::make_unique<fn::GVArray_For_EmbeddedVArray<float3, VArray_For_BezierHandle>>(
+ offsets.last(), curve->splines(), std::move(offsets), is_right_);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
+ return {};
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>(
+ offsets.last(), curve->splines(), std::move(offsets), is_right_);
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return false;
+ }
+
+ return curve->has_spline_with_type(Spline::Type::Bezier) &&
+ component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
+ }
+};
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1196,6 +1464,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
spline_custom_data_access);
static PositionAttributeProvider position;
+ static BezierHandleAttributeProvider handles_start(false);
+ static BezierHandleAttributeProvider handles_end(true);
static BuiltinPointAttributeProvider<float> radius(
"radius",
@@ -1213,8 +1483,9 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static DynamicPointAttributeProvider point_custom_data;
- return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic},
- {&spline_custom_data, &point_custom_data});
+ return ComponentAttributeProviders(
+ {&position, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic},
+ {&spline_custom_data, &point_custom_data});
}
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index ad13342ad9e..77348c3d22c 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -574,7 +574,7 @@ static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, G
geometry_set_gather_instances_attribute_info(
set_groups,
{GEO_COMPONENT_TYPE_CURVE},
- {"position", "radius", "tilt", "cyclic", "resolution"},
+ {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"},
attributes);
join_attributes(set_groups,
{GEO_COMPONENT_TYPE_CURVE},
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
index b36d7a21669..f719a1cfda2 100644
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -289,6 +289,56 @@ void BezierSpline::transform(const blender::float4x4 &matrix)
this->mark_cache_invalid();
}
+static void set_handle_position(const float3 &position,
+ const BezierSpline::HandleType type,
+ const BezierSpline::HandleType type_other,
+ const float3 &new_value,
+ float3 &handle,
+ float3 &handle_other)
+{
+ /* Don't bother when the handle positions are calculated automatically anyway. */
+ if (ELEM(type, BezierSpline::HandleType::Auto, BezierSpline::HandleType::Vector)) {
+ return;
+ }
+
+ handle = new_value;
+ if (type_other == BezierSpline::HandleType::Align) {
+ /* Keep track of the old length of the opposite handle. */
+ const float length = float3::distance(handle_other, position);
+ /* Set the other handle to directly opposite from the current handle. */
+ const float3 dir = (handle - position).normalized();
+ handle_other = position - dir * length;
+ }
+}
+
+/**
+ * Set positions for the right handle of the control point, ensuring that
+ * aligned handles stay aligned. Has no effect for auto and vector type handles.
+ */
+void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value)
+{
+ set_handle_position(positions_[index],
+ handle_types_right_[index],
+ handle_types_left_[index],
+ value,
+ handle_positions_right_[index],
+ handle_positions_left_[index]);
+}
+
+/**
+ * Set positions for the left handle of the control point, ensuring that
+ * aligned handles stay aligned. Has no effect for auto and vector type handles.
+ */
+void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value)
+{
+ set_handle_position(positions_[index],
+ handle_types_left_[index],
+ handle_types_right_[index],
+ value,
+ handle_positions_left_[index],
+ handle_positions_right_[index]);
+}
+
bool BezierSpline::point_is_sharp(const int index) const
{
return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) ||
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 93643298f92..3e9b615f478 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -437,7 +437,7 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge
/* Retrieve attribute info before moving the splines out of the input components. */
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(
{(const GeometryComponent **)src_components.data(), src_components.size()},
- {"position", "radius", "tilt", "cyclic", "resolution"});
+ {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"});
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
CurveEval *dst_curve = new CurveEval();