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:
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_action.h4
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh382
-rw-r--r--source/blender/blenkernel/BKE_attribute_math.hh82
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h6
-rw-r--r--source/blender/blenkernel/BKE_callbacks.h1
-rw-r--r--source/blender/blenkernel/BKE_context.h19
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h14
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh353
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h5
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h6
-rw-r--r--source/blender/blenkernel/BKE_mesh.h6
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.hh55
-rw-r--r--source/blender/blenkernel/BKE_modifier.h4
-rw-r--r--source/blender/blenkernel/BKE_nla.h9
-rw-r--r--source/blender/blenkernel/BKE_node.h9
-rw-r--r--source/blender/blenkernel/BKE_node_ui_storage.hh8
-rw-r--r--source/blender/blenkernel/BKE_object.h1
-rw-r--r--source/blender/blenkernel/BKE_paint.h6
-rw-r--r--source/blender/blenkernel/BKE_persistent_data_handle.hh153
-rw-r--r--source/blender/blenkernel/BKE_softbody.h2
-rw-r--r--source/blender/blenkernel/BKE_spline.hh501
-rw-r--r--source/blender/blenkernel/CMakeLists.txt16
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc8
-rw-r--r--source/blender/blenkernel/intern/action.c18
-rw-r--r--source/blender/blenkernel/intern/anim_data.c8
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c528
-rw-r--r--source/blender/blenkernel/intern/appdir.c2
-rw-r--r--source/blender/blenkernel/intern/armature.c7
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc746
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh230
-rw-r--r--source/blender/blenkernel/intern/blender.c2
-rw-r--r--source/blender/blenkernel/intern/blendfile.c3
-rw-r--r--source/blender/blenkernel/intern/brush.c1
-rw-r--r--source/blender/blenkernel/intern/constraint.c2
-rw-r--r--source/blender/blenkernel/intern/context.c51
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc237
-rw-r--r--source/blender/blenkernel/intern/curveprofile.c3
-rw-r--r--source/blender/blenkernel/intern/displist.cc (renamed from source/blender/blenkernel/intern/displist.c)226
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc735
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc100
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc398
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc59
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc52
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc123
-rw-r--r--source/blender/blenkernel/intern/gpencil.c20
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c4
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c6
-rw-r--r--source/blender/blenkernel/intern/lib_override.c38
-rw-r--r--source/blender/blenkernel/intern/lib_query.c2
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.c7
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c62
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.cc158
-rw-r--r--source/blender/blenkernel/intern/movieclip.c2
-rw-r--r--source/blender/blenkernel/intern/nla.c50
-rw-r--r--source/blender/blenkernel/intern/node.cc98
-rw-r--r--source/blender/blenkernel/intern/node_ui_storage.cc2
-rw-r--r--source/blender/blenkernel/intern/object.c21
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc36
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h28
-rw-r--r--source/blender/blenkernel/intern/pointcache.c2
-rw-r--r--source/blender/blenkernel/intern/softbody.c33
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc343
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc576
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc426
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc113
-rw-r--r--source/blender/blenkernel/intern/volume.cc64
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c348
-rw-r--r--source/blender/blenkernel/nla_private.h11
68 files changed, 5643 insertions, 1988 deletions
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index e27cb2be8ee..3d81fcba37d 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -140,7 +140,7 @@ void BKE_pose_channel_free_bbone_cache(struct bPoseChannel_Runtime *runtime);
void BKE_pose_channels_free(struct bPose *pose);
void BKE_pose_channels_free_ex(struct bPose *pose, bool do_id_user);
-void BKE_pose_channels_hash_make(struct bPose *pose);
+void BKE_pose_channels_hash_ensure(struct bPose *pose);
void BKE_pose_channels_hash_free(struct bPose *pose);
void BKE_pose_channels_remove(struct Object *ob,
@@ -161,7 +161,7 @@ void BKE_pose_channel_session_uuid_generate(struct bPoseChannel *pchan);
struct bPoseChannel *BKE_pose_channel_find_name(const struct bPose *pose, const char *name);
struct bPoseChannel *BKE_pose_channel_active(struct Object *ob);
struct bPoseChannel *BKE_pose_channel_active_or_first_selected(struct Object *ob);
-struct bPoseChannel *BKE_pose_channel_verify(struct bPose *pose, const char *name);
+struct bPoseChannel *BKE_pose_channel_ensure(struct bPose *pose, const char *name);
struct bPoseChannel *BKE_pose_channel_get_mirrored(const struct bPose *pose, const char *name);
void BKE_pose_check_uuids_unique_and_report(const struct bPose *pose);
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index 120b4e08b9c..358daa40723 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -20,299 +20,289 @@
#include "FN_cpp_type.hh"
#include "FN_generic_span.hh"
+#include "FN_generic_virtual_array.hh"
#include "BKE_attribute.h"
#include "BLI_color.hh"
#include "BLI_float2.hh"
#include "BLI_float3.hh"
-
-namespace blender::bke {
-
-using fn::CPPType;
-
-const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
-CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
-CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
-AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
+#include "BLI_function_ref.hh"
/**
- * This class offers an indirection for reading an attribute.
- * This is useful for the following reasons:
- * - Blender does not store all attributes the same way.
- * The simplest case are custom data layers with primitive types.
- * A bit more complex are mesh attributes like the position of vertices,
- * which are embedded into the MVert struct.
- * Even more complex to access are vertex weights.
- * - Sometimes attributes are stored on one domain, but we want to access
- * the attribute on a different domain. Therefore, we have to interpolate
- * between the domains.
+ * Contains information about an attribute in a geometry component.
+ * More information can be added in the future. E.g. whether the attribute is builtin and how it is
+ * stored (uv map, vertex group, ...).
*/
-class ReadAttribute {
- protected:
- const AttributeDomain domain_;
- const CPPType &cpp_type_;
- const CustomDataType custom_data_type_;
- const int64_t size_;
-
- /* Protects the span below, so that no two threads initialize it at the same time. */
- mutable std::mutex span_mutex_;
- /* When it is not null, it points to the attribute array or a temporary array that contains all
- * the attribute values. */
- mutable void *array_buffer_ = nullptr;
- /* Is true when the buffer above is owned by the attribute accessor. */
- mutable bool array_is_temporary_ = false;
+struct AttributeMetaData {
+ AttributeDomain domain;
+ CustomDataType data_type;
+};
- public:
- ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
- : domain_(domain),
- cpp_type_(cpp_type),
- custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
- size_(size)
+/**
+ * Base class for the attribute intializer types described below.
+ */
+struct AttributeInit {
+ enum class Type {
+ Default,
+ VArray,
+ MoveArray,
+ };
+ Type type;
+ AttributeInit(const Type type) : type(type)
{
}
+};
- virtual ~ReadAttribute();
-
- AttributeDomain domain() const
+/**
+ * Create an attribute using the default value for the data type.
+ * The default values may depend on the attribute provider implementation.
+ */
+struct AttributeInitDefault : public AttributeInit {
+ AttributeInitDefault() : AttributeInit(Type::Default)
{
- return domain_;
}
+};
- const CPPType &cpp_type() const
- {
- return cpp_type_;
- }
+/**
+ * Create an attribute by copying data from an existing virtual array. The virtual array
+ * must have the same type as the newly created attribute.
+ *
+ * Note that this can be used to fill the new attribute with the default
+ */
+struct AttributeInitVArray : public AttributeInit {
+ const blender::fn::GVArray *varray;
- CustomDataType custom_data_type() const
+ AttributeInitVArray(const blender::fn::GVArray *varray)
+ : AttributeInit(Type::VArray), varray(varray)
{
- return custom_data_type_;
}
+};
- int64_t size() const
- {
- return size_;
- }
+/**
+ * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
+ * Sometimes data is created before a geometry component is available. In that case, it's
+ * preferable to move data directly to the created attribute to avoid a new allocation and a copy.
+ *
+ * Note that this will only have a benefit for attributes that are stored directly as contiguous
+ * arrays, so not for some built-in attributes.
+ *
+ * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
+ * can't be used directly, and that is generally how Blender expects custom data to be allocated.
+ */
+struct AttributeInitMove : public AttributeInit {
+ void *data = nullptr;
- void get(const int64_t index, void *r_value) const
+ AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
{
- BLI_assert(index < size_);
- this->get_internal(index, r_value);
}
+};
+
+/* Returns false when the iteration should be stopped. */
+using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
+ const AttributeMetaData &meta_data)>;
- /* Get a span that contains all attribute values. */
- fn::GSpan get_span() const;
+namespace blender::bke {
+
+using fn::CPPType;
+using fn::GVArray;
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArrayPtr;
+
+const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
+CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
+CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
+AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
- template<typename T> Span<T> get_span() const
+/**
+ * Used when looking up a "plain attribute" based on a name for reading from it.
+ */
+struct ReadAttributeLookup {
+ /* The virtual array that is used to read from this attribute. */
+ GVArrayPtr varray;
+ /* Domain the attribute lives on in the geometry. */
+ AttributeDomain domain;
+
+ /* Convenience function to check if the attribute has been found. */
+ operator bool() const
{
- return this->get_span().typed<T>();
+ return this->varray.get() != nullptr;
}
+};
- protected:
- /* r_value is expected to be uninitialized. */
- virtual void get_internal(const int64_t index, void *r_value) const = 0;
-
- virtual void initialize_span() const;
+/**
+ * Used when looking up a "plain attribute" based on a name for reading from it and writing to it.
+ */
+struct WriteAttributeLookup {
+ /* The virtual array that is used to read from and write to the attribute. */
+ GVMutableArrayPtr varray;
+ /* Domain the attributes lives on in the geometry. */
+ AttributeDomain domain;
+
+ /* Convenience function to check if the attribute has been found. */
+ operator bool() const
+ {
+ return this->varray.get() != nullptr;
+ }
};
/**
- * This exists for similar reasons as the ReadAttribute class, except that
- * it does not deal with interpolation between domains.
+ * An output attribute allows writing to an attribute (and optionally reading as well). It adds
+ * some convenience features on top of `GVMutableArray` that are very commonly used.
+ *
+ * Supported convenience features:
+ * - Implicit type conversion when writing to builtin attributes.
+ * - Supports simple access to a span containing the attribute values (that avoids the use of
+ * VMutableArray_Span in many cases).
+ * - An output attribute can live side by side with an existing attribute with a different domain
+ * or data type. The old attribute will only be overwritten when the #save function is called.
*/
-class WriteAttribute {
- protected:
- const AttributeDomain domain_;
- const CPPType &cpp_type_;
- const CustomDataType custom_data_type_;
- const int64_t size_;
-
- /* When not null, this points either to the attribute array or to a temporary array. */
- void *array_buffer_ = nullptr;
- /* True, when the buffer points to a temporary array. */
- bool array_is_temporary_ = false;
- /* This helps to protect against forgetting to apply changes done to the array. */
- bool array_should_be_applied_ = false;
+class OutputAttribute {
+ public:
+ using SaveFn = std::function<void(OutputAttribute &)>;
+
+ private:
+ GVMutableArrayPtr varray_;
+ AttributeDomain domain_;
+ SaveFn save_;
+ std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
+ bool ignore_old_values_ = false;
+ bool save_has_been_called_ = false;
public:
- WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
- : domain_(domain),
- cpp_type_(cpp_type),
- custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
- size_(size)
+ OutputAttribute() = default;
+
+ OutputAttribute(GVMutableArrayPtr varray,
+ AttributeDomain domain,
+ SaveFn save,
+ const bool ignore_old_values)
+ : varray_(std::move(varray)),
+ domain_(domain),
+ save_(std::move(save)),
+ ignore_old_values_(ignore_old_values)
{
}
- virtual ~WriteAttribute();
+ OutputAttribute(OutputAttribute &&other) = default;
- AttributeDomain domain() const
+ ~OutputAttribute();
+
+ operator bool() const
{
- return domain_;
+ return varray_.get() != nullptr;
}
- const CPPType &cpp_type() const
+ GVMutableArray &operator*()
{
- return cpp_type_;
+ return *varray_;
}
- CustomDataType custom_data_type() const
+ GVMutableArray *operator->()
{
- return custom_data_type_;
+ return varray_.get();
}
- int64_t size() const
+ GVMutableArray &varray()
{
- return size_;
+ return *varray_;
}
- void get(const int64_t index, void *r_value) const
+ AttributeDomain domain() const
{
- BLI_assert(index < size_);
- this->get_internal(index, r_value);
+ return domain_;
}
- void set(const int64_t index, const void *value)
+ const CPPType &cpp_type() const
{
- BLI_assert(index < size_);
- this->set_internal(index, value);
+ return varray_->type();
}
- /* Get a span that new attribute values can be written into. When all values have been changed,
- * #apply_span has to be called. */
- fn::GMutableSpan get_span();
- /* The span returned by this method might not contain the current attribute values. */
- fn::GMutableSpan get_span_for_write_only();
- /* Write the changes to the span into the actual attribute, if they aren't already. */
- void apply_span();
-
- template<typename T> MutableSpan<T> get_span()
+ CustomDataType custom_data_type() const
{
- return this->get_span().typed<T>();
+ return cpp_type_to_custom_data_type(this->cpp_type());
}
- template<typename T> MutableSpan<T> get_span_for_write_only()
+ fn::GMutableSpan as_span()
{
- return this->get_span_for_write_only().typed<T>();
+ if (!optional_span_varray_.has_value()) {
+ const bool materialize_old_values = !ignore_old_values_;
+ optional_span_varray_.emplace(*varray_, materialize_old_values);
+ }
+ fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
+ return span_varray;
}
- protected:
- virtual void get_internal(const int64_t index, void *r_value) const = 0;
- virtual void set_internal(const int64_t index, const void *value) = 0;
+ template<typename T> MutableSpan<T> as_span()
+ {
+ return this->as_span().typed<T>();
+ }
- virtual void initialize_span(const bool write_only);
- virtual void apply_span_if_necessary();
+ void save();
};
-using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
-using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
-
-/* This provides type safe access to an attribute.
- * The underlying ReadAttribute is owned optionally. */
-template<typename T> class TypedReadAttribute {
+/**
+ * Same as OutputAttribute, but should be used when the data type is known at compile time.
+ */
+template<typename T> class OutputAttribute_Typed {
private:
- std::unique_ptr<const ReadAttribute> owned_attribute_;
- const ReadAttribute *attribute_;
+ OutputAttribute attribute_;
+ std::optional<fn::GVMutableArray_Typed<T>> optional_varray_;
+ VMutableArray<T> *varray_ = nullptr;
public:
- TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute)
+ OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
{
- owned_attribute_ = std::move(attribute);
- BLI_assert(owned_attribute_);
+ if (attribute_) {
+ optional_varray_.emplace(attribute_.varray());
+ varray_ = &**optional_varray_;
+ }
}
- TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute)
+ operator bool() const
{
- BLI_assert(attribute_->cpp_type().is<T>());
+ return varray_ != nullptr;
}
- int64_t size() const
+ VMutableArray<T> &operator*()
{
- return attribute_->size();
+ return *varray_;
}
- T operator[](const int64_t index) const
+ VMutableArray<T> *operator->()
{
- BLI_assert(index < attribute_->size());
- T value;
- value.~T();
- attribute_->get(index, &value);
- return value;
+ return varray_;
}
- /* Get a span to that contains all attribute values for faster and more convenient access. */
- Span<T> get_span() const
+ VMutableArray<T> &varray()
{
- return attribute_->get_span().template typed<T>();
+ return *varray_;
}
-};
-/* This provides type safe access to an attribute.
- * The underlying WriteAttribute is owned optionally. */
-template<typename T> class TypedWriteAttribute {
- private:
- std::unique_ptr<WriteAttribute> owned_attribute_;
- WriteAttribute *attribute_;
-
- public:
- TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute)
- {
- owned_attribute_ = std::move(attribute);
- BLI_assert(owned_attribute_);
- }
-
- TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute)
- {
- BLI_assert(attribute_->cpp_type().is<T>());
- }
-
- int64_t size() const
+ AttributeDomain domain() const
{
- return attribute_->size();
+ return attribute_.domain();
}
- T operator[](const int64_t index) const
+ const CPPType &cpp_type() const
{
- BLI_assert(index < attribute_->size());
- T value;
- value.~T();
- attribute_->get(index, &value);
- return value;
+ return CPPType::get<T>();
}
- void set(const int64_t index, const T &value)
+ CustomDataType custom_data_type() const
{
- attribute_->set(index, &value);
+ return cpp_type_to_custom_data_type(this->cpp_type());
}
- /* Get a span that new values can be written into. Once all values have been updated #apply_span
- * has to be called. */
- MutableSpan<T> get_span()
+ MutableSpan<T> as_span()
{
- return attribute_->get_span().typed<T>();
- }
- /* The span returned by this method might not contain the current attribute values. */
- MutableSpan<T> get_span_for_write_only()
- {
- return attribute_->get_span_for_write_only().typed<T>();
+ return attribute_.as_span<T>();
}
- /* Write back all changes to the actual attribute, if necessary. */
- void apply_span()
+ void save()
{
- attribute_->apply_span();
+ attribute_.save();
}
};
-using BooleanReadAttribute = TypedReadAttribute<bool>;
-using FloatReadAttribute = TypedReadAttribute<float>;
-using Float2ReadAttribute = TypedReadAttribute<float2>;
-using Float3ReadAttribute = TypedReadAttribute<float3>;
-using Int32ReadAttribute = TypedReadAttribute<int>;
-using Color4fReadAttribute = TypedReadAttribute<Color4f>;
-using BooleanWriteAttribute = TypedWriteAttribute<bool>;
-using FloatWriteAttribute = TypedWriteAttribute<float>;
-using Float2WriteAttribute = TypedWriteAttribute<float2>;
-using Float3WriteAttribute = TypedWriteAttribute<float3>;
-using Int32WriteAttribute = TypedWriteAttribute<int>;
-using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
-
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh
index 16fc0db60fb..0afdc436415 100644
--- a/source/blender/blenkernel/BKE_attribute_math.hh
+++ b/source/blender/blenkernel/BKE_attribute_math.hh
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
+
#include "BLI_array.hh"
#include "BLI_color.hh"
#include "BLI_float2.hh"
@@ -21,13 +23,17 @@
#include "DNA_customdata_types.h"
+#include "FN_cpp_type.hh"
+
namespace blender::attribute_math {
+using fn::CPPType;
+
/**
* Utility function that simplifies calling a templated function based on a custom data type.
*/
template<typename Func>
-void convert_to_static_type(const CustomDataType data_type, const Func &func)
+inline void convert_to_static_type(const CustomDataType data_type, const Func &func)
{
switch (data_type) {
case CD_PROP_FLOAT:
@@ -54,6 +60,32 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
}
}
+template<typename Func>
+inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func)
+{
+ if (cpp_type.is<float>()) {
+ func(float());
+ }
+ else if (cpp_type.is<float2>()) {
+ func(float2());
+ }
+ else if (cpp_type.is<float3>()) {
+ func(float3());
+ }
+ else if (cpp_type.is<int>()) {
+ func(int());
+ }
+ else if (cpp_type.is<bool>()) {
+ func(bool());
+ }
+ else if (cpp_type.is<Color4f>()) {
+ func(Color4f());
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+}
+
/* -------------------------------------------------------------------- */
/** \name Mix three values of the same type.
*
@@ -101,6 +133,48 @@ inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Mix two values of the same type.
+ *
+ * This is just basic linear interpolation.
+ * \{ */
+
+template<typename T> T mix2(const float factor, const T &a, const T &b);
+
+template<> inline bool mix2(const float factor, const bool &a, const bool &b)
+{
+ return ((1.0f - factor) * a + factor * b) >= 0.5f;
+}
+
+template<> inline int mix2(const float factor, const int &a, const int &b)
+{
+ return static_cast<int>((1.0f - factor) * a + factor * b);
+}
+
+template<> inline float mix2(const float factor, const float &a, const float &b)
+{
+ return (1.0f - factor) * a + factor * b;
+}
+
+template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b)
+{
+ return float2::interpolate(a, b, factor);
+}
+
+template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
+{
+ return float3::interpolate(a, b, factor);
+}
+
+template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b)
+{
+ Color4f result;
+ interp_v4_v4v4(result, a, b, factor);
+ return result;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Mix a dynamic amount of values with weights for many elements.
*
* This section provides an abstraction for "mixers". The abstraction encapsulates details about
@@ -153,8 +227,10 @@ template<typename T> class SimpleMixer {
}
};
-/** This mixer accumulates values in a type that is different from the one that is mixed. Some
- * types cannot encode the floating point weights in their values (e.g. int and bool). */
+/**
+ * This mixer accumulates values in a type that is different from the one that is mixed.
+ * Some types cannot encode the floating point weights in their values (e.g. int and bool).
+ */
template<typename T, typename AccumulationT, T (*ConvertToT)(const AccumulationT &value)>
class SimpleMixerWithAccumulationType {
private:
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index f74f7fe84de..0bab980cfcd 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -31,15 +31,15 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 293
+#define BLENDER_VERSION 300
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
-#define BLENDER_VERSION_CYCLE beta
+#define BLENDER_VERSION_CYCLE alpha
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 20
+#define BLENDER_FILE_SUBVERSION 1
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h
index fadba5644de..f04b5e45720 100644
--- a/source/blender/blenkernel/BKE_callbacks.h
+++ b/source/blender/blenkernel/BKE_callbacks.h
@@ -59,6 +59,7 @@ typedef enum {
BKE_CB_EVT_VERSION_UPDATE,
BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST,
BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST,
+ BKE_CB_EVT_XR_SESSION_START_PRE,
BKE_CB_EVT_TOT,
} eCbEvent;
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 3d30188e517..50aa6027840 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area);
void CTX_wm_region_set(bContext *C, struct ARegion *region);
void CTX_wm_menu_set(bContext *C, struct ARegion *menu);
void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup);
-const char *CTX_wm_operator_poll_msg_get(struct bContext *C);
+
+/**
+ * Values to create the message that describes the reason poll failed.
+ *
+ * \note This must be called in the same context as the poll function that created it.
+ */
+struct bContextPollMsgDyn_Params {
+ /** The result is allocated . */
+ char *(*get_fn)(bContext *C, void *user_data);
+ /** Optionally free the user-data. */
+ void (*free_fn)(bContext *C, void *user_data);
+ void *user_data;
+};
+
+const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free);
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg);
+void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
+ const struct bContextPollMsgDyn_Params *params);
+void CTX_wm_operator_poll_msg_clear(struct bContext *C);
/* Data Context
*
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
index c00310408af..5f6a9ec7b91 100644
--- a/source/blender/blenkernel/BKE_geometry_set.h
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -36,25 +36,13 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
GEO_COMPONENT_TYPE_INSTANCES = 2,
GEO_COMPONENT_TYPE_VOLUME = 3,
+ GEO_COMPONENT_TYPE_CURVE = 4,
} GeometryComponentType;
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
-typedef enum InstancedDataType {
- INSTANCE_DATA_TYPE_OBJECT = 0,
- INSTANCE_DATA_TYPE_COLLECTION = 1,
-} InstancedDataType;
-
-typedef struct InstancedData {
- InstancedDataType type;
- union {
- struct Object *object;
- struct Collection *collection;
- } data;
-} InstancedData;
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index d94b2e7902b..3b3856f11ab 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -25,11 +25,11 @@
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
-#include "BLI_function_ref.hh"
#include "BLI_hash.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_user_counter.hh"
+#include "BLI_vector_set.hh"
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
@@ -39,6 +39,7 @@ struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
+class CurveEval;
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
@@ -56,74 +57,6 @@ class ComponentAttributeProviders;
class GeometryComponent;
/**
- * An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final
- * destination yet. Therefore, once the attribute has been filled with data, the #save method has
- * to be called, to store the attribute where it belongs (possibly by replacing an existing
- * attribute with the same name).
- *
- * This is useful for example in the Attribute Color Ramp node, when the same attribute name is
- * used as input and output. Typically the input is a float attribute, and the output is a color.
- * Those two attributes cannot exist at the same time, due to a name collision. To handle this
- * situation well, first the output colors have to be computed before the input floats are deleted.
- * Therefore, the outputs have to be written to a temporary buffer that replaces the existing
- * attribute once all computations are done.
- */
-class OutputAttributePtr {
- private:
- blender::bke::WriteAttributePtr attribute_;
-
- public:
- OutputAttributePtr() = default;
- OutputAttributePtr(blender::bke::WriteAttributePtr attribute);
- OutputAttributePtr(GeometryComponent &component,
- AttributeDomain domain,
- std::string name,
- CustomDataType data_type);
-
- ~OutputAttributePtr();
-
- /* Returns false, when this wrapper is empty. */
- operator bool() const
- {
- return static_cast<bool>(attribute_);
- }
-
- /* Get a reference to the underlying #WriteAttribute. */
- blender::bke::WriteAttribute &get()
- {
- BLI_assert(attribute_);
- return *attribute_;
- }
-
- blender::bke::WriteAttribute &operator*()
- {
- return *attribute_;
- }
-
- blender::bke::WriteAttribute *operator->()
- {
- return attribute_.get();
- }
-
- void save();
- void apply_span_and_save();
-};
-
-/**
- * Contains information about an attribute in a geometry component.
- * More information can be added in the future. E.g. whether the attribute is builtin and how it is
- * stored (uv map, vertex group, ...).
- */
-struct AttributeMetaData {
- AttributeDomain domain;
- CustomDataType data_type;
-};
-
-/* Returns false when the iteration should be stopped. */
-using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
- const AttributeMetaData &meta_data)>;
-
-/**
* This is the base class for specialized geometry component types.
*/
class GeometryComponent {
@@ -156,26 +89,34 @@ class GeometryComponent {
/* Return true when any attribute with this name exists, including built in attributes. */
bool attribute_exists(const blender::StringRef attribute_name) const;
+ /* Return the data type and domain of an attribute with the given name if it exists. */
+ std::optional<AttributeMetaData> attribute_get_meta_data(
+ const blender::StringRef attribute_name) const;
+
/* Returns true when the geometry component supports this attribute domain. */
bool attribute_domain_supported(const AttributeDomain domain) const;
/* Can only be used with supported domain types. */
virtual int attribute_domain_size(const AttributeDomain domain) const;
+ bool attribute_is_builtin(const blender::StringRef attribute_name) const;
+
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name) const;
/* Get read and write access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- blender::bke::WriteAttributePtr attribute_try_get_for_write(
+ blender::bke::WriteAttributeLookup attribute_try_get_for_write(
const blender::StringRef attribute_name);
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
* interpolate from one domain to another.
* Returns null if the interpolation is not implemented. */
- virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
- blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const;
+ virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const;
/* Returns true when the attribute has been deleted. */
bool attribute_try_delete(const blender::StringRef attribute_name);
@@ -183,82 +124,104 @@ class GeometryComponent {
/* Returns true when the attribute has been created. */
bool attribute_try_create(const blender::StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type);
+ const CustomDataType data_type,
+ const AttributeInit &initializer);
+
+ /* Try to create the builtin attribute with the given name. No data type or domain has to be
+ * provided, because those are fixed for builtin attributes. */
+ bool attribute_try_create_builtin(const blender::StringRef attribute_name,
+ const AttributeInit &initializer);
blender::Set<std::string> attribute_names() const;
bool attribute_foreach(const AttributeForeachCallback callback) const;
virtual bool is_empty() const;
- /* Get a read-only attribute for the given domain and data type.
- * Returns null when it does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ /* Get a virtual array to read the data of an attribute on the given domain and data type.
+ * Returns null when the attribute does not exist or cannot be converted to the requested domain
+ * and data type. */
+ std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const;
- /* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged.
- * Returns null when the attribute does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ /* Get a virtual array to read the data of an attribute on the given domain. The data type is
+ * left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
+ * requested domain. */
+ std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name, const AttributeDomain domain) const;
- /* Get a read-only attribute for the given domain and data type.
- * Returns a constant attribute based on the default value if the attribute does not exist.
- * Never returns null. */
- blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value) const;
+ /* Get a virtual array to read data of an attribute with the given data type. The domain is
+ * left unchanged. Returns null when the attribute does not exist or cannot be converted to the
+ * requested data type. */
+ blender::bke::ReadAttributeLookup attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const CustomDataType data_type) const;
- /* Get a typed read-only attribute for the given domain and type. */
- template<typename T>
- blender::bke::TypedReadAttribute<T> attribute_get_for_read(
+ /* Get a virtual array to read the data of an attribute. If that is not possible, the returned
+ * virtual array will contain a default value. This never returns null. */
+ std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
- const T &default_value) const
+ const CustomDataType data_type,
+ const void *default_value = nullptr) const;
+
+ /* Should be used instead of the method above when the requested data type is known at compile
+ * time for better type safety. */
+ template<typename T>
+ blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
- return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
+ std::unique_ptr varray = this->attribute_get_for_read(
+ attribute_name, domain, type, &default_value);
+ return blender::fn::GVArray_Typed<T>(std::move(varray));
}
- /* Get a read-only dummy attribute that always returns the same value. */
- blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
- const CustomDataType data_type,
- const void *value) const;
+ /**
+ * Returns an "output attribute", which is essentially a mutable virtual array with some commonly
+ * used convince features. The returned output attribute might be empty if requested attribute
+ * cannot exist on the geometry.
+ *
+ * The included convenience features are:
+ * - Implicit type conversion when writing to builtin attributes.
+ * - If the attribute name exists already, but has a different type/domain, a temporary attribute
+ * is created that will overwrite the existing attribute in the end.
+ */
+ blender::bke::OutputAttribute attribute_try_get_for_output(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value = nullptr);
- /* Create a read-only dummy attribute that always returns the same value.
- * The given value is converted to the correct type if necessary. */
- blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted(
+ /* Same as attribute_try_get_for_output, but should be used when the original values in the
+ * attributes are not read, i.e. the attribute is used only for output. Since values are not read
+ * from this attribute, no default value is necessary. */
+ blender::bke::OutputAttribute attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType in_data_type,
- const CustomDataType out_data_type,
- const void *value) const;
+ const CustomDataType data_type);
- /* Get a read-only dummy attribute that always returns the same value. */
+ /* Statically typed method corresponding to the equally named generic one. */
template<typename T>
- blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
- const T &value) const
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
+ const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
- const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
- return this->attribute_get_constant_for_read(domain, type, &value);
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value);
}
- /**
- * If an attribute with the given params exist, it is returned.
- * If no attribute with the given name exists, create it and
- * fill it with the default value if it is provided.
- * If an attribute with the given name but different domain or type exists, a temporary attribute
- * is created that has to be saved after the output has been computed. This avoids deleting
- * another attribute, before a computation is finished.
- *
- * This might return no attribute when the attribute cannot exist on the component.
- */
- OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value = nullptr);
+ /* Statically typed method corresponding to the equally named generic one. */
+ template<typename T>
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name, const AttributeDomain domain)
+ {
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
+ }
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
@@ -328,23 +291,34 @@ struct GeometrySet {
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ static GeometrySet create_with_curve(
+ CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
bool has_volume() const;
+ bool has_curve() const;
+
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
const Volume *get_volume_for_read() const;
+ const CurveEval *get_curve_for_read() const;
+
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
Volume *get_volume_for_write();
+ CurveEval *get_curve_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_volume(Volume *volume,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_curve(CurveEval *curve,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
};
/** A geometry component that can store a mesh. */
@@ -377,8 +351,10 @@ class MeshComponent : public GeometryComponent {
Mesh *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
- blender::bke::ReadAttributePtr attribute_try_adapt_domain(
- blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final;
+ std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const final;
bool is_empty() const final;
@@ -424,12 +400,113 @@ class PointCloudComponent : public GeometryComponent {
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
+/** A geometry component that stores curve data, in other words, a group of splines. */
+class CurveComponent : public GeometryComponent {
+ private:
+ CurveEval *curve_ = nullptr;
+ GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+
+ public:
+ CurveComponent();
+ ~CurveComponent();
+ GeometryComponent *copy() const override;
+
+ void clear();
+ bool has_curve() const;
+ void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ CurveEval *release();
+
+ const CurveEval *get_for_read() const;
+ CurveEval *get_for_write();
+
+ int attribute_domain_size(const AttributeDomain domain) const final;
+
+ bool is_empty() const final;
+
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
+
+ private:
+ const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
+};
+
+class InstanceReference {
+ public:
+ enum class Type {
+ /**
+ * An empty instance. This allows an `InstanceReference` to be default constructed without
+ * being in an invalid state. There might also be other use cases that we haven't explored much
+ * yet (such as changing the instance later on, and "disabling" some instances).
+ */
+ None,
+ Object,
+ Collection,
+ };
+
+ private:
+ Type type_ = Type::None;
+ /** Depending on the type this is either null, an Object or Collection pointer. */
+ void *data_ = nullptr;
+
+ public:
+ InstanceReference() = default;
+
+ InstanceReference(Object &object) : type_(Type::Object), data_(&object)
+ {
+ }
+
+ InstanceReference(Collection &collection) : type_(Type::Collection), data_(&collection)
+ {
+ }
+
+ Type type() const
+ {
+ return type_;
+ }
+
+ Object &object() const
+ {
+ BLI_assert(type_ == Type::Object);
+ return *(Object *)data_;
+ }
+
+ Collection &collection() const
+ {
+ BLI_assert(type_ == Type::Collection);
+ return *(Collection *)data_;
+ }
+
+ uint64_t hash() const
+ {
+ return blender::get_default_hash(data_);
+ }
+
+ friend bool operator==(const InstanceReference &a, const InstanceReference &b)
+ {
+ return a.data_ == b.data_;
+ }
+};
+
/** A geometry component that stores instances. */
class InstancesComponent : public GeometryComponent {
private:
- blender::Vector<blender::float4x4> transforms_;
- blender::Vector<int> ids_;
- blender::Vector<InstancedData> instanced_data_;
+ /**
+ * Indexed set containing information about the data that is instanced.
+ * Actual instances store an index ("handle") into this set.
+ */
+ blender::VectorSet<InstanceReference> references_;
+
+ /** Index into `references_`. Determines what data is instanced. */
+ blender::Vector<int> instance_reference_handles_;
+ /** Transformation of the instances. */
+ blender::Vector<blender::float4x4> instance_transforms_;
+ /**
+ * IDs of the instances. They are used for consistency over multiple frames for things like
+ * motion blur.
+ */
+ blender::Vector<int> instance_ids_;
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
* all. They are *almost* unique, because under certain very unlikely circumstances, they are not
@@ -444,14 +521,22 @@ class InstancesComponent : public GeometryComponent {
GeometryComponent *copy() const override;
void clear();
- void add_instance(Object *object, blender::float4x4 transform, const int id = -1);
- void add_instance(Collection *collection, blender::float4x4 transform, const int id = -1);
- void add_instance(InstancedData data, blender::float4x4 transform, const int id = -1);
-
- blender::Span<InstancedData> instanced_data() const;
- blender::Span<blender::float4x4> transforms() const;
- blender::Span<int> ids() const;
- blender::MutableSpan<blender::float4x4> transforms();
+
+ void reserve(int min_capacity);
+ void resize(int capacity);
+
+ int add_reference(InstanceReference reference);
+ void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1);
+
+ blender::Span<InstanceReference> references() const;
+
+ blender::Span<int> instance_reference_handles() const;
+ blender::MutableSpan<int> instance_reference_handles();
+ blender::MutableSpan<blender::float4x4> instance_transforms();
+ blender::Span<blender::float4x4> instance_transforms() const;
+ blender::MutableSpan<int> instance_ids();
+ blender::Span<int> instance_ids() const;
+
int instances_amount() const;
blender::Span<int> almost_unique_ids() const;
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 4b4886e8bf3..bb145580928 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -108,7 +108,10 @@ void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps);
struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
-struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive);
+struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd,
+ const char *name,
+ const bool setactive,
+ const bool add_to_header);
struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]);
struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src,
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index b9a478f8227..f1ed5a453ba 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -85,10 +85,12 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
struct ID *id_root,
struct Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce,
- const bool do_post_process);
+ const bool do_post_process,
+ struct ReportList *reports);
void BKE_lib_override_library_main_resync(struct Main *bmain,
struct Scene *scene,
- struct ViewLayer *view_layer);
+ struct ViewLayer *view_layer,
+ struct ReportList *reports);
void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 22ce197088a..62837c4f2a7 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -578,9 +578,9 @@ void BKE_mesh_polygons_flip(struct MPoly *mpoly,
struct CustomData *ldata,
int totpoly);
-/* merge verts */
-/* Enum for merge_mode of CDDM_merge_verts.
- * Refer to mesh.c for details. */
+/* Merge verts. */
+/* Enum for merge_mode of #BKE_mesh_merge_verts.
+ * Refer to mesh_merge.c for details. */
enum {
MESH_MERGE_VERTS_DUMP_IF_MAPPED,
MESH_MERGE_VERTS_DUMP_IF_EQUAL,
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
new file mode 100644
index 00000000000..f504650e349
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "FN_generic_virtual_array.hh"
+
+#include "BLI_float3.hh"
+
+#include "BKE_attribute.h"
+
+struct Mesh;
+
+namespace blender::bke::mesh_surface_sample {
+
+using fn::CPPType;
+using fn::GMutableSpan;
+using fn::GSpan;
+using fn::GVArray;
+
+void sample_point_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ Span<float3> bary_coords,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+void sample_corner_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ Span<float3> bary_coords,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+void sample_face_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index aea07c45412..48b4540e3d9 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -258,10 +258,6 @@ typedef struct ModifierTypeInfo {
const struct ModifierEvalContext *ctx,
struct GeometrySet *geometry_set);
- struct Volume *(*modifyVolume)(struct ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct Volume *volume);
-
/********************* Optional functions *********************/
/**
diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h
index 16d48024d07..af238fda659 100644
--- a/source/blender/blenkernel/BKE_nla.h
+++ b/source/blender/blenkernel/BKE_nla.h
@@ -58,7 +58,14 @@ struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain,
struct NlaTrack *nlt,
const bool use_same_actions,
const int flag);
-void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, ListBase *src, const int flag);
+void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, const ListBase *src, const int flag);
+
+/* Copy NLA tracks from #adt_source to #adt_dest, and update the active track/strip pointers to
+ * point at those copies. */
+void BKE_nla_tracks_copy_from_adt(struct Main *bmain,
+ struct AnimData *adt_dest,
+ const struct AnimData *adt_source,
+ int flag);
struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
struct NlaTrack *prev,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index d6c4ad037e2..fbe7e91e985 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -410,6 +410,9 @@ typedef struct bNodeTreeType {
void (*node_add_init)(struct bNodeTree *ntree, struct bNode *bnode);
+ /* Check if the socket type is valid for this tree type. */
+ bool (*valid_socket_type)(enum eNodeSocketDatatype socket_type, struct bNodeTreeType *ntreetype);
+
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeTreeType;
@@ -1413,6 +1416,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
#define GEO_NODE_ATTRIBUTE_CLAMP 1041
#define GEO_NODE_BOUNDING_BOX 1042
+#define GEO_NODE_SWITCH 1043
+#define GEO_NODE_ATTRIBUTE_TRANSFER 1044
+#define GEO_NODE_CURVE_TO_MESH 1045
+#define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046
+#define GEO_NODE_CURVE_RESAMPLE 1047
+#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048
/** \} */
diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh
index 5f9c039ef9e..8bf89cd8f58 100644
--- a/source/blender/blenkernel/BKE_node_ui_storage.hh
+++ b/source/blender/blenkernel/BKE_node_ui_storage.hh
@@ -95,8 +95,16 @@ struct AvailableAttributeInfo {
};
struct NodeUIStorage {
+ std::mutex mutex;
blender::Vector<NodeWarning> warnings;
blender::Set<AvailableAttributeInfo> attribute_hints;
+
+ NodeUIStorage() = default;
+ /* Needed because the mutex can't be moved or copied. */
+ NodeUIStorage(NodeUIStorage &&other)
+ : warnings(std::move(other.warnings)), attribute_hints(std::move(other.attribute_hints))
+ {
+ }
};
struct NodeTreeUIStorage {
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 9fe286df36d..f3a5c794de8 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -374,6 +374,7 @@ struct MovieClip *BKE_object_movieclip_get(struct Scene *scene,
void BKE_object_runtime_reset(struct Object *object);
void BKE_object_runtime_reset_on_copy(struct Object *object, const int flag);
+void BKE_object_runtime_free_data(struct Object *object);
void BKE_object_batch_cache_dirty_tag(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 228b52123f3..73413b61456 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -486,7 +486,11 @@ typedef struct SculptSession {
/* Total number of polys of the base mesh. */
int totfaces;
/* Face sets store its visibility in the sign of the integer, using the absolute value as the
- * Face Set ID. Positive IDs are visible, negative IDs are hidden. */
+ * Face Set ID. Positive IDs are visible, negative IDs are hidden.
+ * The 0 ID is not used by the tools or the visibility system, it is just used when creating new
+ * geometry (the trim tool, for example) to detect which geometry was just added, so it can be
+ * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set
+ * to 0. */
int *face_sets;
/* BMesh for dynamic topology sculpting */
diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh
deleted file mode 100644
index bbee09c7bf4..00000000000
--- a/source/blender/blenkernel/BKE_persistent_data_handle.hh
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup bke
- *
- * A PersistentDataHandle is a weak reference to some data in a Blender file. The handle itself is
- * just a number. A PersistentDataHandleMap is used to convert between handles and the actual data.
- */
-
-#include "BLI_map.hh"
-
-#include "DNA_ID.h"
-
-struct Collection;
-struct Object;
-
-namespace blender::bke {
-
-class PersistentDataHandleMap;
-
-class PersistentDataHandle {
- private:
- /* Negative values indicate that the handle is "empty". */
- int32_t handle_;
-
- friend PersistentDataHandleMap;
-
- protected:
- PersistentDataHandle(int handle) : handle_(handle)
- {
- }
-
- public:
- PersistentDataHandle() : handle_(-1)
- {
- }
-
- friend bool operator==(const PersistentDataHandle &a, const PersistentDataHandle &b)
- {
- return a.handle_ == b.handle_;
- }
-
- friend bool operator!=(const PersistentDataHandle &a, const PersistentDataHandle &b)
- {
- return !(a == b);
- }
-
- friend std::ostream &operator<<(std::ostream &stream, const PersistentDataHandle &a)
- {
- stream << a.handle_;
- return stream;
- }
-
- uint64_t hash() const
- {
- return static_cast<uint64_t>(handle_);
- }
-};
-
-class PersistentIDHandle : public PersistentDataHandle {
- friend PersistentDataHandleMap;
- using PersistentDataHandle::PersistentDataHandle;
-};
-
-class PersistentObjectHandle : public PersistentIDHandle {
- friend PersistentDataHandleMap;
- using PersistentIDHandle::PersistentIDHandle;
-};
-
-class PersistentCollectionHandle : public PersistentIDHandle {
- friend PersistentDataHandleMap;
- using PersistentIDHandle::PersistentIDHandle;
-};
-
-class PersistentDataHandleMap {
- private:
- Map<int32_t, ID *> id_by_handle_;
- Map<ID *, int32_t> handle_by_id_;
-
- public:
- void add(int32_t handle, ID &id)
- {
- BLI_assert(handle >= 0);
- handle_by_id_.add(&id, handle);
- id_by_handle_.add(handle, &id);
- }
-
- PersistentIDHandle lookup(ID *id) const
- {
- const int handle = handle_by_id_.lookup_default(id, -1);
- return PersistentIDHandle(handle);
- }
-
- PersistentObjectHandle lookup(Object *object) const
- {
- const int handle = handle_by_id_.lookup_default((ID *)object, -1);
- return PersistentObjectHandle(handle);
- }
-
- PersistentCollectionHandle lookup(Collection *collection) const
- {
- const int handle = handle_by_id_.lookup_default((ID *)collection, -1);
- return PersistentCollectionHandle(handle);
- }
-
- ID *lookup(const PersistentIDHandle &handle) const
- {
- ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr);
- return id;
- }
-
- Object *lookup(const PersistentObjectHandle &handle) const
- {
- ID *id = this->lookup((const PersistentIDHandle &)handle);
- if (id == nullptr) {
- return nullptr;
- }
- if (GS(id->name) != ID_OB) {
- return nullptr;
- }
- return (Object *)id;
- }
-
- Collection *lookup(const PersistentCollectionHandle &handle) const
- {
- ID *id = this->lookup((const PersistentIDHandle &)handle);
- if (id == nullptr) {
- return nullptr;
- }
- if (GS(id->name) != ID_GR) {
- return nullptr;
- }
- return (Collection *)id;
- }
-};
-
-} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h
index 4f8b21141b6..58dc90f62dc 100644
--- a/source/blender/blenkernel/BKE_softbody.h
+++ b/source/blender/blenkernel/BKE_softbody.h
@@ -47,7 +47,7 @@ typedef struct BodyPoint {
} BodyPoint;
/* allocates and initializes general main data */
-extern struct SoftBody *sbNew(struct Scene *scene);
+extern struct SoftBody *sbNew(void);
/* frees internal data and soft-body itself */
extern void sbFree(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
new file mode 100644
index 00000000000..35f21ccb897
--- /dev/null
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -0,0 +1,501 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <mutex>
+
+#include "FN_generic_virtual_array.hh"
+
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_vector.hh"
+
+#include "BKE_attribute_math.hh"
+
+struct Curve;
+
+class Spline;
+using SplinePtr = std::unique_ptr<Spline>;
+
+/**
+ * A spline is an abstraction of a single branch-less curve section, its evaluation methods,
+ * and data. The spline data itself is just control points and a set of attributes by the set
+ * of "evaluated" data is often used instead.
+ *
+ * Any derived class of Spline has to manage two things:
+ * 1. Interpolating arbitrary attribute data from the control points to evaluated points.
+ * 2. Evaluating the positions based on the stored control point data.
+ *
+ * Beyond that, everything is the base class's responsibility, with minor exceptions. Further
+ * evaluation happens in a layer on top of the evaluated points generated by the derived types.
+ *
+ * There are a few methods to evaluate a spline:
+ * 1. #evaluated_positions and #interpolate_to_evaluated_points give data for the initial
+ * evaluated points, depending on the resolution.
+ * 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups
+ * along the length of a curve.
+ * 3. #sample_uniform_index_factors returns an array that stores uniform-length samples
+ * along the spline which can be used to interpolate data from method 1.
+ *
+ * Commonly used evaluated data is stored in caches on the spline itself so that operations on
+ * splines don't need to worry about taking ownership of evaluated data when they don't need to.
+ */
+class Spline {
+ public:
+ enum class Type {
+ Bezier,
+ NURBS,
+ Poly,
+ };
+
+ enum NormalCalculationMode {
+ ZUp,
+ Minimum,
+ Tangent,
+ };
+ /* Only #Zup is supported at the moment. */
+ NormalCalculationMode normal_mode;
+
+ protected:
+ Type type_;
+ bool is_cyclic_ = false;
+
+ /** Direction of the spline at each evaluated point. */
+ mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
+ mutable std::mutex tangent_cache_mutex_;
+ mutable bool tangent_cache_dirty_ = true;
+
+ /** Normal direction vectors for each evaluated point. */
+ mutable blender::Vector<blender::float3> evaluated_normals_cache_;
+ mutable std::mutex normal_cache_mutex_;
+ mutable bool normal_cache_dirty_ = true;
+
+ /** Accumulated lengths along the evaluated points. */
+ mutable blender::Vector<float> evaluated_lengths_cache_;
+ mutable std::mutex length_cache_mutex_;
+ mutable bool length_cache_dirty_ = true;
+
+ public:
+ virtual ~Spline() = default;
+ Spline(const Type type) : type_(type)
+ {
+ }
+ Spline(Spline &other)
+ : normal_mode(other.normal_mode), type_(other.type_), is_cyclic_(other.is_cyclic_)
+ {
+ }
+
+ virtual SplinePtr copy() const = 0;
+
+ Spline::Type type() const;
+
+ /** Return the number of control points. */
+ virtual int size() const = 0;
+ int segments_size() const;
+ bool is_cyclic() const;
+ void set_cyclic(const bool value);
+
+ virtual void resize(const int size) = 0;
+ virtual blender::MutableSpan<blender::float3> positions() = 0;
+ virtual blender::Span<blender::float3> positions() const = 0;
+ virtual blender::MutableSpan<float> radii() = 0;
+ virtual blender::Span<float> radii() const = 0;
+ virtual blender::MutableSpan<float> tilts() = 0;
+ virtual blender::Span<float> tilts() const = 0;
+
+ virtual void translate(const blender::float3 &translation);
+ virtual void transform(const blender::float4x4 &matrix);
+
+ /**
+ * Mark all caches for re-computation. This must be called after any operation that would
+ * change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
+ */
+ virtual void mark_cache_invalid() = 0;
+ virtual int evaluated_points_size() const = 0;
+ int evaluated_edges_size() const;
+
+ float length() const;
+
+ virtual blender::Span<blender::float3> evaluated_positions() const = 0;
+
+ blender::Span<float> evaluated_lengths() const;
+ blender::Span<blender::float3> evaluated_tangents() const;
+ blender::Span<blender::float3> evaluated_normals() const;
+
+ void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
+
+ struct LookupResult {
+ /**
+ * The index of the evaluated point before the result location. In other words, the index of
+ * the edge that the result lies on. If the sampled factor/length is the very end of the
+ * spline, this will be the second to last index, if it's the very beginning, this will be 0.
+ */
+ int evaluated_index;
+ /**
+ * The index of the evaluated point after the result location, accounting for wrapping when
+ * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will
+ * be the last index (#evaluated_points_size - 1).
+ */
+ int next_evaluated_index;
+ /**
+ * The portion of the way from the evaluated point at #evaluated_index to the next point.
+ * If the sampled factor/length is the very end of the spline, this will be the 1.0f
+ */
+ float factor;
+ };
+ LookupResult lookup_evaluated_factor(const float factor) const;
+ LookupResult lookup_evaluated_length(const float length) const;
+
+ blender::Array<float> sample_uniform_index_factors(const int samples_size) const;
+ LookupResult lookup_data_from_index_factor(const float index_factor) const;
+
+ /**
+ * Interpolate a virtual array of data with the size of the number of control points to the
+ * evaluated points. For poly splines, the lifetime of the returned virtual array must not
+ * exceed the lifetime of the input data.
+ */
+ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const = 0;
+
+ protected:
+ virtual void correct_end_tangents() const = 0;
+};
+
+/**
+ * A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature
+ * by constraining the alignment of curve handles. Evaluation stores the positions and a map of
+ * factors and indices in a list of floats, which is then used to interpolate any other data.
+ */
+class BezierSpline final : public Spline {
+ public:
+ enum class HandleType {
+ /** The handle can be moved anywhere, and doesn't influence the point's other handle. */
+ Free,
+ /** The location is automatically calculated to be smooth. */
+ Auto,
+ /** The location is calculated to point to the next/previous control point. */
+ Vector,
+ /** The location is constrained to point in the opposite direction as the other handle. */
+ Align,
+ };
+
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+ int resolution_;
+
+ blender::Vector<HandleType> handle_types_left_;
+ blender::Vector<HandleType> handle_types_right_;
+
+ /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
+ mutable blender::Vector<blender::float3> handle_positions_left_;
+ mutable blender::Vector<blender::float3> handle_positions_right_;
+
+ mutable std::mutex auto_handle_mutex_;
+ mutable bool auto_handles_dirty_ = true;
+
+ /** Start index in evaluated points array for every control point. */
+ mutable blender::Vector<int> offset_cache_;
+ mutable std::mutex offset_cache_mutex_;
+ mutable bool offset_cache_dirty_ = true;
+
+ /** Cache of evaluated positions. */
+ mutable blender::Vector<blender::float3> evaluated_position_cache_;
+ mutable std::mutex position_cache_mutex_;
+ mutable bool position_cache_dirty_ = true;
+
+ /** Cache of "index factors" based calculated from the evaluated positions. */
+ mutable blender::Vector<float> evaluated_mapping_cache_;
+ mutable std::mutex mapping_cache_mutex_;
+ mutable bool mapping_cache_dirty_ = true;
+
+ public:
+ virtual SplinePtr copy() const final;
+ BezierSpline() : Spline(Type::Bezier)
+ {
+ }
+ BezierSpline(const BezierSpline &other)
+ : Spline((Spline &)other),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_),
+ resolution_(other.resolution_),
+ handle_types_left_(other.handle_types_left_),
+ handle_types_right_(other.handle_types_right_),
+ handle_positions_left_(other.handle_positions_left_),
+ handle_positions_right_(other.handle_positions_right_)
+ {
+ }
+
+ int size() const final;
+ int resolution() const;
+ void set_resolution(const int value);
+
+ void add_point(const blender::float3 position,
+ const HandleType handle_type_start,
+ const blender::float3 handle_position_start,
+ const HandleType handle_type_end,
+ const blender::float3 handle_position_end,
+ const float radius,
+ const float tilt);
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+ blender::Span<HandleType> handle_types_left() const;
+ blender::MutableSpan<HandleType> handle_types_left();
+ blender::Span<blender::float3> handle_positions_left() const;
+ blender::MutableSpan<blender::float3> handle_positions_left();
+ blender::Span<HandleType> handle_types_right() const;
+ blender::MutableSpan<HandleType> handle_types_right();
+ blender::Span<blender::float3> handle_positions_right() const;
+ blender::MutableSpan<blender::float3> handle_positions_right();
+
+ void translate(const blender::float3 &translation) override;
+ void transform(const blender::float4x4 &matrix) override;
+
+ bool point_is_sharp(const int index) const;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<int> control_point_offsets() const;
+ blender::Span<float> evaluated_mappings() const;
+ blender::Span<blender::float3> evaluated_positions() const final;
+ struct InterpolationData {
+ int control_point_index;
+ int next_control_point_index;
+ /**
+ * Linear interpolation weight between the two indices, from 0 to 1.
+ * Higher means closer to next control point.
+ */
+ float factor;
+ };
+ InterpolationData interpolation_data_from_index_factor(const float index_factor) const;
+
+ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const override;
+
+ private:
+ void ensure_auto_handles() const;
+ void correct_end_tangents() const final;
+ bool segment_is_vector(const int start_index) const;
+ void evaluate_bezier_segment(const int index,
+ const int next_index,
+ blender::MutableSpan<blender::float3> positions) const;
+};
+
+/**
+ * Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
+ * influenced by a vector of knots, weights for each point, and the order of the spline. Every
+ * mapping of data to evaluated points is handled the same way, but the positions are cached in
+ * the spline.
+ */
+class NURBSpline final : public Spline {
+ public:
+ enum class KnotsMode {
+ Normal,
+ EndPoint,
+ Bezier,
+ };
+
+ /** Method used to recalculate the knots vector when points are added or removed. */
+ KnotsMode knots_mode;
+
+ struct BasisCache {
+ /** The influence at each control point `i + #start_index`. */
+ blender::Vector<float> weights;
+ /**
+ * An offset for the start of #weights: the first control point index with a non-zero weight.
+ */
+ int start_index;
+ };
+
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+ blender::Vector<float> weights_;
+ int resolution_;
+ /**
+ * Defines the number of nearby control points that influence a given evaluated point. Higher
+ * orders give smoother results. The number of control points must be greater than or equal to
+ * this value.
+ */
+ uint8_t order_;
+
+ /**
+ * Determines where and how the control points affect the evaluated points. The length should
+ * always be the value returned by #knots_size(), and each value should be greater than or equal
+ * to the previous. Only invalidated when a point is added or removed.
+ */
+ mutable blender::Vector<float> knots_;
+ mutable std::mutex knots_mutex_;
+ mutable bool knots_dirty_ = true;
+
+ /** Cache of control point influences on each evaluated point. */
+ mutable blender::Vector<BasisCache> basis_cache_;
+ mutable std::mutex basis_cache_mutex_;
+ mutable bool basis_cache_dirty_ = true;
+
+ /**
+ * Cache of position data calculated from the basis cache. Though it is interpolated
+ * in the same way as any other attribute, it is stored to save unnecessary recalculation.
+ */
+ mutable blender::Vector<blender::float3> evaluated_position_cache_;
+ mutable std::mutex position_cache_mutex_;
+ mutable bool position_cache_dirty_ = true;
+
+ public:
+ SplinePtr copy() const final;
+ NURBSpline() : Spline(Type::NURBS)
+ {
+ }
+ NURBSpline(const NURBSpline &other)
+ : Spline((Spline &)other),
+ knots_mode(other.knots_mode),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_),
+ weights_(other.weights_),
+ resolution_(other.resolution_),
+ order_(other.order_)
+ {
+ }
+
+ int size() const final;
+ int resolution() const;
+ void set_resolution(const int value);
+ uint8_t order() const;
+ void set_order(const uint8_t value);
+
+ void add_point(const blender::float3 position,
+ const float radius,
+ const float tilt,
+ const float weight);
+
+ bool check_valid_size_and_order() const;
+ int knots_size() const;
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+ blender::Span<float> knots() const;
+
+ blender::MutableSpan<float> weights();
+ blender::Span<float> weights() const;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<blender::float3> evaluated_positions() const final;
+
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const final;
+
+ protected:
+ void correct_end_tangents() const final;
+ void calculate_knots() const;
+ void calculate_basis_cache() const;
+};
+
+/**
+ * A Poly spline is like a bezier spline with a resolution of one. The main reason to distinguish
+ * the two is for reduced complexity and increased performance, since interpolating data to control
+ * points does not change it.
+ */
+class PolySpline final : public Spline {
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+
+ public:
+ SplinePtr copy() const final;
+ PolySpline() : Spline(Type::Poly)
+ {
+ }
+ PolySpline(const PolySpline &other)
+ : Spline((Spline &)other),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_)
+ {
+ }
+
+ int size() const final;
+
+ void add_point(const blender::float3 position, const float radius, const float tilt);
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<blender::float3> evaluated_positions() const final;
+
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const final;
+
+ protected:
+ void correct_end_tangents() const final;
+};
+
+/**
+ * A #CurveEval corresponds to the #Curve object data. The name is different for clarity, since
+ * more of the data is stored in the splines, but also just to be different than the name in DNA.
+ */
+class CurveEval {
+ private:
+ blender::Vector<SplinePtr> splines_;
+
+ public:
+ blender::Span<SplinePtr> splines() const;
+ blender::MutableSpan<SplinePtr> splines();
+
+ void add_spline(SplinePtr spline);
+ void remove_splines(blender::IndexMask mask);
+
+ CurveEval *copy();
+
+ void translate(const blender::float3 &translation);
+ void transform(const blender::float4x4 &matrix);
+ void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
+
+ blender::Array<int> control_point_offsets() const;
+ blender::Array<int> evaluated_point_offsets() const;
+};
+
+std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 59e2c74ead1..4dc2560f908 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -112,12 +112,13 @@ set(SRC
intern/curve_convert.c
intern/curve_decimate.c
intern/curve_deform.c
+ intern/curve_eval.cc
intern/curveprofile.c
intern/customdata.c
intern/customdata_file.c
intern/data_transfer.c
intern/deform.c
- intern/displist.c
+ intern/displist.cc
intern/displist_tangent.c
intern/dynamicpaint.c
intern/editlattice.c
@@ -133,6 +134,7 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
+ intern/geometry_component_curve.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
@@ -190,6 +192,7 @@ set(SRC
intern/mesh_remap.c
intern/mesh_remesh_voxel.c
intern/mesh_runtime.c
+ intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_validate.c
intern/mesh_validate.cc
@@ -240,6 +243,10 @@ set(SRC
intern/softbody.c
intern/sound.c
intern/speaker.c
+ intern/spline_base.cc
+ intern/spline_bezier.cc
+ intern/spline_nurbs.cc
+ intern/spline_poly.cc
intern/studiolight.c
intern/subdiv.c
intern/subdiv_ccg.c
@@ -321,6 +328,7 @@ set(SRC
BKE_customdata_file.h
BKE_data_transfer.h
BKE_deform.h
+ BKE_spline.hh
BKE_displist.h
BKE_displist_tangent.h
BKE_duplilist.h
@@ -379,6 +387,7 @@ set(SRC
BKE_mesh_remap.h
BKE_mesh_remesh_voxel.h
BKE_mesh_runtime.h
+ BKE_mesh_sample.hh
BKE_mesh_tangent.h
BKE_mesh_types.h
BKE_mesh_wrapper.h
@@ -397,7 +406,6 @@ set(SRC
BKE_paint.h
BKE_particle.h
BKE_pbvh.h
- BKE_persistent_data_handle.hh
BKE_pointcache.h
BKE_pointcloud.h
BKE_preferences.h
@@ -583,10 +591,6 @@ if(WITH_CODEC_FFMPEG)
${FFMPEG_LIBRARIES}
)
add_definitions(-DWITH_FFMPEG)
-
- remove_strict_c_flags_file(
- intern/writeffmpeg.c
- )
endif()
if(WITH_PYTHON)
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index 5bf87f53f75..d4dd7e248d5 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -1861,9 +1861,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
BKE_id_free(nullptr, mesh_orco);
}
- /* Ensure normals calculation below is correct. */
- BLI_assert((mesh_input->flag & ME_AUTOSMOOTH) == (mesh_final->flag & ME_AUTOSMOOTH));
- BLI_assert(mesh_input->smoothresh == mesh_final->smoothresh);
+ /* Ensure normals calculation below is correct (normal settings have transferred properly).
+ * However, nodes modifiers might create meshes from scratch or transfer meshes from other
+ * objects with different settings, and in general it doesn't make sense to guarantee that
+ * the settings are the same as the original mesh. If necessary, this could become a modifier
+ * type flag. */
BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh);
/* Compute normals. */
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 0b38e2d9f75..a7e36b09516 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -635,7 +635,7 @@ bPoseChannel *BKE_pose_channel_find_name(const bPose *pose, const char *name)
* \note Use with care, not on Armature poses but for temporal ones.
* \note (currently used for action constraints and in rebuild_pose).
*/
-bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
+bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name)
{
bPoseChannel *chan;
@@ -656,7 +656,9 @@ bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
BLI_strncpy(chan->name, name, sizeof(chan->name));
- chan->custom_scale = 1.0f;
+ copy_v3_fl(chan->custom_scale_xyz, 1.0f);
+ zero_v3(chan->custom_translation);
+ zero_v3(chan->custom_rotation_euler);
/* init vars to prevent math errors */
unit_qt(chan->quat);
@@ -815,7 +817,7 @@ void BKE_pose_copy_data_ex(bPose **dst,
*/
if (outPose->chanbase.first != outPose->chanbase.last) {
outPose->chanhash = NULL;
- BKE_pose_channels_hash_make(outPose);
+ BKE_pose_channels_hash_ensure(outPose);
}
outPose->iksolver = src->iksolver;
@@ -945,7 +947,7 @@ bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
* Removes the hash for quick lookup of channels, must
* be done when adding/removing channels.
*/
-void BKE_pose_channels_hash_make(bPose *pose)
+void BKE_pose_channels_hash_ensure(bPose *pose)
{
if (!pose->chanhash) {
bPoseChannel *pchan;
@@ -1191,7 +1193,7 @@ void BKE_pose_free(bPose *pose)
* and ID-Props, used when duplicating bones in editmode.
* (unlike copy_pose_channel_data which only does posing-related stuff).
*
- * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify)
+ * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_ensure)
*/
void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from)
{
@@ -1235,8 +1237,10 @@ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_f
if (pchan->custom) {
id_us_plus(&pchan->custom->id);
}
+ copy_v3_v3(pchan->custom_scale_xyz, pchan_from->custom_scale_xyz);
+ copy_v3_v3(pchan->custom_translation, pchan_from->custom_translation);
+ copy_v3_v3(pchan->custom_rotation_euler, pchan_from->custom_rotation_euler);
- pchan->custom_scale = pchan_from->custom_scale;
pchan->drawflag = pchan_from->drawflag;
}
@@ -1774,7 +1778,7 @@ void what_does_obaction(Object *ob,
* allocation and also will make lookup slower.
*/
if (pose->chanbase.first != pose->chanbase.last) {
- BKE_pose_channels_hash_make(pose);
+ BKE_pose_channels_hash_ensure(pose);
}
if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(pose);
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 447ed8fbe14..44b760aefc8 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -354,7 +354,7 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
}
/* duplicate NLA data */
- BKE_nla_tracks_copy(bmain, &dadt->nla_tracks, &adt->nla_tracks, flag);
+ BKE_nla_tracks_copy_from_adt(bmain, dadt, adt, flag);
/* duplicate drivers (F-Curves) */
BKE_fcurves_copy(&dadt->drivers, &adt->drivers);
@@ -947,7 +947,7 @@ static bool nlastrips_path_rename_fix(ID *owner_id,
owner_id, prefix, oldName, newName, oldKey, newKey, &strip->act->curves, verify_paths);
}
/* Ignore own F-Curves, since those are local. */
- /* Check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
is_changed |= nlastrips_path_rename_fix(
owner_id, prefix, oldName, newName, oldKey, newKey, &strip->strips, verify_paths);
}
@@ -1177,7 +1177,7 @@ static bool nlastrips_path_remove_fix(const char *prefix, ListBase *strips)
any_removed |= fcurves_path_remove_fix(prefix, &strip->act->curves);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
any_removed |= nlastrips_path_remove_fix(prefix, &strip->strips);
}
return any_removed;
@@ -1245,7 +1245,7 @@ static void nlastrips_apply_all_curves_cb(ID *id, ListBase *strips, AllFCurvesCb
fcurves_apply_cb(id, &strip->act->curves, wrapper->func, wrapper->user_data);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
nlastrips_apply_all_curves_cb(id, &strip->strips, wrapper);
}
}
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 6f4af6f655d..e347306e0ae 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1040,6 +1040,7 @@ static NlaEvalChannelSnapshot *nlaevalchan_snapshot_new(NlaEvalChannel *nec)
nec_snapshot->channel = nec;
nec_snapshot->length = length;
nlavalidmask_init(&nec_snapshot->blend_domain, length);
+ nlavalidmask_init(&nec_snapshot->remap_domain, length);
return nec_snapshot;
}
@@ -1050,6 +1051,7 @@ static void nlaevalchan_snapshot_free(NlaEvalChannelSnapshot *nec_snapshot)
BLI_assert(!nec_snapshot->is_base);
nlavalidmask_free(&nec_snapshot->blend_domain);
+ nlavalidmask_free(&nec_snapshot->remap_domain);
MEM_freeN(nec_snapshot);
}
@@ -1650,6 +1652,363 @@ static bool nla_combine_quaternion_get_inverted_strip_values(const float lower_v
}
/* ---------------------- */
+
+/* Assert necs and necs->channel is nonNull. */
+static void nlaevalchan_assert_nonNull(NlaEvalChannelSnapshot *necs)
+{
+ UNUSED_VARS_NDEBUG(necs);
+ BLI_assert(necs != NULL && necs->channel != NULL);
+}
+
+/* Assert that the channels given can be blended or combined together. */
+static void nlaevalchan_assert_blendOrcombine_compatible(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ NlaEvalChannelSnapshot *blended_necs)
+{
+ UNUSED_VARS_NDEBUG(lower_necs, upper_necs, blended_necs);
+ BLI_assert(!ELEM(NULL, lower_necs, blended_necs));
+ BLI_assert(upper_necs == NULL || lower_necs->length == upper_necs->length);
+ BLI_assert(lower_necs->length == blended_necs->length);
+}
+
+/* Assert that the channels given can be blended or combined together as a quaternion. */
+static void nlaevalchan_assert_blendOrcombine_compatible_quaternion(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ NlaEvalChannelSnapshot *blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, blended_necs);
+ BLI_assert(lower_necs->length == 4);
+}
+
+static void nlaevalchan_copy_values(NlaEvalChannelSnapshot *dst, NlaEvalChannelSnapshot *src)
+{
+ memcpy(dst->values, src->values, src->length * sizeof(float));
+}
+
+/**
+ * Copies lower necs to blended necs if upper necs is NULL or has zero influence.
+ * \return true if copied.
+ */
+static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ const bool has_influence = !IS_EQF(upper_influence, 0.0f);
+ if (upper_necs != NULL && has_influence) {
+ return false;
+ }
+
+ nlaevalchan_copy_values(r_blended_necs, lower_necs);
+ return true;
+}
+
+/**
+ * Based on blend-mode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ */
+static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ const int length = lower_necs->length;
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_blended_necs->values[j] = lower_necs->values[j];
+ continue;
+ }
+
+ r_blended_necs->values[j] = nla_blend_value(
+ upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
+ }
+}
+
+/**
+ * Based on mix-mode, provided by one the necs,
+ * combines lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ */
+static void nlaevalchan_combine_value(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ /* Assumes every base is the same. */
+ float *base_values = lower_necs->channel->base_snapshot.values;
+ const int length = lower_necs->length;
+ const char mix_mode = lower_necs->channel->mix_mode;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_blended_necs->values[j] = lower_necs->values[j];
+ continue;
+ }
+
+ r_blended_necs->values[j] = nla_combine_value(
+ mix_mode, base_values[j], lower_necs->values[j], upper_necs->values[j], upper_influence);
+ }
+}
+
+/**
+ * Quaternion combines lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
+ */
+static void nlaevalchan_combine_quaternion(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ /** No need to check per index. We limit to all or nothing combining for quaternions. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
+ nlaevalchan_copy_values(r_blended_necs, lower_necs);
+ return;
+ }
+
+ nla_combine_quaternion(
+ lower_necs->values, upper_necs->values, upper_influence, r_blended_necs->values);
+}
+
+/**
+ * Based on blend-mode and mix-mode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
+ *
+ * \param lower_necs: Never NULL.
+ * \param upper_necs: Can be NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param upper_necs: Never NULL.
+ *
+ */
+static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_nonNull(r_blended_necs);
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_blended_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion(lower_necs, upper_necs, upper_influence, r_blended_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value(lower_necs, upper_necs, upper_influence, r_blended_necs);
+ return;
+ }
+ default:
+ BLI_assert("Mix mode should've been handled");
+ }
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value(
+ lower_necs, upper_necs, upper_blendmode, upper_influence, r_blended_necs);
+ return;
+ }
+ default:
+ BLI_assert("Blend mode should've been handled");
+ }
+}
+
+/**
+ * Based on blend-mode, solve for the upper values such that when lower blended with upper then we
+ * get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ */
+static void nlaevalchan_blend_value_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, r_upper_necs, blended_necs);
+
+ const int length = lower_necs->length;
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_upper_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_blend_get_inverted_strip_value(upper_blendmode,
+ lower_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_upper_necs->values[j]);
+ BLI_BITMAP_SET(r_upper_necs->remap_domain.ptr, j, success);
+ }
+}
+
+/**
+ * Based on mix-mode, solve for the upper values such that when lower combined with upper then we
+ * get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ */
+static void nlaevalchan_combine_value_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, r_upper_necs, blended_necs);
+
+ /* Assumes every channel's base is the same. */
+ float *base_values = lower_necs->channel->base_snapshot.values;
+ const int length = lower_necs->length;
+ const char mix_mode = lower_necs->channel->mix_mode;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_upper_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_combine_get_inverted_strip_value(mix_mode,
+ base_values[j],
+ lower_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_upper_necs->values[j]);
+
+ BLI_BITMAP_SET(r_upper_necs->remap_domain.ptr, j, success);
+ }
+}
+
+/**
+ * Solve for the upper values such that when lower quaternion combined with upper then we get
+ * blended values as a result.
+ *
+ * All blended values must be in the remap domain. If successfully remapped, then all upper values
+ * are placed in the remap domain so caller knows the result is usable.
+ */
+static void nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, r_upper_necs, blended_necs);
+
+ /* Must check each domain index individually in case animator had a non-combine NLA strip with a
+ * subset of quaternion channels and remapping through any of them failed and thus potentially
+ * has undefined values. */
+ for (int j = 0; j < 4; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, 4);
+ return;
+ }
+ }
+
+ const bool success = nla_combine_quaternion_get_inverted_strip_values(
+ lower_necs->values, blended_necs->values, upper_influence, r_upper_necs->values);
+
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, success, 4);
+}
+
+/**
+ * Based on blend-mode and mix mode, solve for the upper values such that when lower blended or
+ * combined with upper then we get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ *
+ * \param lower_necs: Never NULL.
+ * \param blended_necs: Never NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param r_upper_necs: Never NULL.
+ */
+static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+
+ if (IS_EQF(upper_influence, 0.0f)) {
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, r_upper_necs->length);
+ return;
+ }
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_upper_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_influence, r_upper_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_influence, r_upper_necs);
+ return;
+ }
+ default:
+ BLI_assert("Mix mode should've been handled");
+ }
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_blendmode, upper_influence, r_upper_necs);
+ return;
+ }
+ default:
+ BLI_assert("Blend mode should've been handled");
+ }
+}
+
+/* ---------------------- */
/* F-Modifier stack joining/separation utilities -
* should we generalize these for BLI_listbase.h interface? */
@@ -2048,12 +2407,12 @@ static void nla_eval_domain_strips(PointerRNA *ptr,
GSet *touched_actions)
{
LISTBASE_FOREACH (NlaStrip *, strip, strips) {
- /* check strip's action */
+ /* Check strip's action. */
if (strip->act) {
nla_eval_domain_action(ptr, channels, strip->act, touched_actions);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
nla_eval_domain_strips(ptr, channels, &strip->strips, touched_actions);
}
}
@@ -2500,9 +2859,9 @@ void nlasnapshot_ensure_channels(NlaEvalData *eval_data, NlaEvalSnapshot *snapsh
* Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
* to the given \a upper_blendmode and \a upper_influence.
*
- * For \a upper_snapshot, blending limited to values in the \a blend_domain. For Replace blendmode,
- * this allows the upper snapshot to have a location XYZ channel where only a subset of values are
- * blended.
+ * For \a upper_snapshot, blending limited to values in the \a blend_domain.
+ * For Replace blend-mode, this allows the upper snapshot to have a location XYZ channel
+ * where only a subset of values are blended.
*/
void nlasnapshot_blend(NlaEvalData *eval_data,
NlaEvalSnapshot *lower_snapshot,
@@ -2513,11 +2872,7 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
{
nlaeval_snapshot_ensure_size(r_blended_snapshot, eval_data->num_channels);
- const bool zero_upper_influence = IS_EQF(upper_influence, 0.0f);
-
LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
- const int length = nec->base_snapshot.length;
-
NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index);
NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
if (upper_necs == NULL && lower_necs == NULL) {
@@ -2530,49 +2885,44 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
}
NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_blended_snapshot, nec);
+ nlaevalchan_blendOrcombine(
+ lower_necs, upper_necs, upper_blendmode, upper_influence, result_necs);
+ }
+}
- /** Always copy \a lower_snapshot to result, irrelevant of whether \a upper_snapshot has a
- * corresponding channel. This only matters when \a lower_snapshot not the same as
- * \a r_blended_snapshot. */
- memcpy(result_necs->values, lower_necs->values, length * sizeof(float));
- if (upper_necs == NULL || zero_upper_influence) {
+/**
+ * Using \a blended_snapshot and \a lower_snapshot, we can solve for the \a r_upper_snapshot.
+ *
+ * Only channels that exist within \a blended_snapshot are inverted.
+ *
+ * For \a r_upper_snapshot, disables \a NlaEvalChannelSnapshot->remap_domain for failed inversions.
+ * Only values within the \a remap_domain are processed.
+ */
+void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *lower_snapshot,
+ NlaEvalSnapshot *blended_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_upper_snapshot)
+{
+ nlaeval_snapshot_ensure_size(r_upper_snapshot, eval_data->num_channels);
+
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_get(blended_snapshot, nec->index);
+ if (blended_necs == NULL) {
+ /** We assume the caller only wants a subset of channels to be inverted, those that exist
+ * within \a blended_snapshot. */
continue;
}
- if (upper_blendmode == NLASTRIP_MODE_COMBINE) {
- const int mix_mode = nec->mix_mode;
- if (mix_mode == NEC_MIX_QUATERNION) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
- continue;
- }
-
- nla_combine_quaternion(
- lower_necs->values, upper_necs->values, upper_influence, result_necs->values);
- }
- else {
- for (int j = 0; j < length; j++) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
- continue;
- }
-
- result_necs->values[j] = nla_combine_value(mix_mode,
- nec->base_snapshot.values[j],
- lower_necs->values[j],
- upper_necs->values[j],
- upper_influence);
- }
- }
+ NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
+ if (lower_necs == NULL) {
+ lower_necs = nlaeval_snapshot_find_channel(lower_snapshot->base, nec);
}
- else {
- for (int j = 0; j < length; j++) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
- continue;
- }
- result_necs->values[j] = nla_blend_value(
- upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
- }
- }
+ NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_upper_snapshot, nec);
+ nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_blendmode, upper_influence, result_necs);
}
}
@@ -2670,74 +3020,64 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
return false;
}
- /* Find the evaluation channel for the NLA stack below current strip. */
+ /** Create \a blended_snapshot and fill with input \a values. */
+ NlaEvalData *eval_data = &context->lower_eval_data;
+ NlaEvalSnapshot blended_snapshot;
+ nlaeval_snapshot_init(&blended_snapshot, eval_data, NULL);
+
NlaEvalChannelKey key = {
.ptr = *prop_ptr,
.prop = prop,
};
- /**
- * Remove lower NLA stack effects.
- *
- * Using the tweak strip's blended result and the lower snapshot value, we can solve for the
- * tweak strip value it must evaluate to.
- */
- NlaEvalData *const lower_eval_data = &context->lower_eval_data;
- NlaEvalChannel *const lower_nec = nlaevalchan_verify_key(lower_eval_data, NULL, &key);
- if ((lower_nec->base_snapshot.length != count)) {
+ NlaEvalChannel *nec = nlaevalchan_verify_key(eval_data, NULL, &key);
+ BLI_assert(nec);
+ if (nec->base_snapshot.length != count) {
BLI_assert(!"invalid value count");
+ nlaeval_snapshot_free_data(&blended_snapshot);
return false;
}
- /* Invert the blending operation to compute the desired strip values. */
- NlaEvalChannelSnapshot *const lower_nec_snapshot = nlaeval_snapshot_find_channel(
- &lower_eval_data->eval_snapshot, lower_nec);
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_ensure_channel(&blended_snapshot, nec);
+ memcpy(blended_necs->values, values, sizeof(float) * count);
+ BLI_bitmap_set_all(blended_necs->remap_domain.ptr, true, count);
- float *lower_values = lower_nec_snapshot->values;
+ /** Remove lower NLA stack effects. */
+ nlasnapshot_blend_get_inverted_upper_snapshot(eval_data,
+ &context->lower_eval_data.eval_snapshot,
+ &blended_snapshot,
+ blend_mode,
+ influence,
+ &blended_snapshot);
- if (blend_mode == NLASTRIP_MODE_COMBINE) {
- /* Quaternion combine handles all sub-channels as a unit. */
- if (lower_nec->mix_mode == NEC_MIX_QUATERNION) {
- if (r_force_all == NULL) {
- return false;
- }
+ /** Write results into \a values. */
+ bool successful_remap = true;
+ if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION &&
+ blend_mode == NLASTRIP_MODE_COMBINE) {
+ if (r_force_all != NULL) {
*r_force_all = true;
-
- if (!nla_combine_quaternion_get_inverted_strip_values(
- lower_values, values, influence, values)) {
- return false;
- }
+ index = -1;
}
else {
- float *base_values = lower_nec->base_snapshot.values;
-
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_combine_get_inverted_strip_value(lower_nec->mix_mode,
- base_values[i],
- lower_values[i],
- values[i],
- influence,
- &values[i])) {
- return false;
- }
- }
- }
+ successful_remap = false;
}
}
- else {
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_blend_get_inverted_strip_value(
- blend_mode, lower_values[i], values[i], influence, &values[i])) {
- return false;
- }
- }
+
+ for (int i = 0; i < count; i++) {
+ if (!ELEM(index, i, -1)) {
+ continue;
+ }
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, i)) {
+ successful_remap = false;
}
+
+ values[i] = blended_necs->values[i];
}
- return true;
+ nlaeval_snapshot_free_data(&blended_snapshot);
+
+ return successful_remap;
}
/**
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index 1075a46e72b..bcfd34ab42f 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -137,7 +137,7 @@ static char *blender_version_decimal(const int version)
{
static char version_str[5];
BLI_assert(version < 1000);
- BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100);
+ BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100);
return version_str;
}
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index da8a3b49f3c..f67c2cb4372 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -2444,7 +2444,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
static int rebuild_pose_bone(
bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p)
{
- bPoseChannel *pchan = BKE_pose_channel_verify(pose, bone->name); /* verify checks and/or adds */
+ bPoseChannel *pchan = BKE_pose_channel_ensure(pose, bone->name); /* verify checks and/or adds */
pchan->bone = bone;
pchan->parent = parchan;
@@ -2562,7 +2562,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
/* and a check for garbage */
BKE_pose_channels_clear_with_null_bone(pose, do_id_user);
- BKE_pose_channels_hash_make(pose);
+ BKE_pose_channels_hash_ensure(pose);
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
/* Find the custom B-Bone handles. */
@@ -2881,7 +2881,8 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden
NULL;
if (bb_custom) {
float mat[4][4], smat[4][4];
- scale_m4_fl(smat, PCHAN_CUSTOM_DRAW_SIZE(pchan));
+ scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan));
+ mul_m4_v3(smat, pchan->custom_scale_xyz);
mul_m4_series(mat, ob->obmat, pchan_tx->pose_mat, smat);
BKE_boundbox_minmax(bb_custom, mat, r_min, r_max);
}
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index ac582fc30e7..c24630c5666 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -44,196 +44,10 @@ using blender::float3;
using blender::Set;
using blender::StringRef;
using blender::StringRefNull;
-using blender::bke::ReadAttributePtr;
-using blender::bke::WriteAttributePtr;
using blender::fn::GMutableSpan;
namespace blender::bke {
-/* -------------------------------------------------------------------- */
-/** \name Attribute Accessor implementations
- * \{ */
-
-ReadAttribute::~ReadAttribute()
-{
- if (array_is_temporary_ && array_buffer_ != nullptr) {
- cpp_type_.destruct_n(array_buffer_, size_);
- MEM_freeN(array_buffer_);
- }
-}
-
-fn::GSpan ReadAttribute::get_span() const
-{
- if (size_ == 0) {
- return fn::GSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- std::lock_guard lock{span_mutex_};
- if (array_buffer_ == nullptr) {
- this->initialize_span();
- }
- }
- return fn::GSpan(cpp_type_, array_buffer_, size_);
-}
-
-void ReadAttribute::initialize_span() const
-{
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- for (const int i : IndexRange(size_)) {
- this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
-}
-
-WriteAttribute::~WriteAttribute()
-{
- if (array_should_be_applied_) {
- CLOG_ERROR(&LOG, "Forgot to call apply_span.");
- }
- if (array_is_temporary_ && array_buffer_ != nullptr) {
- cpp_type_.destruct_n(array_buffer_, size_);
- MEM_freeN(array_buffer_);
- }
-}
-
-/**
- * Get a mutable span that can be modified. When all modifications to the attribute are done,
- * #apply_span should be called. */
-fn::GMutableSpan WriteAttribute::get_span()
-{
- if (size_ == 0) {
- return fn::GMutableSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- this->initialize_span(false);
- }
- array_should_be_applied_ = true;
- return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
-}
-
-fn::GMutableSpan WriteAttribute::get_span_for_write_only()
-{
- if (size_ == 0) {
- return fn::GMutableSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- this->initialize_span(true);
- }
- array_should_be_applied_ = true;
- return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
-}
-
-void WriteAttribute::initialize_span(const bool write_only)
-{
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- if (write_only) {
- /* This does nothing for trivial types, but is necessary for general correctness. */
- cpp_type_.construct_default_n(array_buffer_, size_);
- }
- else {
- for (const int i : IndexRange(size_)) {
- this->get(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
- }
-}
-
-void WriteAttribute::apply_span()
-{
- this->apply_span_if_necessary();
- array_should_be_applied_ = false;
-}
-
-void WriteAttribute::apply_span_if_necessary()
-{
- /* Only works when the span has been initialized beforehand. */
- BLI_assert(array_buffer_ != nullptr);
-
- const int element_size = cpp_type_.size();
- for (const int i : IndexRange(size_)) {
- this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
-}
-
-/* This is used by the #OutputAttributePtr class. */
-class TemporaryWriteAttribute final : public WriteAttribute {
- public:
- GMutableSpan data;
- GeometryComponent &component;
- std::string final_name;
-
- TemporaryWriteAttribute(AttributeDomain domain,
- GMutableSpan data,
- GeometryComponent &component,
- std::string final_name)
- : WriteAttribute(domain, data.type(), data.size()),
- data(data),
- component(component),
- final_name(std::move(final_name))
- {
- }
-
- ~TemporaryWriteAttribute() override
- {
- if (data.data() != nullptr) {
- cpp_type_.destruct_n(data.data(), data.size());
- MEM_freeN(data.data());
- }
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- data.type().copy_to_uninitialized(data[index], r_value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data.type().copy_to_initialized(value, data[index]);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
-class ConvertedReadAttribute final : public ReadAttribute {
- private:
- const CPPType &from_type_;
- const CPPType &to_type_;
- ReadAttributePtr base_attribute_;
- void (*convert_)(const void *src, void *dst);
-
- public:
- ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type)
- : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()),
- from_type_(base_attribute->cpp_type()),
- to_type_(to_type),
- base_attribute_(std::move(base_attribute))
- {
- const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
- convert_ = conversions.get_conversion_functions(base_attribute_->cpp_type(), to_type)
- ->convert_single_to_uninitialized;
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
- base_attribute_->get(index, buffer);
- convert_(buffer, r_value);
- }
-};
-
-/** \} */
-
const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
{
switch (type) {
@@ -329,10 +143,8 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_
static int attribute_domain_priority(const AttributeDomain domain)
{
switch (domain) {
-#if 0
case ATTR_DOMAIN_CURVE:
return 0;
-#endif
case ATTR_DOMAIN_FACE:
return 1;
case ATTR_DOMAIN_EDGE:
@@ -368,7 +180,27 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
return highest_priority_domain;
}
-ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
+void OutputAttribute::save()
+{
+ save_has_been_called_ = true;
+ if (optional_span_varray_.has_value()) {
+ optional_span_varray_->save();
+ }
+ if (save_) {
+ save_(*this);
+ }
+}
+
+OutputAttribute::~OutputAttribute()
+{
+ if (!save_has_been_called_) {
+ if (varray_) {
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ }
+ }
+}
+
+GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read(
const GeometryComponent &component) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -384,7 +216,7 @@ ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
return as_read_attribute_(data, domain_size);
}
-WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write(
+GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
GeometryComponent &component) const
{
if (writable_ != Writable) {
@@ -430,7 +262,43 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co
return delete_success;
}
-bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) const
+static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data,
+ const CustomDataType data_type,
+ const int domain_size,
+ const AttributeInit &initializer)
+{
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::VArray: {
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ if (data == nullptr) {
+ return false;
+ }
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
+ return true;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *data = CustomData_add_layer(
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_size);
+ if (data == nullptr) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
+bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
+ const AttributeInit &initializer) const
{
if (createable_ != Creatable) {
return false;
@@ -443,10 +311,10 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) co
/* Exists already. */
return false;
}
+
const int domain_size = component.attribute_domain_size(domain_);
- const void *data = CustomData_add_layer(
- custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size);
- const bool success = data != nullptr;
+ const bool success = add_custom_data_layer_from_attribute_init(
+ *custom_data, stored_type_, domain_size, initializer);
if (success) {
custom_data_access_.update_custom_data_pointers(component);
}
@@ -463,7 +331,7 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component)
return data != nullptr;
}
-ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
+ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -496,7 +364,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
return {};
}
-WriteAttributePtr CustomDataAttributeProvider::try_get_for_write(
+WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
@@ -548,10 +416,52 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
return false;
}
+static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name,
+ CustomData &custom_data,
+ const CustomDataType data_type,
+ const int domain_size,
+ const AttributeInit &initializer)
+{
+ char attribute_name_c[MAX_NAME];
+ attribute_name.copy(attribute_name_c);
+
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::VArray: {
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ if (data == nullptr) {
+ return false;
+ }
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
+ return true;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c);
+ if (data == nullptr) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type) const
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const
{
if (domain_ != domain) {
return false;
@@ -569,10 +479,8 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
}
}
const int domain_size = component.attribute_domain_size(domain_);
- char attribute_name_c[MAX_NAME];
- attribute_name.copy(attribute_name_c);
- CustomData_add_layer_named(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ add_named_custom_data_layer_from_attribute_init(
+ attribute_name, *custom_data, data_type, domain_size, initializer);
return true;
}
@@ -595,7 +503,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
return true;
}
-ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
+ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -606,14 +514,14 @@ ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
if (layer.type == stored_type_) {
if (layer.name == attribute_name) {
const int domain_size = component.attribute_domain_size(domain_);
- return as_read_attribute_(layer.data, domain_size);
+ return {as_read_attribute_(layer.data, domain_size), domain_};
}
}
}
return {};
}
-WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
+WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
@@ -630,7 +538,7 @@ WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
if (data_old != data_new) {
custom_data_access_.update_custom_data_pointers(component);
}
- return as_write_attribute_(layer.data, domain_size);
+ return {as_write_attribute_(layer.data, domain_size), domain_};
}
}
}
@@ -708,7 +616,17 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain
return 0;
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const
+{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ return providers->builtin_attribute_providers().contains_as(attribute_name);
+}
+
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name) const
{
using namespace blender::bke;
@@ -719,11 +637,11 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
- return builtin_provider->try_get_for_read(*this);
+ return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
+ ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
if (attribute) {
return attribute;
}
@@ -731,16 +649,19 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
return {};
}
-ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(
- ReadAttributePtr attribute, const AttributeDomain new_domain) const
+std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
- if (attribute && attribute->domain() == new_domain) {
- return attribute;
+ if (from_domain == to_domain) {
+ return varray;
}
return {};
}
-WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name)
+blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write(
+ const StringRef attribute_name)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
@@ -750,11 +671,11 @@ WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
- return builtin_provider->try_get_for_write(*this);
+ return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
+ WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
if (attribute) {
return attribute;
}
@@ -784,7 +705,8 @@ bool GeometryComponent::attribute_try_delete(const StringRef attribute_name)
bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type)
+ const CustomDataType data_type,
+ const AttributeInit &initializer)
{
using namespace blender::bke;
if (attribute_name.is_empty()) {
@@ -803,17 +725,36 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
if (builtin_provider->data_type() != data_type) {
return false;
}
- return builtin_provider->try_create(*this);
+ return builtin_provider->try_create(*this, initializer);
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) {
+ if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) {
return true;
}
}
return false;
}
+bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
+ const AttributeInit &initializer)
+{
+ using namespace blender::bke;
+ if (attribute_name.is_empty()) {
+ return false;
+ }
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ const BuiltinAttributeProvider *builtin_provider =
+ providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
+ if (builtin_provider == nullptr) {
+ return false;
+ }
+ return builtin_provider->try_create(*this, initializer);
+}
+
Set<std::string> GeometryComponent::attribute_names() const
{
Set<std::string> attributes;
@@ -867,264 +808,283 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (attribute) {
return true;
}
return false;
}
-static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute,
- const blender::fn::CPPType &to_type)
+std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
+ const StringRef attribute_name) const
{
- const blender::fn::CPPType &from_type = attribute->cpp_type();
- if (from_type == to_type) {
- return attribute;
- }
+ std::optional<AttributeMetaData> result{std::nullopt};
+ this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (attribute_name == name) {
+ result = meta_data;
+ return false;
+ }
+ return true;
+ });
+ return result;
+}
+static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
+ std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
+{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
- if (!conversions.is_convertible(from_type, to_type)) {
- return {};
- }
-
- return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type);
+ return conversions.try_convert(std::move(varray), to_type);
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (!attribute) {
return {};
}
- if (domain != ATTR_DOMAIN_AUTO && attribute->domain() != domain) {
- attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
- if (!attribute) {
+ std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
+ if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
+ varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
+ if (!varray) {
return {};
}
}
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
- if (attribute->cpp_type() != *cpp_type) {
- attribute = try_adapt_data_type(std::move(attribute), *cpp_type);
- if (!attribute) {
+ if (varray->type() != *cpp_type) {
+ varray = try_adapt_data_type(std::move(varray), *cpp_type);
+ if (!varray) {
return {};
}
}
- return attribute;
+ return varray;
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name,
- const AttributeDomain domain) const
+std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read(
+ const StringRef attribute_name, const AttributeDomain domain) const
{
if (!this->attribute_domain_supported(domain)) {
return {};
}
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (!attribute) {
return {};
}
- if (attribute->domain() != domain) {
- attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
- if (!attribute) {
- return {};
- }
+ if (attribute.domain != domain) {
+ return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain);
}
- return attribute;
+ return std::move(attribute.varray);
}
-ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value) const
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const CustomDataType data_type) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type);
- if (attribute) {
- return attribute;
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
+ if (!attribute) {
+ return {};
}
- return this->attribute_get_constant_for_read(domain, data_type, default_value);
-}
-
-blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read(
- const AttributeDomain domain, const CustomDataType data_type, const void *value) const
-{
- BLI_assert(this->attribute_domain_supported(domain));
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
- if (value == nullptr) {
- value = cpp_type->default_value();
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(type != nullptr);
+ if (attribute.varray->type() == *type) {
+ return attribute;
}
- const int domain_size = this->attribute_domain_size(domain);
- return std::make_unique<blender::bke::ConstantReadAttribute>(
- domain, domain_size, *cpp_type, value);
+ const blender::nodes::DataTypeConversions &conversions =
+ blender::nodes::get_implicit_type_conversions();
+ return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain};
}
-blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted(
+std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
+ const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType in_data_type,
- const CustomDataType out_data_type,
- const void *value) const
+ const CustomDataType data_type,
+ const void *default_value) const
{
- BLI_assert(this->attribute_domain_supported(domain));
- if (value == nullptr || in_data_type == out_data_type) {
- return this->attribute_get_constant_for_read(domain, out_data_type, value);
+ std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read(
+ attribute_name, domain, data_type);
+ if (varray) {
+ return varray;
+ }
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ if (default_value == nullptr) {
+ default_value = type->default_value();
}
-
- const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type(
- in_data_type);
- const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type(
- out_data_type);
- BLI_assert(in_cpp_type != nullptr);
- BLI_assert(out_cpp_type != nullptr);
-
- const blender::nodes::DataTypeConversions &conversions =
- blender::nodes::get_implicit_type_conversions();
- BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type));
-
- void *out_value = alloca(out_cpp_type->size());
- conversions.convert_to_uninitialized(*in_cpp_type, *out_cpp_type, value, out_value);
-
const int domain_size = this->attribute_domain_size(domain);
- blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>(
- domain, domain_size, *out_cpp_type, out_value);
-
- out_cpp_type->destruct(out_value);
- return attribute;
+ return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
}
-OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value)
-{
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
-
- WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name);
+class GVMutableAttribute_For_OutputAttribute
+ : public blender::fn::GVMutableArray_For_GMutableSpan {
+ public:
+ GeometryComponent *component;
+ std::string final_name;
- /* If the attribute doesn't exist, make a new one with the correct type. */
- if (!attribute) {
- this->attribute_try_create(attribute_name, domain, data_type);
- attribute = this->attribute_try_get_for_write(attribute_name);
- if (attribute && default_value != nullptr) {
- void *data = attribute->get_span_for_write_only().data();
- cpp_type->fill_initialized(default_value, data, attribute->size());
- attribute->apply_span();
- }
- return OutputAttributePtr(std::move(attribute));
+ GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
+ GeometryComponent &component,
+ std::string final_name)
+ : blender::fn::GVMutableArray_For_GMutableSpan(data),
+ component(&component),
+ final_name(std::move(final_name))
+ {
}
- /* If an existing attribute has a matching domain and type, just use that. */
- if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) {
- return OutputAttributePtr(std::move(attribute));
+ ~GVMutableAttribute_For_OutputAttribute() override
+ {
+ type_->destruct_n(data_, size_);
+ MEM_freeN(data_);
}
+};
- /* Otherwise create a temporary buffer to use before saving the new attribute. */
- return OutputAttributePtr(*this, domain, attribute_name, data_type);
-}
-
-/* Construct from an attribute that already exists in the geometry component. */
-OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute)
- : attribute_(std::move(attribute))
+static void save_output_attribute(blender::bke::OutputAttribute &output_attribute)
{
-}
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
-/* Construct a temporary attribute that has to replace an existing one later on. */
-OutputAttributePtr::OutputAttributePtr(GeometryComponent &component,
- AttributeDomain domain,
- std::string final_name,
- CustomDataType data_type)
-{
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
+ GVMutableAttribute_For_OutputAttribute &varray =
+ dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray());
- const int domain_size = component.attribute_domain_size(domain);
- void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__);
- GMutableSpan new_span{*cpp_type, buffer, domain_size};
+ GeometryComponent &component = *varray.component;
+ const StringRefNull name = varray.final_name;
+ const AttributeDomain domain = output_attribute.domain();
+ const CustomDataType data_type = output_attribute.custom_data_type();
+ const CPPType &cpp_type = output_attribute.cpp_type();
- /* Copy converted values from conflicting attribute, in case the value is read.
- * TODO: An optimization could be to not do this, when the caller says that the attribute will
- * only be written. */
- ReadAttributePtr src_attribute = component.attribute_get_for_read(
- final_name, domain, data_type, nullptr);
- for (const int i : blender::IndexRange(domain_size)) {
- src_attribute->get(i, new_span[i]);
+ component.attribute_try_delete(name);
+ if (!component.attribute_try_create(
+ varray.final_name, domain, data_type, AttributeInitDefault())) {
+ CLOG_WARN(&LOG,
+ "Could not create the '%s' attribute with type '%s'.",
+ name.c_str(),
+ cpp_type.name().c_str());
+ return;
+ }
+ WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
+ BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
+ for (const int i : IndexRange(varray.size())) {
+ varray.get(i, buffer);
+ write_attribute.varray->set_by_relocate(i, buffer);
}
-
- attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>(
- domain, new_span, component, std::move(final_name));
}
-/* Store the computed attribute. If it was stored from the beginning already, nothing is done. This
- * might delete another attribute with the same name. */
-void OutputAttributePtr::save()
+static blender::bke::OutputAttribute create_output_attribute(
+ GeometryComponent &component,
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const bool ignore_old_values,
+ const void *default_value)
{
- if (!attribute_) {
- CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore.");
- return;
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
+
+ if (attribute_name.is_empty()) {
+ return {};
}
- blender::bke::TemporaryWriteAttribute *attribute =
- dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get());
+ const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+ const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
- if (attribute == nullptr) {
- /* The attribute is saved already. */
- attribute_.reset();
- return;
+ if (component.attribute_is_builtin(attribute_name)) {
+ WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ if (default_value) {
+ const int64_t domain_size = component.attribute_domain_size(domain);
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create_builtin(attribute_name,
+ AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
+ }
+ attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ /* Builtin attribute does not exist and can't be created. */
+ return {};
+ }
+ }
+ if (attribute.domain != domain) {
+ /* Builtin attribute is on different domain. */
+ return {};
+ }
+ GVMutableArrayPtr varray = std::move(attribute.varray);
+ if (varray->type() == *cpp_type) {
+ /* Builtin attribute matches exactly. */
+ return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
+ }
+ /* Builtin attribute is on the same domain but has a different data type. */
+ varray = conversions.try_convert(std::move(varray), *cpp_type);
+ return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
}
- StringRefNull name = attribute->final_name;
- const blender::fn::CPPType &cpp_type = attribute->cpp_type();
+ const int domain_size = component.attribute_domain_size(domain);
- /* Delete an existing attribute with the same name if necessary. */
- attribute->component.attribute_try_delete(name);
+ WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ if (default_value) {
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create(
+ attribute_name, domain, data_type, AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault());
+ }
- if (!attribute->component.attribute_try_create(
- name, attribute_->domain(), attribute_->custom_data_type())) {
- /* Cannot create the target attribute for some reason. */
- CLOG_WARN(&LOG,
- "Creating the '%s' attribute with type '%s' failed.",
- name.c_str(),
- cpp_type.name().c_str());
- attribute_.reset();
- return;
+ attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ /* Can't create the attribute. */
+ return {};
+ }
+ }
+ if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
+ /* Existing generic attribute matches exactly. */
+ return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
}
- WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name);
-
- GMutableSpan temp_span = attribute->data;
- GMutableSpan new_span = new_attribute->get_span_for_write_only();
- BLI_assert(temp_span.size() == new_span.size());
-
- /* Currently we copy over the attribute. In the future we want to reuse the buffer. */
- cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size());
- new_attribute->apply_span();
+ /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
+ * attribute after processing is done. */
+ void *data = MEM_mallocN_aligned(
+ cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
+ if (ignore_old_values) {
+ /* This does nothing for trivially constructible types, but is necessary for correctness. */
+ cpp_type->construct_default_n(data, domain);
+ }
+ else {
+ /* Fill the temporary array with values from the existing attribute. */
+ GVArrayPtr old_varray = component.attribute_get_for_read(
+ attribute_name, domain, data_type, default_value);
+ old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
+ }
+ GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>(
+ GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name);
- attribute_.reset();
+ return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
}
-OutputAttributePtr::~OutputAttributePtr()
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output(
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value)
{
- if (attribute_) {
- CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save.");
- }
+ return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value);
}
-/* Utility function to call #apply_span and #save in the right order. */
-void OutputAttributePtr::apply_span_and_save()
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
{
- BLI_assert(attribute_);
- attribute_->apply_span();
- this->save();
+ return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
}
-
-/** \} */
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 806d10e9e89..b3a795faa30 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -24,166 +24,8 @@
namespace blender::bke {
-class ConstantReadAttribute final : public ReadAttribute {
- private:
- void *value_;
-
- public:
- ConstantReadAttribute(AttributeDomain domain,
- const int64_t size,
- const CPPType &type,
- const void *value)
- : ReadAttribute(domain, type, size)
- {
- value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
- type.copy_to_uninitialized(value, value_);
- }
-
- ~ConstantReadAttribute() override
- {
- this->cpp_type_.destruct(value_);
- MEM_freeN(value_);
- }
-
- void get_internal(const int64_t UNUSED(index), void *r_value) const override
- {
- this->cpp_type_.copy_to_uninitialized(value_, r_value);
- }
-
- void initialize_span() const override
- {
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
- }
-};
-
-template<typename T> class ArrayReadAttribute final : public ReadAttribute {
- private:
- Span<T> data_;
-
- public:
- ArrayReadAttribute(AttributeDomain domain, Span<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
- private:
- Array<T> data_;
-
- public:
- OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
-class DerivedArrayReadAttribute final : public ReadAttribute {
- private:
- Span<StructT> data_;
-
- public:
- DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
- : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-};
-
-template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<T> data_;
-
- public:
- ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
- : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data_[index] = *reinterpret_cast<const T *>(value);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data_.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
-template<typename StructT,
- typename ElemT,
- ElemT (*GetFunc)(const StructT &),
- void (*SetFunc)(StructT &, const ElemT &)>
-class DerivedArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<StructT> data_;
-
- public:
- DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
- : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- StructT &struct_value = data_[index];
- const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
- SetFunc(struct_value, typed_value);
- }
-};
+using fn::GVArrayPtr;
+using fn::GVMutableArrayPtr;
/**
* Utility to group together multiple functions that are used to access custom data on geometry
@@ -244,10 +86,11 @@ class BuiltinAttributeProvider {
{
}
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
+ virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
+ virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0;
virtual bool try_delete(GeometryComponent &component) const = 0;
- virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
+ virtual bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const = 0;
virtual bool exists(const GeometryComponent &component) const = 0;
StringRefNull name() const
@@ -272,15 +115,16 @@ class BuiltinAttributeProvider {
*/
class DynamicAttributesProvider {
public:
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const = 0;
+ virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
+ virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const StringRef UNUSED(attribute_name),
const AttributeDomain UNUSED(domain),
- const CustomDataType UNUSED(data_type)) const
+ const CustomDataType UNUSED(data_type),
+ const AttributeInit &UNUSED(initializer)) const
{
/* Some providers should not create new attributes. */
return false;
@@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final;
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type) const final;
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
template<typename T>
- ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
- const int domain_size) const
+ ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
+ const int domain_size) const
{
- return std::make_unique<ArrayReadAttribute<T>>(
- domain_, Span(static_cast<const T *>(layer.data), domain_size));
+ return {std::make_unique<fn::GVArray_For_Span<T>>(
+ Span(static_cast<const T *>(layer.data), domain_size)),
+ domain_};
}
template<typename T>
- WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
+ WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
+ const int domain_size) const
{
- return std::make_unique<ArrayWriteAttribute<T>>(
- domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
+ return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
+ MutableSpan(static_cast<T *>(layer.data), domain_size)),
+ domain_};
}
bool type_is_supported(CustomDataType data_type) const
@@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
*/
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
private:
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
const AttributeDomain domain_;
const CustomDataType attribute_type_;
const CustomDataType stored_type_;
@@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final;
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
* the #MVert struct, but is exposed as float3 attribute.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
using UpdateOnRead = void (*)(const GeometryComponent &component);
using UpdateOnWrite = void (*)(GeometryComponent &component);
const CustomDataType stored_type_;
@@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component) const final;
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final;
bool try_delete(GeometryComponent &component) const final;
- bool try_create(GeometryComponent &component) const final;
+ bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
bool exists(const GeometryComponent &component) const final;
};
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index e8879cdda8f..e84b485c466 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -132,7 +132,7 @@ static void blender_version_init(void)
BLI_snprintf(blender_version_string,
ARRAY_SIZE(blender_version_string),
- "%d.%02d.%d%s",
+ "%d.%01d.%d%s",
BLENDER_VERSION / 100,
BLENDER_VERSION % 100,
BLENDER_VERSION_PATCH,
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index a61e7a2d1d8..54fd3f55c31 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -399,7 +399,8 @@ static void setup_app_data(bContext *C,
BKE_lib_override_library_main_resync(
bmain,
curscene,
- bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene));
+ bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene),
+ reports);
/* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
BKE_lib_override_library_main_operations_create(bmain, true);
}
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index ef567044282..20c5af0efb6 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -989,6 +989,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothfac = 0.1f;
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 1;
+ brush->gpencil_settings->dilate_pixels = 1;
brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES;
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 2ee030ca83f..17f36bd0860 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -2836,7 +2836,7 @@ static void actcon_get_tarmat(struct Depsgraph *depsgraph,
* including rotation order, otherwise this fails. */
pchan = cob->pchan;
- tchan = BKE_pose_channel_verify(&pose, pchan->name);
+ tchan = BKE_pose_channel_ensure(&pose, pchan->name);
tchan->rotmode = pchan->rotmode;
/* evaluate action using workob (it will only set the PoseChannel in question) */
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index cbf7a4483c0..81830f5bb61 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -80,7 +80,17 @@ struct bContext {
struct ARegion *menu;
struct wmGizmoGroup *gizmo_group;
struct bContextStore *store;
- const char *operator_poll_msg; /* reason for poll failing */
+
+ /* Operator poll. */
+ /**
+ * Store the reason the poll function fails (static string, not allocated).
+ * For more advanced formatting use `operator_poll_msg_dyn_params`.
+ */
+ const char *operator_poll_msg;
+ /**
+ * Store values to dynamically to create the string (called when a tool-tip is shown).
+ */
+ struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params;
} wm;
/* data context */
@@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C)
{
bContext *newC = MEM_dupallocN((void *)C);
+ memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params));
+
return newC;
}
void CTX_free(bContext *C)
{
+ /* This may contain a dynamically allocated message, free. */
+ CTX_wm_operator_poll_msg_clear(C);
+
MEM_freeN(C);
}
@@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup)
C->wm.gizmo_group = gzgroup;
}
+void CTX_wm_operator_poll_msg_clear(bContext *C)
+{
+ struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
+ if (params->free_fn != NULL) {
+ params->free_fn(C, params->user_data);
+ }
+ params->get_fn = NULL;
+ params->free_fn = NULL;
+ params->user_data = NULL;
+
+ C->wm.operator_poll_msg = NULL;
+}
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
{
+ CTX_wm_operator_poll_msg_clear(C);
+
C->wm.operator_poll_msg = msg;
}
-const char *CTX_wm_operator_poll_msg_get(bContext *C)
+void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
+ const struct bContextPollMsgDyn_Params *params)
{
+ CTX_wm_operator_poll_msg_clear(C);
+
+ C->wm.operator_poll_msg_dyn_params = *params;
+}
+
+const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
+{
+ struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
+ if (params->get_fn != NULL) {
+ char *msg = params->get_fn(C, params->user_data);
+ if (msg != NULL) {
+ *r_free = true;
+ }
+ return msg;
+ }
+
+ *r_free = false;
return IFACE_(C->wm.operator_poll_msg);
}
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
new file mode 100644
index 00000000000..19bbd8178b7
--- /dev/null
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -0,0 +1,237 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_listbase.h"
+#include "BLI_span.hh"
+
+#include "DNA_curve_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::float4x4;
+using blender::Span;
+
+blender::Span<SplinePtr> CurveEval::splines() const
+{
+ return splines_;
+}
+
+blender::MutableSpan<SplinePtr> CurveEval::splines()
+{
+ return splines_;
+}
+
+void CurveEval::add_spline(SplinePtr spline)
+{
+ splines_.append(std::move(spline));
+}
+
+void CurveEval::remove_splines(blender::IndexMask mask)
+{
+ for (int i = mask.size() - 1; i >= 0; i--) {
+ splines_.remove_and_reorder(mask.indices()[i]);
+ }
+}
+
+CurveEval *CurveEval::copy()
+{
+ CurveEval *new_curve = new CurveEval();
+
+ for (SplinePtr &spline : this->splines()) {
+ new_curve->add_spline(spline->copy());
+ }
+
+ return new_curve;
+}
+
+void CurveEval::translate(const float3 &translation)
+{
+ for (SplinePtr &spline : this->splines()) {
+ spline->translate(translation);
+ spline->mark_cache_invalid();
+ }
+}
+
+void CurveEval::transform(const float4x4 &matrix)
+{
+ for (SplinePtr &spline : this->splines()) {
+ spline->transform(matrix);
+ }
+}
+
+void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
+{
+ for (const SplinePtr &spline : this->splines()) {
+ spline->bounds_min_max(min, max, use_evaluated);
+ }
+}
+
+/**
+ * Return the start indices for each of the curve spline's evaluated points, as if they were part
+ * of a flattened array. This can be used to facilitate parallelism by avoiding the need to
+ * accumulate an offset while doing more complex calculations.
+ *
+ * \note The result array is one longer than the spline count; the last element is the total size.
+ */
+blender::Array<int> CurveEval::control_point_offsets() const
+{
+ Array<int> offsets(splines_.size() + 1);
+ int offset = 0;
+ for (const int i : splines_.index_range()) {
+ offsets[i] = offset;
+ offset += splines_[i]->size();
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+/**
+ * Exactly like #control_point_offsets, but uses the number of evaluated points instead.
+ */
+blender::Array<int> CurveEval::evaluated_point_offsets() const
+{
+ Array<int> offsets(splines_.size() + 1);
+ int offset = 0;
+ for (const int i : splines_.index_range()) {
+ offsets[i] = offset;
+ offset += splines_[i]->evaluated_points_size();
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
+{
+ switch (dna_handle_type) {
+ case HD_FREE:
+ return BezierSpline::HandleType::Free;
+ case HD_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case HD_VECT:
+ return BezierSpline::HandleType::Vector;
+ case HD_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case HD_AUTO_ANIM:
+ return BezierSpline::HandleType::Auto;
+ case HD_ALIGN_DOUBLESIDE:
+ return BezierSpline::HandleType::Align;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
+{
+ switch (twist_mode) {
+ case CU_TWIST_Z_UP:
+ return Spline::NormalCalculationMode::ZUp;
+ case CU_TWIST_MINIMUM:
+ return Spline::NormalCalculationMode::Minimum;
+ case CU_TWIST_TANGENT:
+ return Spline::NormalCalculationMode::Tangent;
+ }
+ BLI_assert_unreachable();
+ return Spline::NormalCalculationMode::Minimum;
+}
+
+static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag)
+{
+ switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
+ case CU_NURB_ENDPOINT:
+ return NURBSpline::KnotsMode::EndPoint;
+ case CU_NURB_BEZIER:
+ return NURBSpline::KnotsMode::Bezier;
+ default:
+ return NURBSpline::KnotsMode::Normal;
+ }
+
+ BLI_assert_unreachable();
+ return NURBSpline::KnotsMode::Normal;
+}
+
+std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+
+ const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve));
+
+ /* TODO: Optimize by reserving the correct points size. */
+ LISTBASE_FOREACH (const Nurb *, nurb, nurbs) {
+ switch (nurb->type) {
+ case CU_BEZIER: {
+ std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
+ spline->set_resolution(nurb->resolu);
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+
+ for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) {
+ spline->add_point(bezt.vec[1],
+ handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1),
+ bezt.vec[0],
+ handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2),
+ bezt.vec[2],
+ bezt.radius,
+ bezt.tilt);
+ }
+
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ case CU_NURBS: {
+ std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
+ spline->set_resolution(nurb->resolu);
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+ spline->set_order(nurb->orderu);
+ spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu);
+
+ for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
+ spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
+ }
+
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ case CU_POLY: {
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+
+ for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
+ spline->add_point(bp.vec, bp.radius, bp.tilt);
+ }
+
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ }
+
+ /* Note: Normal mode is stored separately in each spline to facilitate combining splines
+ * from multiple curve objects, where the value may be different. */
+ const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
+ dna_curve.twist_mode);
+ for (SplinePtr &spline : curve->splines()) {
+ spline->normal_mode = normal_mode;
+ }
+
+ return curve;
+}
diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c
index 752e0d4dfcf..00cdc7b3031 100644
--- a/source/blender/blenkernel/intern/curveprofile.c
+++ b/source/blender/blenkernel/intern/curveprofile.c
@@ -1015,7 +1015,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
{
const float total_length = BKE_curveprofile_total_length(profile);
const float segment_length = total_length / n_segments;
- float length_travelled = 0.0f;
float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0);
float distance_to_previous_table_point = 0.0f;
int i_table = 0;
@@ -1029,7 +1028,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
for (int i = 1; i < n_segments; i++) {
/* Travel over all of the points that fit inside this segment. */
while (distance_to_next_table_point < segment_left) {
- length_travelled += distance_to_next_table_point;
segment_left -= distance_to_next_table_point;
i_table++;
distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table);
@@ -1057,7 +1055,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
/* We sampled in between this table point and the next, so the next travel step is smaller. */
distance_to_next_table_point -= segment_left;
distance_to_previous_table_point += segment_left;
- length_travelled += segment_left;
segment_left = segment_length;
}
}
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.cc
index ad8939fa5d1..20534ef933b 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -21,9 +21,9 @@
* \ingroup bke
*/
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -82,7 +82,7 @@ void BKE_displist_free(ListBase *lb)
{
DispList *dl;
- while ((dl = BLI_pophead(lb))) {
+ while ((dl = (DispList *)BLI_pophead(lb))) {
BKE_displist_elem_free(dl);
}
}
@@ -95,7 +95,7 @@ DispList *BKE_displist_find_or_create(ListBase *lb, int type)
}
}
- DispList *dl = MEM_callocN(sizeof(DispList), "find_disp");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "find_disp");
dl->type = type;
BLI_addtail(lb, dl);
@@ -110,7 +110,7 @@ DispList *BKE_displist_find(ListBase *lb, int type)
}
}
- return NULL;
+ return nullptr;
}
bool BKE_displist_has_faces(const ListBase *lb)
@@ -129,11 +129,11 @@ void BKE_displist_copy(ListBase *lbn, const ListBase *lb)
BKE_displist_free(lbn);
LISTBASE_FOREACH (const DispList *, dl, lb) {
- DispList *dln = MEM_dupallocN(dl);
+ DispList *dln = (DispList *)MEM_dupallocN(dl);
BLI_addtail(lbn, dln);
- dln->verts = MEM_dupallocN(dl->verts);
- dln->nors = MEM_dupallocN(dl->nors);
- dln->index = MEM_dupallocN(dl->index);
+ dln->verts = (float *)MEM_dupallocN(dl->verts);
+ dln->nors = (float *)MEM_dupallocN(dl->nors);
+ dln->index = (int *)MEM_dupallocN(dl->index);
}
}
@@ -146,8 +146,8 @@ void BKE_displist_normals_add(ListBase *lb)
LISTBASE_FOREACH (DispList *, dl, lb) {
if (dl->type == DL_INDEX3) {
- if (dl->nors == NULL) {
- dl->nors = MEM_callocN(sizeof(float[3]), "dlnors");
+ if (dl->nors == nullptr) {
+ dl->nors = (float *)MEM_callocN(sizeof(float[3]), "dlnors");
if (dl->flag & DL_BACK_CURVE) {
dl->nors[2] = -1.0f;
@@ -158,8 +158,8 @@ void BKE_displist_normals_add(ListBase *lb)
}
}
else if (dl->type == DL_SURF) {
- if (dl->nors == NULL) {
- dl->nors = MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
+ if (dl->nors == nullptr) {
+ dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
vdata = dl->verts;
ndata = dl->nors;
@@ -338,9 +338,9 @@ static void curve_to_displist(const Curve *cu,
* and resolution > 1. */
const bool use_cyclic_sample = is_cyclic && (samples_len != 2);
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
/* Add one to the length because of 'BKE_curve_forward_diff_bezier'. */
- dl->verts = MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = samples_len;
@@ -393,8 +393,8 @@ static void curve_to_displist(const Curve *cu,
}
else if (nu->type == CU_NURBS) {
const int len = (resolution * SEGMENTSU(nu));
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -402,12 +402,12 @@ static void curve_to_displist(const Curve *cu,
dl->charidx = nu->charidx;
dl->type = is_cyclic ? DL_POLY : DL_SEGM;
- BKE_nurb_makeCurve(nu, dl->verts, NULL, NULL, NULL, resolution, sizeof(float[3]));
+ BKE_nurb_makeCurve(nu, dl->verts, nullptr, nullptr, nullptr, resolution, sizeof(float[3]));
}
else if (nu->type == CU_POLY) {
const int len = nu->pntsu;
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -435,7 +435,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const float normal_proj[3],
const bool flip_normal)
{
- if (dispbase == NULL) {
+ if (dispbase == nullptr) {
return;
}
if (BLI_listbase_is_empty(dispbase)) {
@@ -471,14 +471,14 @@ void BKE_displist_fill(const ListBase *dispbase,
sf_ctx.poly_nr++;
/* Make verts and edges. */
- ScanFillVert *sf_vert = NULL;
- ScanFillVert *sf_vert_last = NULL;
- ScanFillVert *sf_vert_new = NULL;
+ ScanFillVert *sf_vert = nullptr;
+ ScanFillVert *sf_vert_last = nullptr;
+ ScanFillVert *sf_vert_new = nullptr;
for (int i = 0; i < dl->nr; i++) {
sf_vert_last = sf_vert;
sf_vert = BLI_scanfill_vert_add(&sf_ctx, &dl->verts[3 * i]);
totvert++;
- if (sf_vert_last == NULL) {
+ if (sf_vert_last == nullptr) {
sf_vert_new = sf_vert;
}
else {
@@ -486,7 +486,7 @@ void BKE_displist_fill(const ListBase *dispbase,
}
}
- if (sf_vert != NULL && sf_vert_new != NULL) {
+ if (sf_vert != nullptr && sf_vert_new != nullptr) {
BLI_scanfill_edge_add(&sf_ctx, sf_vert, sf_vert_new);
}
}
@@ -503,7 +503,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const int triangles_len = BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal_proj);
if (totvert != 0 && triangles_len != 0) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), "filldisplist");
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), "filldisplist");
dlnew->type = DL_INDEX3;
dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE));
dlnew->rt = (dl_rt_accum & CU_SMOOTH);
@@ -511,8 +511,8 @@ void BKE_displist_fill(const ListBase *dispbase,
dlnew->nr = totvert;
dlnew->parts = triangles_len;
- dlnew->index = MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
+ dlnew->index = (int *)MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
/* vert data */
int i;
@@ -551,16 +551,16 @@ void BKE_displist_fill(const ListBase *dispbase,
static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
{
- ListBase front = {NULL, NULL};
- ListBase back = {NULL, NULL};
+ ListBase front = {nullptr, nullptr};
+ ListBase back = {nullptr, nullptr};
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
if (dl->type == DL_SURF) {
if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U) == 0) {
if ((cu->flag & CU_BACK) && (dl->flag & DL_BACK_CURVE)) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&front, dlnew);
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -577,9 +577,9 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
}
}
if ((cu->flag & CU_FRONT) && (dl->flag & DL_FRONT_CURVE)) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&back, dlnew);
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -634,16 +634,16 @@ static float displist_calc_taper(Depsgraph *depsgraph,
Object *taperobj,
float fac)
{
- DispList *dl;
-
- if (taperobj == NULL || taperobj->type != OB_CURVE) {
+ if (taperobj == nullptr || taperobj->type != OB_CURVE) {
return 1.0;
}
- dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL;
- if (dl == NULL) {
+ DispList *dl = taperobj->runtime.curve_cache ?
+ (DispList *)taperobj->runtime.curve_cache->disp.first :
+ nullptr;
+ if (dl == nullptr) {
BKE_displist_make_curveTypes(depsgraph, scene, taperobj, false, false);
- dl = taperobj->runtime.curve_cache->disp.first;
+ dl = (DispList *)taperobj->runtime.curve_cache->disp.first;
}
if (dl) {
float minx, dx, *fp;
@@ -693,7 +693,8 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
BKE_displist_free(&(ob->runtime.curve_cache->disp));
}
else {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall");
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for MBall");
}
BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp);
@@ -738,9 +739,9 @@ static ModifierData *curve_get_tessellate_point(const Scene *scene,
required_mode |= eModifierMode_Editmode;
}
- pretessellatePoint = NULL;
+ pretessellatePoint = nullptr;
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -777,22 +778,22 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
ModifierData *pretessellatePoint;
- Curve *cu = ob->data;
+ Curve *cu = (Curve *)ob->data;
int numElems = 0, numVerts = 0;
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- ModifierApplyFlag apply_flag = 0;
- float(*deformedVerts)[3] = NULL;
- float *keyVerts = NULL;
+ ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
+ float(*deformedVerts)[3] = nullptr;
+ float *keyVerts = nullptr;
int required_mode;
bool modified = false;
BKE_modifiers_clear_errors(ob);
if (editmode) {
- apply_flag |= MOD_APPLY_USECACHE;
+ apply_flag = MOD_APPLY_USECACHE;
}
if (for_render) {
- apply_flag |= MOD_APPLY_RENDER;
+ apply_flag = MOD_APPLY_RENDER;
required_mode = eModifierMode_Render;
}
else {
@@ -823,7 +824,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
if (pretessellatePoint) {
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -836,7 +837,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts);
}
- mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts);
+ mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts);
modified = true;
if (md == pretessellatePoint) {
@@ -869,7 +870,7 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[
*r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr;
}
- allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts");
+ allverts = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__);
fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
@@ -903,16 +904,16 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
ModifierData *pretessellatePoint;
- const Curve *cu = ob->data;
+ const Curve *cu = (const Curve *)ob->data;
int required_mode = 0, totvert = 0;
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- Mesh *modified = NULL, *mesh_applied;
- float(*vertCos)[3] = NULL;
+ Mesh *modified = nullptr, *mesh_applied;
+ float(*vertCos)[3] = nullptr;
int useCache = !for_render;
- ModifierApplyFlag apply_flag = 0;
+ ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
if (for_render) {
- apply_flag |= MOD_APPLY_RENDER;
+ apply_flag = MOD_APPLY_RENDER;
required_mode = eModifierMode_Render;
}
else {
@@ -920,9 +921,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
const ModifierEvalContext mectx_deform = {
- depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
+ depsgraph, ob, editmode ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
const ModifierEvalContext mectx_apply = {
- depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
+ depsgraph, ob, useCache ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
@@ -935,22 +936,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
if (r_final && *r_final) {
- BKE_id_free(NULL, *r_final);
+ BKE_id_free(nullptr, *r_final);
}
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
}
/* If we need normals, no choice, have to convert to mesh now. */
- bool need_normal = mti->dependsOnNormals != NULL && mti->dependsOnNormals(md);
+ bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md);
/* XXX 2.8 : now that batch cache is stored inside the ob->data
* we need to create a Mesh for each curve that uses modifiers. */
- if (modified == NULL /* && need_normal */) {
- if (vertCos != NULL) {
+ if (modified == nullptr /* && need_normal */) {
+ if (vertCos != nullptr) {
displist_vert_coords_apply(dispbase, vertCos);
}
@@ -976,7 +977,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (!vertCos) {
vertCos = displist_vert_coords_alloc(dispbase, &totvert);
}
- mti->deformVerts(md, &mectx_deform, NULL, vertCos, totvert);
+ mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert);
}
}
else {
@@ -991,8 +992,8 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (modified) {
if (vertCos) {
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
- NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
- BKE_id_free(NULL, modified);
+ nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1013,7 +1014,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
/* Vertex coordinates were applied to necessary data, could free it */
MEM_freeN(vertCos);
- vertCos = NULL;
+ vertCos = nullptr;
}
if (need_normal) {
@@ -1025,7 +1026,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
/* Modifier returned a new derived mesh */
if (modified && modified != mesh_applied) { /* Modifier */
- BKE_id_free(NULL, modified);
+ BKE_id_free(nullptr, modified);
}
modified = mesh_applied;
}
@@ -1034,8 +1035,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
if (modified) {
- Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
- BKE_id_free(NULL, modified);
+ Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
+ nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1046,7 +1048,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
else {
displist_vert_coords_apply(dispbase, vertCos);
MEM_freeN(vertCos);
- vertCos = NULL;
+ vertCos = nullptr;
}
}
@@ -1081,18 +1083,18 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
MEM_SAFE_FREE(modified->mat);
/* Set flag which makes it easier to see what's going on in a debugger. */
modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
- modified->mat = MEM_dupallocN(cu->mat);
+ modified->mat = (Material **)MEM_dupallocN(cu->mat);
modified->totcol = cu->totcol;
(*r_final) = modified;
}
else {
- (*r_final) = NULL;
+ (*r_final) = nullptr;
}
}
- else if (modified != NULL) {
+ else if (modified != nullptr) {
/* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */
- BKE_id_free(NULL, modified);
+ BKE_id_free(nullptr, modified);
}
}
@@ -1103,8 +1105,8 @@ static void displist_surf_indices(DispList *dl)
dl->totindex = 0;
- index = dl->index = MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
- "index array nurbs");
+ index = dl->index = (int *)MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
+ "index array nurbs");
for (a = 0; a < dl->parts; a++) {
@@ -1136,8 +1138,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
const bool for_render,
const bool for_orco)
{
- ListBase nubase = {NULL, NULL};
- Curve *cu = ob->data;
+ ListBase nubase = {nullptr, nullptr};
+ Curve *cu = (Curve *)ob->data;
DispList *dl;
float *data;
int len;
@@ -1174,8 +1176,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
if (nu->pntsv == 1) {
len = SEGMENTSU(nu) * resolu;
- dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
@@ -1195,13 +1197,13 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
dl->type = DL_SEGM;
}
- BKE_nurb_makeCurve(nu, data, NULL, NULL, NULL, resolu, sizeof(float[3]));
+ BKE_nurb_makeCurve(nu, data, nullptr, nullptr, nullptr, resolu, sizeof(float[3]));
}
else {
len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
- dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->col = nu->mat_nr;
@@ -1258,7 +1260,7 @@ static void rotateBevelPiece(const Curve *cu,
vec[1] = fp[2];
vec[2] = 0.0;
- if (nbevp == NULL) {
+ if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
copy_qt_qt(quat, bevp->quat);
}
@@ -1276,7 +1278,7 @@ static void rotateBevelPiece(const Curve *cu,
else {
float sina, cosa;
- if (nbevp == NULL) {
+ if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
sina = bevp->sina;
cosa = bevp->cosa;
@@ -1307,8 +1309,8 @@ static void fillBevelCap(const Nurb *nu,
{
DispList *dl;
- dl = MEM_callocN(sizeof(DispList), "makeDispListbev2");
- dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
+ dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev2");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
memcpy(dl->verts, prev_fp, sizeof(float[3]) * dlb->nr);
dl->type = DL_POLY;
@@ -1469,7 +1471,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
const bool for_orco,
Mesh **r_final)
{
- Curve *cu = ob->data;
+ Curve *cu = (Curve *)ob->data;
/* we do allow duplis... this is only displist on curve level */
if (!ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) {
@@ -1481,7 +1483,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
else if (ELEM(ob->type, OB_CURVE, OB_FONT)) {
ListBase dlbev;
- ListBase nubase = {NULL, NULL};
+ ListBase nubase = {nullptr, nullptr};
bool force_mesh_conversion = false;
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
@@ -1494,7 +1496,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
if (ob->runtime.curve_cache->anim_path_accum_length) {
MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
- ob->runtime.curve_cache->anim_path_accum_length = NULL;
+ ob->runtime.curve_cache->anim_path_accum_length = nullptr;
}
if (ob->type == OB_FONT) {
@@ -1520,8 +1522,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
else {
const float widfac = cu->width - 1.0f;
- BevList *bl = ob->runtime.curve_cache->bev.first;
- Nurb *nu = nubase.first;
+ BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first;
+ Nurb *nu = (Nurb *)nubase.first;
for (; bl && nu; bl = bl->next, nu = nu->next) {
float *data;
@@ -1532,8 +1534,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
/* exception handling; curve without bevel or extrude, with width correction */
if (BLI_listbase_is_empty(&dlbev)) {
- DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev");
- dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
BLI_addtail(dispbase, dl);
if (bl->poly != -1) {
@@ -1565,8 +1567,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
}
else {
- ListBase bottom_capbase = {NULL, NULL};
- ListBase top_capbase = {NULL, NULL};
+ ListBase bottom_capbase = {nullptr, nullptr};
+ ListBase top_capbase = {nullptr, nullptr};
float bottom_no[3] = {0.0f};
float top_no[3] = {0.0f};
float first_blend = 0.0f, last_blend = 0.0f;
@@ -1585,8 +1587,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
LISTBASE_FOREACH (DispList *, dlb, &dlbev) {
/* for each part of the bevel use a separate displblock */
- DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev1");
- dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1");
+ dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
BLI_addtail(dispbase, dl);
dl->type = DL_SURF;
@@ -1616,7 +1618,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
float radius_factor = 1.0;
float *cur_data = data;
- if (cu->taperobj == NULL) {
+ if (cu->taperobj == nullptr) {
radius_factor = bevp->radius;
}
else {
@@ -1666,7 +1668,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data);
}
else {
- rotateBevelPiece(cu, bevp, NULL, dlb, 0.0f, widfac, radius_factor, &data);
+ rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data);
}
if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) {
@@ -1737,15 +1739,16 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
BKE_object_free_derived_caches(ob);
if (!ob->runtime.curve_cache) {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types");
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for curve types");
}
dispbase = &(ob->runtime.curve_cache->disp);
- Mesh *mesh_eval = NULL;
+ Mesh *mesh_eval = nullptr;
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval);
- if (mesh_eval != NULL) {
+ if (mesh_eval != nullptr) {
BKE_object_eval_assign_data(ob, &mesh_eval->id, true);
}
@@ -1759,8 +1762,9 @@ void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph,
Mesh **r_final,
const bool for_orco)
{
- if (ob->runtime.curve_cache == NULL) {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+ if (ob->runtime.curve_cache == nullptr) {
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for Curve");
}
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final);
@@ -1797,8 +1801,8 @@ static void boundbox_displist_object(Object *ob)
*/
/* object's BB is calculated from final displist */
- if (ob->runtime.bb == NULL) {
- ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox");
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox");
}
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
new file mode 100644
index 00000000000..44c5cce92dd
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -0,0 +1,735 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+#include "BKE_geometry_set.hh"
+
+#include "attribute_access_intern.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component Implementation
+ * \{ */
+
+CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
+{
+}
+
+CurveComponent::~CurveComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *CurveComponent::copy() const
+{
+ CurveComponent *new_component = new CurveComponent();
+ if (curve_ != nullptr) {
+ new_component->curve_ = curve_->copy();
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void CurveComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (curve_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ delete curve_;
+ }
+ curve_ = nullptr;
+ }
+}
+
+bool CurveComponent::has_curve() const
+{
+ return curve_ != nullptr;
+}
+
+/* Clear the component and replace it with the new curve. */
+void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ curve_ = curve;
+ ownership_ = ownership;
+}
+
+CurveEval *CurveComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ CurveEval *curve = curve_;
+ curve_ = nullptr;
+ return curve;
+}
+
+const CurveEval *CurveComponent::get_for_read() const
+{
+ return curve_;
+}
+
+CurveEval *CurveComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ curve_ = curve_->copy();
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return curve_;
+}
+
+bool CurveComponent::is_empty() const
+{
+ return curve_ == nullptr;
+}
+
+bool CurveComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void CurveComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ curve_ = curve_->copy();
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Access Helper Functions
+ * \{ */
+
+int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ if (curve_ == nullptr) {
+ return 0;
+ }
+ if (domain == ATTR_DOMAIN_POINT) {
+ int total = 0;
+ for (const SplinePtr &spline : curve_->splines()) {
+ total += spline->size();
+ }
+ return total;
+ }
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return curve_->splines().size();
+ }
+ return 0;
+}
+
+static CurveEval *get_curve_from_component_for_write(GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
+ CurveComponent &curve_component = static_cast<CurveComponent &>(component);
+ return curve_component.get_for_write();
+}
+
+static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return curve_component.get_for_read();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Builtin Spline Attributes
+ *
+ * Attributes with a value for every spline, stored contiguously or in every spline separately.
+ * \{ */
+
+namespace blender::bke {
+
+class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
+ using AsReadAttribute = GVArrayPtr (*)(const CurveEval &data);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(CurveEval &data);
+ const AsReadAttribute as_read_attribute_;
+ const AsWriteAttribute as_write_attribute_;
+
+ public:
+ BuiltinSplineAttributeProvider(std::string attribute_name,
+ const CustomDataType attribute_type,
+ const WritableEnum writable,
+ const AsReadAttribute as_read_attribute,
+ const AsWriteAttribute as_write_attribute)
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ ATTR_DOMAIN_CURVE,
+ attribute_type,
+ BuiltinAttributeProvider::NonCreatable,
+ writable,
+ BuiltinAttributeProvider::NonDeletable),
+ as_read_attribute_(as_read_attribute),
+ as_write_attribute_(as_write_attribute)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+ return as_read_attribute_(*curve);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ {
+ if (writable_ != Writable) {
+ return {};
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+ return as_write_attribute_(*curve);
+ }
+
+ 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
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
+ }
+};
+
+static int get_spline_resolution(const SplinePtr &spline)
+{
+ if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) {
+ return bezier_spline->resolution();
+ }
+ if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) {
+ return nurb_spline->resolution();
+ }
+ return 1;
+}
+
+static void set_spline_resolution(SplinePtr &spline, const int resolution)
+{
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
+ bezier_spline->set_resolution(std::max(resolution, 1));
+ }
+ if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) {
+ nurb_spline->set_resolution(std::max(resolution, 1));
+ }
+}
+
+static GVArrayPtr make_resolution_read_attribute(const CurveEval &curve)
+{
+ return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
+ curve.splines());
+}
+
+static GVMutableArrayPtr make_resolution_write_attribute(CurveEval &curve)
+{
+ return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
+ int,
+ get_spline_resolution,
+ set_spline_resolution>>(
+ curve.splines());
+}
+
+static bool get_cyclic_value(const SplinePtr &spline)
+{
+ return spline->is_cyclic();
+}
+
+static void set_cyclic_value(SplinePtr &spline, const bool value)
+{
+ if (spline->is_cyclic() != value) {
+ spline->set_cyclic(value);
+ spline->mark_cache_invalid();
+ }
+}
+
+static GVArrayPtr make_cyclic_read_attribute(const CurveEval &curve)
+{
+ return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
+ curve.splines());
+}
+
+static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
+ curve.splines());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Builtin Control Point Attributes
+ *
+ * Attributes with a value for every control point. Most of the complexity here is due to the fact
+ * that we must provide access to the attribute data as if it was a contiguous array when it is
+ * really stored separately on each spline. That will be inherently rather slow, but these virtual
+ * array implementations try to make it workable in common situations.
+ * \{ */
+
+namespace {
+struct PointIndices {
+ int spline_index;
+ int point_index;
+};
+} // namespace
+static PointIndices lookup_point_indices(Span<int> offsets, const int index)
+{
+ const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) -
+ offsets.begin() - 1;
+ const int index_in_spline = index - offsets[spline_index];
+ return {spline_index, index_in_spline};
+}
+
+template<typename T>
+static void point_attribute_materialize(Span<Span<T>> data,
+ Span<int> offsets,
+ const IndexMask mask,
+ MutableSpan<T> r_span)
+{
+ const int total_size = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : data.index_range()) {
+ const int offset = offsets[spline_index];
+ const int next_offset = offsets[spline_index + 1];
+ initialized_copy_n(data[spline_index].data(), next_offset - offset, r_span.data() + offset);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int i : r_span.index_range()) {
+ const int dst_index = mask[i];
+
+ while (offsets[spline_index] < dst_index) {
+ spline_index++;
+ }
+
+ const int index_in_spline = dst_index - offsets[spline_index];
+ r_span[dst_index] = data[spline_index][index_in_spline];
+ }
+ }
+}
+
+template<typename T>
+static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
+ Span<int> offsets,
+ const IndexMask mask,
+ MutableSpan<T> r_span)
+{
+ T *dst = r_span.data();
+ const int total_size = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ 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);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int i : r_span.index_range()) {
+ const int dst_index = mask[i];
+
+ while (offsets[spline_index] < dst_index) {
+ spline_index++;
+ }
+
+ const int index_in_spline = dst_index - offsets[spline_index];
+ new (dst + dst_index) T(data[spline_index][index_in_spline]);
+ }
+ }
+}
+
+/**
+ * Virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VArray_For_SplinePoints : public VArray<T> {
+ private:
+ const Array<Span<T>> data_;
+ Array<int> offsets_;
+
+ public:
+ VArray_For_SplinePoints(Array<Span<T>> data, Array<int> offsets)
+ : VArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return data_[indices.spline_index][indices.point_index];
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize(data_.as_span(), offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize_to_uninitialized(data_.as_span(), offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Mutable virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VMutableArray_For_SplinePoints final : public VMutableArray<T> {
+ private:
+ Array<MutableSpan<T>> data_;
+ Array<int> offsets_;
+
+ public:
+ VMutableArray_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets)
+ : VMutableArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return data_[indices.spline_index][indices.point_index];
+ }
+
+ void set_impl(const int64_t index, T value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ data_[indices.spline_index][indices.point_index] = value;
+ }
+
+ void set_all_impl(Span<T> src) final
+ {
+ for (const int spline_index : data_.index_range()) {
+ const int offset = offsets_[spline_index];
+ const int next_offsets = offsets_[spline_index + 1];
+ data_[spline_index].copy_from(src.slice(offset, next_offsets - offset));
+ }
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize_to_uninitialized(
+ {(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Virtual array implementation specifically for control point positions. This is only needed for
+ * Bezier splines, where adjusting the position also requires adjusting handle positions depending
+ * on handle types. We pay a small price for this when other spline types are mixed with Bezier.
+ *
+ * \note There is no need to check the handle type to avoid changing auto handles, since
+ * retrieving write access to the position data will mark them for recomputation anyway.
+ */
+class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
+ private:
+ MutableSpan<SplinePtr> splines_;
+ Array<int> offsets_;
+
+ public:
+ VMutableArray_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets)
+ : VMutableArray<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
+ {
+ }
+
+ float3 get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return splines_[indices.spline_index]->positions()[indices.point_index];
+ }
+
+ 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 (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+ const float3 delta = value - bezier_spline->positions()[indices.point_index];
+ bezier_spline->handle_positions_left()[indices.point_index] += delta;
+ bezier_spline->handle_positions_right()[indices.point_index] += delta;
+ bezier_spline->positions()[indices.point_index] = value;
+ }
+ else {
+ spline.positions()[indices.point_index] = value;
+ }
+ }
+
+ void set_all_impl(Span<float3> src) final
+ {
+ for (const int spline_index : splines_.index_range()) {
+ Spline &spline = *splines_[spline_index];
+ const int offset = offsets_[spline_index];
+ const int next_offset = offsets_[spline_index + 1];
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+ MutableSpan<float3> positions = bezier_spline->positions();
+ MutableSpan<float3> handle_positions_left = bezier_spline->handle_positions_left();
+ MutableSpan<float3> handle_positions_right = bezier_spline->handle_positions_right();
+ for (const int i : IndexRange(next_offset - offset)) {
+ const float3 delta = src[offset + i] - positions[i];
+ handle_positions_left[i] += delta;
+ handle_positions_right[i] += delta;
+ positions[i] = src[offset + i];
+ }
+ }
+ else {
+ spline.positions().copy_from(src.slice(offset, next_offset - offset));
+ }
+ }
+ }
+
+ /** Utility so we can pass positions to the materialize functions above. */
+ Array<Span<float3>> get_position_spans() const
+ {
+ Array<Span<float3>> spans(splines_.size());
+ for (const int i : spans.index_range()) {
+ spans[i] = splines_[i]->positions();
+ }
+ return spans;
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+ {
+ Array<Span<float3>> spans = this->get_position_spans();
+ point_attribute_materialize(spans.as_span(), offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask,
+ MutableSpan<float3> r_span) const final
+ {
+ Array<Span<float3>> spans = this->get_position_spans();
+ point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Provider for any builtin control point attribute that doesn't need
+ * special handling like access to other arrays in the spline.
+ */
+template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttributeProvider {
+ protected:
+ using GetSpan = Span<T> (*)(const Spline &spline);
+ using GetMutableSpan = MutableSpan<T> (*)(Spline &spline);
+ using UpdateOnWrite = void (*)(Spline &spline);
+ const GetSpan get_span_;
+ const GetMutableSpan get_mutable_span_;
+ const UpdateOnWrite update_on_write_;
+
+ public:
+ BuiltinPointAttributeProvider(std::string attribute_name,
+ const WritableEnum writable,
+ const GetSpan get_span,
+ const GetMutableSpan get_mutable_span,
+ const UpdateOnWrite update_on_write)
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ ATTR_DOMAIN_POINT,
+ bke::cpp_type_to_custom_data_type(CPPType::get<T>()),
+ BuiltinAttributeProvider::NonCreatable,
+ writable,
+ BuiltinAttributeProvider::NonDeletable),
+ get_span_(get_span),
+ get_mutable_span_(get_mutable_span),
+ update_on_write_(update_on_write)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const override
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ if (splines.size() == 1) {
+ return std::make_unique<fn::GVArray_For_GSpan>(get_span_(*splines.first()));
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ Array<Span<T>> spans(splines.size());
+ for (const int i : splines.index_range()) {
+ spans[i] = get_span_(*splines[i]);
+ }
+
+ return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+ if (splines.size() == 1) {
+ return std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
+ get_mutable_span_(*splines.first()));
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ Array<MutableSpan<T>> spans(splines.size());
+ for (const int i : splines.index_range()) {
+ spans[i] = get_mutable_span_(*splines[i]);
+ if (update_on_write_) {
+ update_on_write_(*splines[i]);
+ }
+ }
+
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+ }
+
+ 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
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
+ }
+};
+
+/**
+ * Special attribute provider for the position attribute. Keeping this separate means we don't
+ * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the
+ * positions is more clear.
+ */
+class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> {
+ public:
+ PositionAttributeProvider()
+ : BuiltinPointAttributeProvider(
+ "position",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.positions(); },
+ [](Spline &spline) { return spline.positions(); },
+ [](Spline &spline) { spline.mark_cache_invalid(); })
+ {
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ bool curve_has_bezier_spline = false;
+ for (SplinePtr &spline : curve->splines()) {
+ if (spline->type() == Spline::Type::Bezier) {
+ curve_has_bezier_spline = true;
+ break;
+ }
+ }
+
+ /* Use the regular position virtual array when there aren't any Bezier splines
+ * to avoid the overhead of thecking the spline type for every point. */
+ if (!curve_has_bezier_spline) {
+ return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
+ }
+
+ /* Changing the positions requires recalculation of cached evaluated data in many cases.
+ * This could set more specific flags in the future to avoid unnecessary recomputation. */
+ for (SplinePtr &spline : curve->splines()) {
+ spline->mark_cache_invalid();
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_SplinePosition>>(
+ offsets.last(), curve->splines(), std::move(offsets));
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Provider Declaration
+ * \{ */
+
+/**
+ * In this function all the attribute providers for a curve component are created.
+ * Most data in this function is statically allocated, because it does not change over time.
+ */
+static ComponentAttributeProviders create_attribute_providers_for_curve()
+{
+ static BuiltinSplineAttributeProvider resolution("resolution",
+ CD_PROP_INT32,
+ BuiltinAttributeProvider::Writable,
+ make_resolution_read_attribute,
+ make_resolution_write_attribute);
+
+ static BuiltinSplineAttributeProvider cyclic("cyclic",
+ CD_PROP_BOOL,
+ BuiltinAttributeProvider::Writable,
+ make_cyclic_read_attribute,
+ make_cyclic_write_attribute);
+
+ static PositionAttributeProvider position;
+
+ static BuiltinPointAttributeProvider<float> radius(
+ "radius",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.radii(); },
+ [](Spline &spline) { return spline.radii(); },
+ nullptr);
+
+ static BuiltinPointAttributeProvider<float> tilt(
+ "tilt",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.tilts(); },
+ [](Spline &spline) { return spline.tilts(); },
+ [](Spline &spline) { spline.mark_cache_invalid(); });
+
+ return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic}, {});
+}
+
+} // namespace blender::bke
+
+const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
+{
+ static blender::bke::ComponentAttributeProviders providers =
+ blender::bke::create_attribute_providers_for_curve();
+ return &providers;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
index feb30e8575a..3b1b7456162 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -42,72 +42,104 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_
GeometryComponent *InstancesComponent::copy() const
{
InstancesComponent *new_component = new InstancesComponent();
- new_component->transforms_ = transforms_;
- new_component->instanced_data_ = instanced_data_;
- new_component->ids_ = ids_;
+ new_component->instance_reference_handles_ = instance_reference_handles_;
+ new_component->instance_transforms_ = instance_transforms_;
+ new_component->instance_ids_ = instance_ids_;
+ new_component->references_ = references_;
return new_component;
}
+void InstancesComponent::reserve(int min_capacity)
+{
+ instance_reference_handles_.reserve(min_capacity);
+ instance_transforms_.reserve(min_capacity);
+ instance_ids_.reserve(min_capacity);
+}
+
+/**
+ * Resize the transform, handles, and ID vectors to the specified capacity.
+ *
+ * \note This function should be used carefully, only when it's guaranteed
+ * that the data will be filled.
+ */
+void InstancesComponent::resize(int capacity)
+{
+ instance_reference_handles_.resize(capacity);
+ instance_transforms_.resize(capacity);
+ instance_ids_.resize(capacity);
+}
+
void InstancesComponent::clear()
{
- instanced_data_.clear();
- transforms_.clear();
- ids_.clear();
+ instance_reference_handles_.clear();
+ instance_transforms_.clear();
+ instance_ids_.clear();
+
+ references_.clear();
}
-void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
+void InstancesComponent::add_instance(const int instance_handle,
+ const float4x4 &transform,
+ const int id)
{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_OBJECT;
- data.data.object = object;
- this->add_instance(data, transform, id);
+ BLI_assert(instance_handle >= 0);
+ BLI_assert(instance_handle < references_.size());
+ instance_reference_handles_.append(instance_handle);
+ instance_transforms_.append(transform);
+ instance_ids_.append(id);
}
-void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id)
+blender::Span<int> InstancesComponent::instance_reference_handles() const
{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_COLLECTION;
- data.data.collection = collection;
- this->add_instance(data, transform, id);
+ return instance_reference_handles_;
}
-void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id)
+blender::MutableSpan<int> InstancesComponent::instance_reference_handles()
{
- instanced_data_.append(data);
- transforms_.append(transform);
- ids_.append(id);
+ return instance_reference_handles_;
}
-Span<InstancedData> InstancesComponent::instanced_data() const
+blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms()
+{
+ return instance_transforms_;
+}
+blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const
{
- return instanced_data_;
+ return instance_transforms_;
}
-Span<float4x4> InstancesComponent::transforms() const
+blender::MutableSpan<int> InstancesComponent::instance_ids()
{
- return transforms_;
+ return instance_ids_;
+}
+blender::Span<int> InstancesComponent::instance_ids() const
+{
+ return instance_ids_;
}
-Span<int> InstancesComponent::ids() const
+/**
+ * Returns a handle for the given reference.
+ * If the reference exists already, the handle of the existing reference is returned.
+ * Otherwise a new handle is added.
+ */
+int InstancesComponent::add_reference(InstanceReference reference)
{
- return ids_;
+ return references_.index_of_or_add_as(reference);
}
-MutableSpan<float4x4> InstancesComponent::transforms()
+blender::Span<InstanceReference> InstancesComponent::references() const
{
- return transforms_;
+ return references_;
}
int InstancesComponent::instances_amount() const
{
- const int size = instanced_data_.size();
- BLI_assert(transforms_.size() == size);
- return size;
+ return instance_transforms_.size();
}
bool InstancesComponent::is_empty() const
{
- return transforms_.size() == 0;
+ return this->instance_reference_handles_.size() == 0;
}
bool InstancesComponent::owns_direct_data() const
@@ -178,8 +210,8 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
blender::Span<int> InstancesComponent::almost_unique_ids() const
{
std::lock_guard lock(almost_unique_ids_mutex_);
- if (almost_unique_ids_.size() != ids_.size()) {
- almost_unique_ids_ = generate_unique_instance_ids(ids_);
+ if (almost_unique_ids_.size() != instance_ids_.size()) {
+ almost_unique_ids_ = generate_unique_instance_ids(instance_ids_);
}
return almost_unique_ids_;
}
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index 5697fb2ccde..2ecd0e6bd85 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -32,7 +32,7 @@
/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
-using blender::bke::ReadAttributePtr;
+using blender::fn::GVArray;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
@@ -201,14 +201,14 @@ namespace blender::bke {
template<typename T>
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int loop_index : IndexRange(mesh.totloop)) {
- const T value = attribute[loop_index];
+ const T value = old_values[loop_index];
const MLoop &loop = mesh.mloop[loop_index];
const int point_index = loop.v;
mixer.mix_in(point_index, value);
@@ -216,43 +216,40 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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>>) {
/* We compute all interpolated values at once, because for this interpolation, one has to
* iterate over all loops anyway. */
Array<T> values(mesh.totvert);
- adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const int vertex_index = mesh.mloop[loop_index].v;
- r_values[loop_index] = attribute[vertex_index];
+ r_values[loop_index] = old_values[vertex_index];
}
}
-static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
/* It is not strictly necessary to compute the value for all corners here. Instead one could
@@ -260,11 +257,10 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
* when an algorithm only accesses very few of the corner values. However, for the algorithms
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
Array<T> values(mesh.totloop);
- adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
- std::move(values));
+ adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -274,7 +270,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -291,26 +287,24 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -332,26 +326,24 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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.totedge);
- adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -370,26 +362,24 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -401,26 +391,24 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
}
}
-static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -437,21 +425,19 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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.totedge);
- adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -461,7 +447,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -478,21 +464,19 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -502,7 +486,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -517,26 +501,24 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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.totedge);
- adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -558,26 +540,24 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -593,21 +573,19 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -617,7 +595,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -634,87 +612,86 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
+ GVArrayPtr new_varray;
+ const CustomDataType data_type = cpp_type_to_custom_data_type(varray->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_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
} // namespace blender::bke
-ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
- const AttributeDomain new_domain) const
+blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
+ blender::fn::GVArrayPtr varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
- if (!attribute) {
+ if (!varray) {
return {};
}
- if (attribute->size() == 0) {
+ if (varray->size() == 0) {
return {};
}
- const AttributeDomain old_domain = attribute->domain();
- if (old_domain == new_domain) {
- return attribute;
+ if (from_domain == to_domain) {
+ return varray;
}
- switch (old_domain) {
+ switch (from_domain) {
case ATTR_DOMAIN_CORNER: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_POINT: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_FACE: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray));
default:
break;
}
@@ -743,25 +720,21 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
namespace blender::bke {
-template<typename StructT,
- typename ElemT,
- ElemT (*GetFunc)(const StructT &),
- AttributeDomain Domain>
-static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size)
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size)
{
- return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>(
- Domain, Span<StructT>((const StructT *)data, domain_size));
+ return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
+ Span<StructT>((const StructT *)data, domain_size));
}
template<typename StructT,
typename ElemT,
ElemT (*GetFunc)(const StructT &),
- void (*SetFunc)(StructT &, const ElemT &),
- AttributeDomain Domain>
-static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size)
+ void (*SetFunc)(StructT &, ElemT)>
+static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size)
{
- return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>(
- Domain, MutableSpan<StructT>((StructT *)data, domain_size));
+ return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(
+ MutableSpan<StructT>((StructT *)data, domain_size));
}
static float3 get_vertex_position(const MVert &vert)
@@ -769,7 +742,7 @@ static float3 get_vertex_position(const MVert &vert)
return float3(vert.co);
}
-static void set_vertex_position(MVert &vert, const float3 &position)
+static void set_vertex_position(MVert &vert, float3 position)
{
copy_v3_v3(vert.co, position);
}
@@ -787,7 +760,7 @@ static int get_material_index(const MPoly &mpoly)
return static_cast<int>(mpoly.mat_nr);
}
-static void set_material_index(MPoly &mpoly, const int &index)
+static void set_material_index(MPoly &mpoly, int index)
{
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
}
@@ -797,7 +770,7 @@ static bool get_shade_smooth(const MPoly &mpoly)
return mpoly.flag & ME_SMOOTH;
}
-static void set_shade_smooth(MPoly &mpoly, const bool &value)
+static void set_shade_smooth(MPoly &mpoly, bool value)
{
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
}
@@ -807,7 +780,7 @@ static float2 get_loop_uv(const MLoopUV &uv)
return float2(uv.uv);
}
-static void set_loop_uv(MLoopUV &uv, const float2 &co)
+static void set_loop_uv(MLoopUV &uv, float2 co)
{
copy_v2_v2(uv.uv, co);
}
@@ -821,7 +794,7 @@ static Color4f get_loop_color(const MLoopCol &col)
return linear_color;
}
-static void set_loop_color(MLoopCol &col, const Color4f &linear_color)
+static void set_loop_color(MLoopCol &col, Color4f linear_color)
{
linearrgb_to_srgb_uchar4(&col.r, linear_color);
}
@@ -831,71 +804,62 @@ static float get_crease(const MEdge &edge)
return edge.crease / 255.0f;
}
-static void set_crease(MEdge &edge, const float &value)
+static void set_crease(MEdge &edge, float value)
{
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
}
-class VertexWeightWriteAttribute final : public WriteAttribute {
+class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
private:
MDeformVert *dverts_;
const int dvert_index_;
public:
- VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
- : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
+ VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
+ : VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
- void get_internal(const int64_t index, void *r_value) const override
+ float get_impl(const int64_t index) const override
{
- get_internal(dverts_, dvert_index_, index, r_value);
+ return get_internal(dverts_, dvert_index_, index);
}
- void set_internal(const int64_t index, const void *value) override
+ void set_impl(const int64_t index, const float value) override
{
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
- weight->weight = *reinterpret_cast<const float *>(value);
+ weight->weight = value;
}
- static void get_internal(const MDeformVert *dverts,
- const int dvert_index,
- const int64_t index,
- void *r_value)
+ static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index)
{
if (dverts == nullptr) {
- *(float *)r_value = 0.0f;
- return;
+ return 0.0f;
}
const MDeformVert &dvert = dverts[index];
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
if (weight.def_nr == dvert_index) {
- *(float *)r_value = weight.weight;
- return;
+ return weight.weight;
}
}
- *(float *)r_value = 0.0f;
+ return 0.0f;
}
};
-class VertexWeightReadAttribute final : public ReadAttribute {
+class VArray_For_VertexWeights final : public VArray<float> {
private:
const MDeformVert *dverts_;
const int dvert_index_;
public:
- VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
- : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
+ VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index)
+ : VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
- void get_internal(const int64_t index, void *r_value) const override
+ float get_impl(const int64_t index) const override
{
- VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
+ return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index);
}
};
@@ -904,8 +868,8 @@ class VertexWeightReadAttribute final : public ReadAttribute {
*/
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
public:
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
@@ -917,15 +881,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
if (mesh == nullptr || mesh->dvert == nullptr) {
static const float default_value = 0.0f;
- return std::make_unique<ConstantReadAttribute>(
- ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
+ return {std::make_unique<fn::GVArray_For_SingleValueRef>(
+ CPPType::get<float>(), mesh->totvert, &default_value),
+ ATTR_DOMAIN_POINT};
}
- return std::make_unique<VertexWeightReadAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>(
+ mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
+ ATTR_DOMAIN_POINT};
}
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
@@ -946,8 +912,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
&mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
}
- return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ return {
+ std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>(
+ mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
+ ATTR_DOMAIN_POINT};
}
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
@@ -1009,7 +978,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
@@ -1022,8 +991,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
- return std::make_unique<ArrayReadAttribute<float3>>(
- ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly));
+ return std::make_unique<fn::GVArray_For_Span<float3>>(
+ Span<float3>((const float3 *)data, mesh->totpoly));
}
Array<float3> normals(mesh->totpoly);
@@ -1032,10 +1001,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
}
- return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
+ return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
}
- WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}
@@ -1045,7 +1014,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
return false;
}
- bool try_create(GeometryComponent &UNUSED(component)) const final
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
@@ -1105,12 +1075,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
- make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>,
- make_derived_write_attribute<MVert,
- float3,
- get_vertex_position,
- set_vertex_position,
- ATTR_DOMAIN_POINT>,
+ make_derived_read_attribute<MVert, float3, get_vertex_position>,
+ make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>,
tag_normals_dirty_when_writing_position);
static NormalAttributeProvider normal;
@@ -1124,12 +1090,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
face_access,
- make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>,
- make_derived_write_attribute<MPoly,
- int,
- get_material_index,
- set_material_index,
- ATTR_DOMAIN_FACE>,
+ make_derived_read_attribute<MPoly, int, get_material_index>,
+ make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>,
nullptr);
static BuiltinCustomDataLayerProvider shade_smooth(
@@ -1141,12 +1103,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
face_access,
- make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>,
- make_derived_write_attribute<MPoly,
- bool,
- get_shade_smooth,
- set_shade_smooth,
- ATTR_DOMAIN_FACE>,
+ make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
+ make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>,
nullptr);
static BuiltinCustomDataLayerProvider crease(
@@ -1158,8 +1116,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
edge_access,
- make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>,
- make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>,
+ make_derived_read_attribute<MEdge, float, get_crease>,
+ make_derived_write_attribute<MEdge, float, get_crease, set_crease>,
nullptr);
static NamedLegacyCustomDataProvider uvs(
@@ -1167,20 +1125,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_FLOAT2,
CD_MLOOPUV,
corner_access,
- make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>,
- make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>);
+ make_derived_read_attribute<MLoopUV, float2, get_loop_uv>,
+ make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>);
static NamedLegacyCustomDataProvider vertex_colors(
ATTR_DOMAIN_CORNER,
CD_PROP_COLOR,
CD_MLOOPCOL,
corner_access,
- make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>,
- make_derived_write_attribute<MLoopCol,
- Color4f,
- get_loop_color,
- set_loop_color,
- ATTR_DOMAIN_CORNER>);
+ make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>,
+ make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>);
static VertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
index 135de14b4f7..6c4af7a6d23 100644
--- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
+++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
@@ -140,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
namespace blender::bke {
-template<typename T, AttributeDomain Domain>
-static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
+template<typename T>
+static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
{
- return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
+ return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
}
-template<typename T, AttributeDomain Domain>
-static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
+template<typename T>
+static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
{
- return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
+ return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
+ MutableSpan<T>((T *)data, domain_size));
}
/**
@@ -179,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
},
update_custom_data_pointers};
- static BuiltinCustomDataLayerProvider position(
- "position",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- CD_PROP_FLOAT3,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- point_access,
- make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
- nullptr);
- static BuiltinCustomDataLayerProvider radius(
- "radius",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT,
- CD_PROP_FLOAT,
- BuiltinAttributeProvider::Creatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::Deletable,
- point_access,
- make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
- nullptr);
+ static BuiltinCustomDataLayerProvider position("position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_array_read_attribute<float3>,
+ make_array_write_attribute<float3>,
+ nullptr);
+ static BuiltinCustomDataLayerProvider radius("radius",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<float>,
+ make_array_write_attribute<float>,
+ nullptr);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
}
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 3e457e48076..3d85118deee 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -24,6 +24,7 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DNA_collection_types.h"
@@ -60,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new InstancesComponent();
case GEO_COMPONENT_TYPE_VOLUME:
return new VolumeComponent();
+ case GEO_COMPONENT_TYPE_CURVE:
+ return new CurveComponent();
}
BLI_assert_unreachable();
return nullptr;
@@ -182,6 +185,11 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (volume != nullptr) {
BKE_volume_min_max(volume, *r_min, *r_max);
}
+ const CurveEval *curve = this->get_curve_for_read();
+ if (curve != nullptr) {
+ /* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */
+ curve->bounds_min_max(*r_min, *r_max, true);
+ }
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
@@ -252,6 +260,13 @@ const Volume *GeometrySet::get_volume_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
+/* Returns a read-only curve or null. */
+const CurveEval *GeometrySet::get_curve_for_read() const
+{
+ const CurveComponent *component = this->get_component_for_read<CurveComponent>();
+ return (component == nullptr) ? nullptr : component->get_for_read();
+}
+
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
@@ -273,6 +288,13 @@ bool GeometrySet::has_volume() const
return component != nullptr && component->has_volume();
}
+/* Returns true when the geometry set has a curve component that has a curve. */
+bool GeometrySet::has_curve() const
+{
+ const CurveComponent *component = this->get_component_for_read<CurveComponent>();
+ return component != nullptr && component->has_curve();
+}
+
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -292,6 +314,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
return geometry_set;
}
+/* Create a new geometry set that only contains the given curve. */
+GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ GeometrySet geometry_set;
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ component.replace(curve, ownership);
+ return geometry_set;
+}
+
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -299,6 +330,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
component.replace(mesh, ownership);
}
+/* Clear the existing curve and replace it with the given one. */
+void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ CurveComponent &component = this->get_component_for_write<CurveComponent>();
+ component.replace(curve, ownership);
+}
+
/* Clear the existing point cloud and replace with the given one. */
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
@@ -306,6 +344,13 @@ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipTy
pointcloud_component.replace(pointcloud, ownership);
}
+/* Clear the existing volume and replace with the given one. */
+void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership)
+{
+ VolumeComponent &volume_component = this->get_component_for_write<VolumeComponent>();
+ volume_component.replace(volume, ownership);
+}
+
/* Returns a mutable mesh or null. No ownership is transferred. */
Mesh *GeometrySet::get_mesh_for_write()
{
@@ -327,6 +372,13 @@ Volume *GeometrySet::get_volume_for_write()
return component.get_for_write();
}
+/* Returns a mutable curve or null. No ownership is transferred. */
+CurveEval *GeometrySet::get_curve_for_write()
+{
+ CurveComponent &component = this->get_component_for_write<CurveComponent>();
+ return component.get_for_write();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 07d0e520c93..b6ee0337dbf 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -19,6 +19,7 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_collection_types.h"
#include "DNA_mesh_types.h"
@@ -50,6 +51,16 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS
}
}
+static void add_curve_data_as_geometry_component(const Object &object, GeometrySet &geometry_set)
+{
+ BLI_assert(object.type == OB_CURVE);
+ if (object.data != nullptr) {
+ std::unique_ptr<CurveEval> curve = curve_eval_from_dna_curve(*(Curve *)object.data);
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ curve_component.replace(curve.release(), GeometryOwnershipType::Owned);
+ }
+}
+
/**
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
*/
@@ -73,6 +84,9 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object)
if (object.type == OB_MESH) {
add_final_mesh_as_geometry_component(object, geometry_set);
}
+ else if (object.type == OB_CURVE) {
+ add_curve_data_as_geometry_component(object, geometry_set);
+ }
/* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the
* #geometry_set_eval case above. */
@@ -135,21 +149,28 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
const InstancesComponent &instances_component =
*geometry_set.get_component_for_read<InstancesComponent>();
- Span<float4x4> transforms = instances_component.transforms();
- Span<InstancedData> instances = instances_component.instanced_data();
- for (const int i : instances.index_range()) {
- const InstancedData &data = instances[i];
+ Span<float4x4> transforms = instances_component.instance_transforms();
+ Span<int> handles = instances_component.instance_reference_handles();
+ Span<InstanceReference> references = instances_component.references();
+ for (const int i : transforms.index_range()) {
+ const InstanceReference &reference = references[handles[i]];
const float4x4 instance_transform = transform * transforms[i];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- BLI_assert(data.data.object != nullptr);
- const Object &object = *data.data.object;
- geometry_set_collect_recursive_object(object, instance_transform, r_sets);
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- BLI_assert(data.data.collection != nullptr);
- const Collection &collection = *data.data.collection;
- geometry_set_collect_recursive_collection_instance(collection, instance_transform, r_sets);
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
+ geometry_set_collect_recursive_object(object, instance_transform, r_sets);
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ geometry_set_collect_recursive_collection_instance(
+ collection, instance_transform, r_sets);
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
}
}
}
@@ -253,19 +274,24 @@ static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_se
return true;
}
- for (const InstancedData &data : instances_component->instanced_data()) {
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- BLI_assert(data.data.object != nullptr);
- const Object &object = *data.data.object;
- if (!object_instance_attribute_foreach(object, callback, limit, count)) {
- return false;
+ for (const InstanceReference &reference : instances_component->references()) {
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ const Object &object = reference.object();
+ if (!object_instance_attribute_foreach(object, callback, limit, count)) {
+ return false;
+ }
+ break;
}
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- BLI_assert(data.data.collection != nullptr);
- const Collection &collection = *data.data.collection;
- if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
- return false;
+ case InstanceReference::Type::Collection: {
+ const Collection &collection = reference.collection();
+ if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
+ return false;
+ }
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
}
}
}
@@ -449,13 +475,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
BLI_assert(cpp_type != nullptr);
- result.attribute_try_create(entry.key, domain_output, data_type_output);
- WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
- if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
- write_attribute->domain() != domain_output) {
+ result.attribute_try_create(
+ entry.key, domain_output, data_type_output, AttributeInitDefault());
+ WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
+ if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
+ write_attribute.domain != domain_output) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+
+ fn::GVMutableArray_GSpan dst_span{*write_attribute.varray};
int offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -467,11 +495,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
if (domain_size == 0) {
continue; /* Domain size is 0, so no need to increment the offset. */
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
name, domain_output, data_type_output);
if (source_attribute) {
- fn::GSpan src_span = source_attribute->get_span();
+ fn::GVArray_GSpan src_span{*source_attribute};
const void *src_buffer = src_span.data();
for (const int UNUSED(i) : set_group.transforms.index_range()) {
void *dst_buffer = dst_span[offset];
@@ -486,10 +514,32 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
}
}
- write_attribute->apply_span();
+ dst_span.save();
}
}
+static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result)
+{
+ CurveEval *new_curve = new CurveEval();
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ if (!set.has_curve()) {
+ continue;
+ }
+
+ const CurveEval &source_curve = *set.get_curve_for_read();
+ for (const SplinePtr &source_spline : source_curve.splines()) {
+ for (const float4x4 &transform : set_group.transforms) {
+ SplinePtr new_spline = source_spline->copy();
+ new_spline->transform(transform);
+ new_curve->add_spline(std::move(new_spline));
+ }
+ }
+ }
+
+ result.replace(new_curve);
+}
+
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
bool convert_points_to_vertices,
GeometrySet &result)
@@ -556,6 +606,12 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
UNUSED_VARS(set_groups, dst_component);
}
+static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
+{
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ join_curve_splines(set_groups, dst_component);
+}
+
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set)
{
if (!geometry_set.has_instances() && !geometry_set.has_pointcloud()) {
@@ -587,6 +643,7 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
join_instance_groups_mesh(set_groups, false, new_geometry_set);
join_instance_groups_pointcloud(set_groups, new_geometry_set);
join_instance_groups_volume(set_groups, new_geometry_set);
+ join_instance_groups_curve(set_groups, new_geometry_set);
return new_geometry_set;
}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 9c84d155330..421cb0ac4f1 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -653,9 +653,13 @@ bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
* \param gpd: Grease pencil data-block
* \param name: Name of the layer
* \param setactive: Set as active
+ * \param add_to_header: Used to force the layer added at header
* \return Pointer to new layer
*/
-bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
+bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd,
+ const char *name,
+ const bool setactive,
+ const bool add_to_header)
{
bGPDlayer *gpl = NULL;
bGPDlayer *gpl_active = NULL;
@@ -671,14 +675,18 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti
gpl_active = BKE_gpencil_layer_active_get(gpd);
/* Add to data-block. */
- if (gpl_active == NULL) {
- BLI_addtail(&gpd->layers, gpl);
+ if (add_to_header) {
+ BLI_addhead(&gpd->layers, gpl);
}
else {
- /* if active layer, add after that layer */
- BLI_insertlinkafter(&gpd->layers, gpl_active, gpl);
+ if (gpl_active == NULL) {
+ BLI_addtail(&gpd->layers, gpl);
+ }
+ else {
+ /* if active layer, add after that layer */
+ BLI_insertlinkafter(&gpd->layers, gpl_active, gpl);
+ }
}
-
/* annotation vs GP Object behavior is slightly different */
if (gpd->flag & GP_DATA_ANNOTATIONS) {
/* set default color of new strokes for this layer */
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 88d3e917a7a..906d0fb0792 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -515,7 +515,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
if (collection != NULL) {
gpl = BKE_gpencil_layer_named_get(gpd, collection->id.name + 2);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true);
+ gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true, false);
}
}
}
@@ -523,7 +523,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
if (gpl == NULL) {
gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index fb6500cfe1b..501ee0c2014 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -1050,7 +1050,7 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
normalize_v3(locx);
normalize_v3(locy);
- /* Calculcate last point first. */
+ /* Calculate last point first. */
const bGPDspoint *pt_last = &points[totpoints - 1];
float tmp[3];
sub_v3_v3v3(tmp, &pt_last->x, &pt0->x);
@@ -2439,7 +2439,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_fill == NULL) {
- gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true);
+ gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
@@ -2492,7 +2492,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_stroke == NULL) {
- gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true);
+ gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 54d14e33209..8341c5b6e78 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -723,7 +723,7 @@ static void lib_override_library_create_post_process(Main *bmain,
switch (GS(id_ref->name)) {
case ID_GR: {
/* Adding the object to a specific collection outside of the root overridden one is a
- * fairly bad idea (it breaks the override hierarchy concept). But htere is no other
+ * fairly bad idea (it breaks the override hierarchy concept). But there is no other
* way to do this currently (we cannot add new collections to overridden root one,
* this is not currently supported).
* Since that will be fairly annoying and noisy, only do that in case the override
@@ -878,7 +878,8 @@ bool BKE_lib_override_library_resync(Main *bmain,
ID *id_root,
Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce,
- const bool do_post_process)
+ const bool do_post_process,
+ ReportList *reports)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(!ID_IS_LINKED(id_root));
@@ -1057,6 +1058,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
/* Delete old override IDs.
* Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT.
* This improves performances anyway, so everything is fine. */
+ int user_edited_overrides_deletion_count = 0;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT) {
/* Note that this works because linked IDs are always after local ones (including overrides),
@@ -1081,6 +1083,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->tag &= ~LIB_TAG_MISSING;
CLOG_INFO(&LOG, 2, "Old override %s is being deleted", id->name);
}
+#if 0
else {
/* Otherwise, keep them, user needs to decide whether what to do with them. */
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
@@ -1088,6 +1091,17 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name);
}
+#else
+ else {
+ /* Delete them nevertheless, with fat warning, user needs to decide whether they want to
+ * save that version of the file (and accept the loss), or not. */
+ id->tag |= LIB_TAG_DOIT;
+ id->tag &= ~LIB_TAG_MISSING;
+ CLOG_WARN(
+ &LOG, "Old override %s is being deleted even though it was user-edited", id->name);
+ user_edited_overrides_deletion_count++;
+ }
+#endif
}
}
FOREACH_MAIN_ID_END;
@@ -1098,6 +1112,15 @@ bool BKE_lib_override_library_resync(Main *bmain,
*/
id_root = id_root_reference->newid;
+ if (user_edited_overrides_deletion_count > 0) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "During resync of data-block %s, %d obsolete overrides were deleted, that had "
+ "local changes defined by user",
+ id_root->name + 2,
+ user_edited_overrides_deletion_count);
+ }
+
if (do_post_process) {
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
/* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
@@ -1136,7 +1159,10 @@ bool BKE_lib_override_library_resync(Main *bmain,
* Then it will handle the resync of necessary IDs (through calls to
* #BKE_lib_override_library_resync).
*/
-void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer)
+void BKE_lib_override_library_main_resync(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ReportList *reports)
{
/* We use a specific collection to gather/store all 'orphaned' override collections and objects
* generated by re-sync-process. This avoids putting them in scene's master collection. */
@@ -1252,7 +1278,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
const bool success = BKE_lib_override_library_resync(
- bmain, scene, view_layer, id, override_resync_residual_storage, false, false);
+ bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
break;
}
@@ -1643,7 +1669,7 @@ bool BKE_lib_override_library_property_operation_operands_validate(
return true;
}
-/** Check against potential \a bmain. */
+/** Check against potential \a bmain. */
void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *reports)
{
if (id->override_library == NULL) {
@@ -1677,7 +1703,7 @@ void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *
}
}
-/** Check against potential \a bmain. */
+/** Check against potential \a bmain. */
void BKE_lib_override_library_main_validate(Main *bmain, ReportList *reports)
{
ID *id;
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index cbbe07f99d8..3281783d81a 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -56,7 +56,7 @@ typedef struct LibraryForeachIDData {
*/
ID *self_id;
- /** Flags controlling the bahaviour of the 'foreach id' looping code. */
+ /** Flags controlling the behavior of the 'foreach id' looping code. */
int flag;
/** Generic flags to be passed to all callback calls for current processed data. */
int cb_flag;
diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c
index 1550401cc9c..7a06fd4ea9d 100644
--- a/source/blender/blenkernel/intern/mball_tessellate.c
+++ b/source/blender/blenkernel/intern/mball_tessellate.c
@@ -162,7 +162,7 @@ static void make_box_from_metaelem(Box *r, const MetaElem *ml)
}
/**
- * Partitions part of mainb array [start, end) along axis s. Returns i,
+ * Partitions part of #process.mainb array [start, end) along axis s. Returns i,
* where centroids of elements in the [start, i) segment lie "on the right side" of div,
* and elements in the [i, end) segment lie "on the left"
*/
@@ -1170,8 +1170,9 @@ static void polygonize(PROCESS *process)
/**
* Iterates over ALL objects in the scene and all of its sets, including
- * making all duplis(not only metas). Copies metas to mainb array.
- * Computes bounding boxes for building BVH. */
+ * making all duplis (not only meta-elements). Copies meta-elements to #process.mainb array.
+ * Computes bounding boxes for building BVH.
+ */
static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Object *ob)
{
Scene *sce_iter = scene;
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index d013becb372..e893a3983bd 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1027,11 +1027,15 @@ static Object *object_for_curve_to_mesh_create(Object *object)
*
* Note that there are extra fields in there like bevel and path, but those are not needed during
* conversion, so they are not copied to save unnecessary allocations. */
- if (object->runtime.curve_cache != NULL) {
+ if (temp_object->runtime.curve_cache == NULL) {
temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
"CurveCache for curve types");
+ }
+
+ if (object->runtime.curve_cache != NULL) {
BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
}
+
/* Constructive modifiers will use mesh to store result. */
if (object->runtime.data_eval != NULL) {
BKE_id_copy_ex(
@@ -1057,17 +1061,25 @@ static Object *object_for_curve_to_mesh_create(Object *object)
return temp_object;
}
+/**
+ * Populate `object->runtime.curve_cache` which is then used to create the mesh.
+ */
static void curve_to_mesh_eval_ensure(Object *object)
{
- if (object->runtime.curve_cache == NULL) {
- object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
- }
Curve *curve = (Curve *)object->data;
Curve remapped_curve = *curve;
Object remapped_object = *object;
- remapped_object.runtime.bb = NULL;
+ BKE_object_runtime_reset(&remapped_object);
+
remapped_object.data = &remapped_curve;
+ if (object->runtime.curve_cache == NULL) {
+ object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+ }
+
+ /* Temporarily share the curve-cache with the temporary object, owned by `object`. */
+ remapped_object.runtime.curve_cache = object->runtime.curve_cache;
+
/* Clear all modifiers for the bevel object.
*
* This is because they can not be reliably evaluated for an original object (at least because
@@ -1078,8 +1090,8 @@ static void curve_to_mesh_eval_ensure(Object *object)
Object bevel_object = {{NULL}};
if (remapped_curve.bevobj != NULL) {
bevel_object = *remapped_curve.bevobj;
- bevel_object.runtime.bb = NULL;
BLI_listbase_clear(&bevel_object.modifiers);
+ BKE_object_runtime_reset(&bevel_object);
remapped_curve.bevobj = &bevel_object;
}
@@ -1087,8 +1099,8 @@ static void curve_to_mesh_eval_ensure(Object *object)
Object taper_object = {{NULL}};
if (remapped_curve.taperobj != NULL) {
taper_object = *remapped_curve.taperobj;
- taper_object.runtime.bb = NULL;
BLI_listbase_clear(&taper_object.modifiers);
+ BKE_object_runtime_reset(&taper_object);
remapped_curve.taperobj = &taper_object;
}
@@ -1110,12 +1122,12 @@ static void curve_to_mesh_eval_ensure(Object *object)
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
}
- MEM_SAFE_FREE(remapped_object.runtime.bb);
- MEM_SAFE_FREE(taper_object.runtime.bb);
- MEM_SAFE_FREE(bevel_object.runtime.bb);
+ /* Owned by `object` & needed by the caller to create the mesh. */
+ remapped_object.runtime.curve_cache = NULL;
- BKE_object_free_curve_cache(&bevel_object);
- BKE_object_free_curve_cache(&taper_object);
+ BKE_object_runtime_free_data(&remapped_object);
+ BKE_object_runtime_free_data(&taper_object);
+ BKE_object_runtime_free_data(&taper_object);
}
static Mesh *mesh_new_from_curve_type_object(Object *object)
@@ -1555,7 +1567,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
* check whether it is still true with Mesh */
Mesh tmp = *mesh_dst;
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
- int did_shapekeys = 0;
+ bool did_shapekeys = false;
eCDAllocType alloctype = CD_DUPLICATE;
if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
@@ -1610,7 +1622,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
}
shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
- did_shapekeys = 1;
+ did_shapekeys = true;
}
/* copy texture space */
@@ -1639,13 +1651,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
totedge);
}
if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
- /* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the
- * BKE_mesh_update_customdata_pointers() call below. */
- tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop);
- tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly);
-
- CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop);
- CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly);
+ CustomData_add_layer(&tmp.ldata,
+ CD_MLOOP,
+ CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mloop :
+ MEM_dupallocN(mesh_src->mloop),
+ tmp.totloop);
+ CustomData_add_layer(&tmp.pdata,
+ CD_MPOLY,
+ CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mpoly :
+ MEM_dupallocN(mesh_src->mpoly),
+ tmp.totpoly);
}
/* object had got displacement layer, should copy this layer to save sculpted data */
@@ -1664,9 +1681,6 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
/* yes, must be before _and_ after tessellate */
BKE_mesh_update_customdata_pointers(&tmp, false);
- /* since 2.65 caller must do! */
- // BKE_mesh_tessface_calc(&tmp);
-
CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
CustomData_free(&mesh_dst->fdata, mesh_dst->totface);
diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc
new file mode 100644
index 00000000000..91c9951ae89
--- /dev/null
+++ b/source/blender/blenkernel/intern/mesh_sample.cc
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+namespace blender::bke::mesh_surface_sample {
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+template<typename T>
+BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ const Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ const T v0 = data_in[v0_index];
+ const T v1 = data_in[v1_index];
+ const T v2 = data_in[v2_index];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+void sample_point_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_out.size() == bary_coords.size());
+ BLI_assert(data_in.size() == mesh.totvert);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_point_attribute<T>(
+ mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+template<typename T>
+BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int loop_index_0 = looptri.tri[0];
+ const int loop_index_1 = looptri.tri[1];
+ const int loop_index_2 = looptri.tri[2];
+
+ const T v0 = data_in[loop_index_0];
+ const T v1 = data_in[loop_index_1];
+ const T v2 = data_in[loop_index_2];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+void sample_corner_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_out.size() == bary_coords.size());
+ BLI_assert(data_in.size() == mesh.totloop);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_corner_attribute<T>(
+ mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+template<typename T>
+void sample_face_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : data_out.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const int poly_index = looptri.poly;
+ data_out[i] = data_in[poly_index];
+ }
+}
+
+void sample_face_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_in.size() == mesh.totpoly);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 9c2cd03dbc2..0f2f56f4f2b 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -950,7 +950,7 @@ static void movieclip_load_get_size(MovieClip *clip)
int width, height;
MovieClipUser user = {0};
- user.framenr = 1;
+ user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, 1);
BKE_movieclip_get_size(clip, &user, &width, &height);
if (width && height) {
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index 97aa6b07ab0..92e21183acb 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -254,7 +254,7 @@ NlaTrack *BKE_nlatrack_copy(Main *bmain,
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
* flags in BKE_lib_id.h
*/
-void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag)
+void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag)
{
NlaTrack *nlt, *nlt_d;
@@ -275,6 +275,54 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int fl
}
}
+/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */
+static void update_active_strip(AnimData *adt_dest,
+ NlaTrack *track_dest,
+ const AnimData *adt_source,
+ NlaTrack *track_source)
+{
+ BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips));
+
+ NlaStrip *strip_dest = track_dest->strips.first;
+ LISTBASE_FOREACH (NlaStrip *, strip_source, &track_source->strips) {
+ if (strip_source == adt_source->actstrip) {
+ adt_dest->actstrip = strip_dest;
+ }
+
+ strip_dest = strip_dest->next;
+ }
+}
+
+/* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */
+static void update_active_track(AnimData *adt_dest, const AnimData *adt_source)
+{
+ BLI_assert(BLI_listbase_count(&adt_source->nla_tracks) ==
+ BLI_listbase_count(&adt_dest->nla_tracks));
+
+ NlaTrack *track_dest = adt_dest->nla_tracks.first;
+ LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) {
+ if (track_source == adt_source->act_track) {
+ adt_dest->act_track = track_dest;
+ /* Assumption: the active strip is on the active track. */
+ update_active_strip(adt_dest, track_dest, adt_source, track_source);
+ }
+
+ track_dest = track_dest->next;
+ }
+}
+
+void BKE_nla_tracks_copy_from_adt(Main *bmain,
+ AnimData *adt_dest,
+ const AnimData *adt_source,
+ const int flag)
+{
+ adt_dest->act_track = NULL;
+ adt_dest->actstrip = NULL;
+
+ BKE_nla_tracks_copy(bmain, &adt_dest->nla_tracks, &adt_source->nla_tracks, flag);
+ update_active_track(adt_dest, adt_source);
+}
+
/* Adding ------------------------------------------- */
/* Add a NLA Track to the given AnimData
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index cf5f7163c17..a0e444186f2 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -303,6 +303,16 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -434,6 +444,12 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case SOCK_COLLECTION:
BLO_write_struct(writer, bNodeSocketValueCollection, sock->default_value);
break;
+ case SOCK_TEXTURE:
+ BLO_write_struct(writer, bNodeSocketValueTexture, sock->default_value);
+ break;
+ case SOCK_MATERIAL:
+ BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value);
+ break;
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
@@ -501,6 +517,12 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) {
BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage);
}
+ else if ((ntree->type == NTREE_GEOMETRY) && (node->type == GEO_NODE_ATTRIBUTE_CURVE_MAP)) {
+ BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_vec);
+ BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_rgb);
+ }
else if (ntree->type == NTREE_SHADER && (node->type == SH_NODE_SCRIPT)) {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
if (nss->bytecode) {
@@ -676,6 +698,18 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
BKE_curvemapping_blend_read(reader, (CurveMapping *)node->storage);
break;
}
+ case GEO_NODE_ATTRIBUTE_CURVE_MAP: {
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BLO_read_data_address(reader, &data->curve_vec);
+ if (data->curve_vec) {
+ BKE_curvemapping_blend_read(reader, data->curve_vec);
+ }
+ BLO_read_data_address(reader, &data->curve_rgb);
+ if (data->curve_rgb) {
+ BKE_curvemapping_blend_read(reader, data->curve_rgb);
+ }
+ break;
+ }
case SH_NODE_SCRIPT: {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
BLO_read_data_address(reader, &nss->bytecode);
@@ -802,6 +836,16 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
BLO_read_id_address(reader, lib, &default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -887,6 +931,16 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
BLO_expand(expander, default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1454,6 +1508,16 @@ static void socket_id_user_increment(bNodeSocket *sock)
id_us_plus((ID *)default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1493,6 +1557,20 @@ static void socket_id_user_decrement(bNodeSocket *sock)
}
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ if (default_value->value != nullptr) {
+ id_us_min(&default_value->value->id);
+ }
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ if (default_value->value != nullptr) {
+ id_us_min(&default_value->value->id);
+ }
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1636,6 +1714,10 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketGeometry";
case SOCK_COLLECTION:
return "NodeSocketCollection";
+ case SOCK_TEXTURE:
+ return "NodeSocketTexture";
+ case SOCK_MATERIAL:
+ return "NodeSocketMaterial";
}
return nullptr;
}
@@ -1707,6 +1789,10 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceGeometry";
case SOCK_COLLECTION:
return "NodeSocketInterfaceCollection";
+ case SOCK_TEXTURE:
+ return "NodeSocketInterfaceTexture";
+ case SOCK_MATERIAL:
+ return "NodeSocketInterfaceMaterial";
}
return nullptr;
}
@@ -2219,8 +2305,8 @@ bNodeLink *nodeAddLink(
ntree->update |= NTREE_UPDATE_LINKS;
}
- if(link->tosock->flag & SOCK_MULTI_INPUT){
- link->multi_input_socket_index = node_count_links(ntree,link->tosock) - 1;
+ if (link->tosock->flag & SOCK_MULTI_INPUT) {
+ link->multi_input_socket_index = node_count_links(ntree, link->tosock) - 1;
}
return link;
@@ -4241,7 +4327,7 @@ void ntreeUpdateAllUsers(Main *main, ID *id)
if (GS(id->name) == ID_NT) {
bNodeTree *ngroup = (bNodeTree *)id;
- if (ngroup->type == NTREE_GEOMETRY) {
+ if (ngroup->type == NTREE_GEOMETRY && (ngroup->update & NTREE_UPDATE_GROUP)) {
LISTBASE_FOREACH (Object *, object, &main->objects) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
@@ -4949,6 +5035,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_combine_xyz();
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_convert();
+ register_node_type_geo_attribute_curve_map();
register_node_type_geo_attribute_fill();
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
@@ -4956,11 +5043,15 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_proximity();
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_separate_xyz();
+ register_node_type_geo_attribute_transfer();
register_node_type_geo_attribute_vector_math();
+ register_node_type_geo_attribute_vector_rotate();
register_node_type_geo_attribute_remove();
register_node_type_geo_boolean();
register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
+ register_node_type_geo_curve_to_mesh();
+ register_node_type_geo_curve_resample();
register_node_type_geo_edge_split();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
@@ -4983,6 +5074,7 @@ static void registerGeometryNodes()
register_node_type_geo_sample_texture();
register_node_type_geo_subdivide();
register_node_type_geo_subdivision_surface();
+ register_node_type_geo_switch();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_volume_to_mesh();
diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc
index cc910bab6ac..7a28fd295fb 100644
--- a/source/blender/blenkernel/intern/node_ui_storage.cc
+++ b/source/blender/blenkernel/intern/node_ui_storage.cc
@@ -152,6 +152,7 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
node_error_message_log(ntree, node, message, type);
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
+ std::lock_guard lock{node_ui_storage.mutex};
node_ui_storage.warnings.append({type, std::move(message)});
}
@@ -163,6 +164,7 @@ void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const CustomDataType data_type)
{
NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
+ std::lock_guard lock{node_ui_storage.mutex};
node_ui_storage.attribute_hints.add_as(
AvailableAttributeInfo{attribute_name, domain, data_type});
}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index ef553e60eb8..1eff5c3a735 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1332,12 +1332,9 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
if (ob->type == OB_HAIR) {
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
- if (ob->type == OB_POINTCLOUD) {
+ if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) {
return (mti->modifyGeometrySet != NULL);
}
- if (ob->type == OB_VOLUME) {
- return (mti->modifyVolume != NULL);
- }
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {
return false;
@@ -4346,7 +4343,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
}
/* Speed optimization for animation lookups. */
if (ob->pose != NULL) {
- BKE_pose_channels_hash_make(ob->pose);
+ BKE_pose_channels_hash_ensure(ob->pose);
if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(ob->pose);
}
@@ -5117,6 +5114,20 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
}
/**
+ * The function frees memory used by the runtime data, but not the runtime field itself.
+ *
+ * All runtime data is cleared to ensure it's not used again,
+ * in keeping with other `_free_data(..)` functions.
+ */
+void BKE_object_runtime_free_data(Object *object)
+{
+ /* Currently this is all that's needed. */
+ BKE_object_free_derived_caches(object);
+
+ BKE_object_runtime_reset(object);
+}
+
+/**
* Find an associated armature object.
*/
static Object *obrel_armature_find(Object *ob)
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 835b62c06bf..768fa9373c1 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -842,38 +842,38 @@ static void make_duplis_instances_component(const DupliContext *ctx)
return;
}
- Span<float4x4> instance_offset_matrices = component->transforms();
+ Span<float4x4> instance_offset_matrices = component->instance_transforms();
+ Span<int> instance_reference_handles = component->instance_reference_handles();
Span<int> almost_unique_ids = component->almost_unique_ids();
- Span<InstancedData> instanced_data = component->instanced_data();
+ Span<InstanceReference> references = component->references();
- for (int i = 0; i < component->instances_amount(); i++) {
- const InstancedData &data = instanced_data[i];
+ for (int64_t i : instance_offset_matrices.index_range()) {
+ const InstanceReference &reference = references[instance_reference_handles[i]];
const int id = almost_unique_ids[i];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- Object *object = data.data.object;
- if (object != nullptr) {
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
float matrix[4][4];
mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values);
- make_dupli(ctx, object, matrix, id);
+ make_dupli(ctx, &object, matrix, id);
float space_matrix[4][4];
- mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object->imat);
+ mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat);
mul_m4_m4_pre(space_matrix, ctx->object->obmat);
- make_recursive_duplis(ctx, object, space_matrix, id);
+ make_recursive_duplis(ctx, &object, space_matrix, id);
+ break;
}
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- Collection *collection = data.data.collection;
- if (collection != nullptr) {
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
float collection_matrix[4][4];
unit_m4(collection_matrix);
- sub_v3_v3(collection_matrix[3], collection->instance_offset);
+ sub_v3_v3(collection_matrix[3], collection.instance_offset);
mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values);
mul_m4_m4_pre(collection_matrix, ctx->object->obmat);
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
- FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) {
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) {
if (object == ctx->object) {
continue;
}
@@ -885,6 +885,10 @@ static void make_duplis_instances_component(const DupliContext *ctx)
make_recursive_duplis(ctx, object, collection_matrix, id);
}
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
}
}
}
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 63bc8753fc7..948b57578dc 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -187,31 +187,31 @@ int BB_widest_axis(const BB *bb);
void pbvh_grow_nodes(PBVH *bvh, int totnode);
bool ray_face_intersection_quad(const float ray_start[3],
struct IsectRayPrecalc *isect_precalc,
- const float *t0,
- const float *t1,
- const float *t2,
- const float *t3,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
+ const float t3[3],
float *depth);
bool ray_face_intersection_tri(const float ray_start[3],
struct IsectRayPrecalc *isect_precalc,
- const float *t0,
- const float *t1,
- const float *t2,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
float *depth);
bool ray_face_nearest_quad(const float ray_start[3],
const float ray_normal[3],
- const float *t0,
- const float *t1,
- const float *t2,
- const float *t3,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
+ const float t3[3],
float *r_depth,
float *r_dist_sq);
bool ray_face_nearest_tri(const float ray_start[3],
const float ray_normal[3],
- const float *t0,
- const float *t1,
- const float *t2,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
float *r_depth,
float *r_dist_sq);
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 17434ee8023..be206f8a642 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -1805,7 +1805,7 @@ int BKE_ptcache_mem_pointers_seek(int point_index, PTCacheMem *pm, void *cur[BPH
}
for (i = 0; i < BPHYS_TOT_DATA; i++) {
- cur[i] = data_types & (1 << i) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
+ cur[i] = (data_types & (1 << i)) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
}
return 1;
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index d52e4443ac1..fcc1afbc59b 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -499,7 +499,6 @@ static void ccd_mesh_free(ccd_Mesh *ccdm)
}
MEM_freeN(ccdm->mima);
MEM_freeN(ccdm);
- ccdm = NULL;
}
}
@@ -830,13 +829,13 @@ static void calculate_collision_balls(Object *ob)
}
/* creates new softbody if didn't exist yet, makes new points and springs arrays */
-static void renew_softbody(Scene *scene, Object *ob, int totpoint, int totspring)
+static void renew_softbody(Object *ob, int totpoint, int totspring)
{
SoftBody *sb;
int i;
short softflag;
if (ob->soft == NULL) {
- ob->soft = sbNew(scene);
+ ob->soft = sbNew();
}
else {
free_softbody_intern(ob->soft);
@@ -2680,7 +2679,7 @@ static void springs_from_mesh(Object *ob)
}
/* makes totally fresh start situation */
-static void mesh_to_softbody(Scene *scene, Object *ob)
+static void mesh_to_softbody(Object *ob)
{
SoftBody *sb;
Mesh *me = ob->data;
@@ -2698,7 +2697,7 @@ static void mesh_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, me->totvert, totedge);
+ renew_softbody(ob, me->totvert, totedge);
/* we always make body points */
sb = ob->soft;
@@ -2910,7 +2909,7 @@ static void makelatticesprings(Lattice *lt, BodySpring *bs, int dostiff, Object
}
/* makes totally fresh start situation */
-static void lattice_to_softbody(Scene *scene, Object *ob)
+static void lattice_to_softbody(Object *ob)
{
Lattice *lt = ob->data;
SoftBody *sb;
@@ -2930,7 +2929,7 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, totvert, totspring);
+ renew_softbody(ob, totvert, totspring);
sb = ob->soft; /* can be created in renew_softbody() */
bp = sb->bpoint;
@@ -2973,7 +2972,7 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
}
/* makes totally fresh start situation */
-static void curve_surf_to_softbody(Scene *scene, Object *ob)
+static void curve_surf_to_softbody(Object *ob)
{
Curve *cu = ob->data;
SoftBody *sb;
@@ -2994,7 +2993,7 @@ static void curve_surf_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, totvert, totspring);
+ renew_softbody(ob, totvert, totspring);
sb = ob->soft; /* can be created in renew_softbody() */
/* set vars now */
@@ -3118,7 +3117,7 @@ static void sb_new_scratch(SoftBody *sb)
/* ************ Object level, exported functions *************** */
/* allocates and initializes general main data */
-SoftBody *sbNew(Scene *scene)
+SoftBody *sbNew(void)
{
SoftBody *sb;
@@ -3141,12 +3140,6 @@ SoftBody *sbNew(Scene *scene)
/*todo backward file compat should copy inspring to inpush while reading old files*/
sb->inpush = 0.5f;
- sb->interval = 10;
- if (scene != NULL) {
- sb->sfra = scene->r.sfra;
- sb->efra = scene->r.efra;
- }
-
sb->colball = 0.49f;
sb->balldamp = 0.50f;
sb->ballstiff = 1.0f;
@@ -3578,17 +3571,17 @@ void sbObjectStep(struct Depsgraph *depsgraph,
switch (ob->type) {
case OB_MESH:
- mesh_to_softbody(scene, ob);
+ mesh_to_softbody(ob);
break;
case OB_LATTICE:
- lattice_to_softbody(scene, ob);
+ lattice_to_softbody(ob);
break;
case OB_CURVE:
case OB_SURF:
- curve_surf_to_softbody(scene, ob);
+ curve_surf_to_softbody(ob);
break;
default:
- renew_softbody(scene, ob, numVerts, 0);
+ renew_softbody(ob, numVerts, 0);
break;
}
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
new file mode 100644
index 00000000000..11620a30948
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -0,0 +1,343 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_spline.hh"
+
+#include "FN_generic_virtual_array.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+
+Spline::Type Spline::type() const
+{
+ return type_;
+}
+
+void Spline::translate(const blender::float3 &translation)
+{
+ for (float3 &position : this->positions()) {
+ position += translation;
+ }
+ this->mark_cache_invalid();
+}
+
+void Spline::transform(const blender::float4x4 &matrix)
+{
+ for (float3 &position : this->positions()) {
+ position = matrix * position;
+ }
+ this->mark_cache_invalid();
+}
+
+int Spline::evaluated_edges_size() const
+{
+ const int eval_size = this->evaluated_points_size();
+ if (eval_size == 1) {
+ return 0;
+ }
+
+ return this->is_cyclic_ ? eval_size : eval_size - 1;
+}
+
+float Spline::length() const
+{
+ Span<float> lengths = this->evaluated_lengths();
+ return (lengths.size() == 0) ? 0 : this->evaluated_lengths().last();
+}
+
+int Spline::segments_size() const
+{
+ const int points_len = this->size();
+
+ return is_cyclic_ ? points_len : points_len - 1;
+}
+
+bool Spline::is_cyclic() const
+{
+ return is_cyclic_;
+}
+
+void Spline::set_cyclic(const bool value)
+{
+ is_cyclic_ = value;
+}
+
+static void accumulate_lengths(Span<float3> positions,
+ const bool is_cyclic,
+ MutableSpan<float> lengths)
+{
+ float length = 0.0f;
+ for (const int i : IndexRange(positions.size() - 1)) {
+ length += float3::distance(positions[i], positions[i + 1]);
+ lengths[i] = length;
+ }
+ if (is_cyclic) {
+ lengths.last() = length + float3::distance(positions.last(), positions.first());
+ }
+}
+
+/**
+ * Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
+ * length of the subsequent segment, i.e. the first value is the length of the first segment rather
+ * than 0. This calculation is rather trivial, and only depends on the evaluated positions.
+ * However, the results are used often, so it makes sense to cache it.
+ */
+Span<float> Spline::evaluated_lengths() const
+{
+ if (!length_cache_dirty_) {
+ return evaluated_lengths_cache_;
+ }
+
+ std::lock_guard lock{length_cache_mutex_};
+ if (!length_cache_dirty_) {
+ return evaluated_lengths_cache_;
+ }
+
+ const int total = evaluated_edges_size();
+ evaluated_lengths_cache_.resize(total);
+
+ Span<float3> positions = this->evaluated_positions();
+ accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
+
+ length_cache_dirty_ = false;
+ return evaluated_lengths_cache_;
+}
+
+static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
+{
+ const float3 dir_prev = (middle - prev).normalized();
+ const float3 dir_next = (next - middle).normalized();
+
+ return (dir_prev + dir_next).normalized();
+}
+
+static void calculate_tangents(Span<float3> positions,
+ const bool is_cyclic,
+ MutableSpan<float3> tangents)
+{
+ if (positions.size() == 1) {
+ return;
+ }
+
+ for (const int i : IndexRange(1, positions.size() - 2)) {
+ tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
+ }
+
+ if (is_cyclic) {
+ const float3 &second_to_last = positions[positions.size() - 2];
+ const float3 &last = positions.last();
+ const float3 &first = positions.first();
+ const float3 &second = positions[1];
+ tangents.first() = direction_bisect(last, first, second);
+ tangents.last() = direction_bisect(second_to_last, last, first);
+ }
+ else {
+ tangents.first() = (positions[1] - positions[0]).normalized();
+ tangents.last() = (positions.last() - positions[positions.size() - 2]).normalized();
+ }
+}
+
+/**
+ * Return non-owning access to the direction of the curve at each evaluated point.
+ */
+Span<float3> Spline::evaluated_tangents() const
+{
+ if (!tangent_cache_dirty_) {
+ return evaluated_tangents_cache_;
+ }
+
+ std::lock_guard lock{tangent_cache_mutex_};
+ if (!tangent_cache_dirty_) {
+ return evaluated_tangents_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_tangents_cache_.resize(eval_size);
+
+ Span<float3> positions = this->evaluated_positions();
+
+ if (eval_size == 1) {
+ evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f);
+ }
+ else {
+ calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_);
+ this->correct_end_tangents();
+ }
+
+ tangent_cache_dirty_ = false;
+ return evaluated_tangents_cache_;
+}
+
+static float3 rotate_direction_around_axis(const float3 &direction,
+ const float3 &axis,
+ const float angle)
+{
+ BLI_ASSERT_UNIT_V3(direction);
+ BLI_ASSERT_UNIT_V3(axis);
+
+ const float3 axis_scaled = axis * float3::dot(direction, axis);
+ const float3 diff = direction - axis_scaled;
+ const float3 cross = float3::cross(axis, diff);
+
+ return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
+}
+
+static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
+{
+ for (const int i : normals.index_range()) {
+ normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
+ }
+}
+
+/**
+ * Return non-owning access to the direction vectors perpendicular to the tangents at every
+ * evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
+ */
+Span<float3> Spline::evaluated_normals() const
+{
+ if (!normal_cache_dirty_) {
+ return evaluated_normals_cache_;
+ }
+
+ std::lock_guard lock{normal_cache_mutex_};
+ if (!normal_cache_dirty_) {
+ return evaluated_normals_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_normals_cache_.resize(eval_size);
+
+ Span<float3> tangents = evaluated_tangents();
+ MutableSpan<float3> normals = evaluated_normals_cache_;
+
+ /* Only Z up normals are supported at the moment. */
+ calculate_normals_z_up(tangents, normals);
+
+ /* Rotate the generated normals with the interpolated tilt data. */
+ blender::fn::GVArray_Typed<float> tilts{
+ this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(this->tilts()))};
+ for (const int i : normals.index_range()) {
+ normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
+ }
+
+ normal_cache_dirty_ = false;
+ return evaluated_normals_cache_;
+}
+
+Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
+{
+ return this->lookup_evaluated_length(this->length() * factor);
+}
+
+/**
+ * \note This does not support extrapolation currently.
+ */
+Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
+{
+ BLI_assert(length >= 0.0f && length <= this->length());
+
+ Span<float> lengths = this->evaluated_lengths();
+
+ const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
+ const int index = offset - lengths.begin();
+ const int next_index = (index == this->size() - 1) ? 0 : index + 1;
+
+ const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
+ const float factor = (length - previous_length) / (lengths[index] - previous_length);
+
+ return LookupResult{index, next_index, factor};
+}
+
+/**
+ * Return an array of evenly spaced samples along the length of the spline. The samples are indices
+ * and factors to the next index encoded in floats. The logic for converting from the float values
+ * to interpolation data is in #lookup_data_from_index_factor.
+ */
+Array<float> Spline::sample_uniform_index_factors(const int samples_size) const
+{
+ const Span<float> lengths = this->evaluated_lengths();
+
+ BLI_assert(samples_size > 0);
+ Array<float> samples(samples_size);
+
+ samples[0] = 0.0f;
+ if (samples_size == 1) {
+ return samples;
+ }
+
+ const float total_length = this->length();
+ const float sample_length = total_length / (samples_size - (is_cyclic_ ? 0 : 1));
+
+ /* Store the length at the previous evaluated point in a variable so it can
+ * start out at zero (the lengths array doesn't contain 0 for the first point). */
+ float prev_length = 0.0f;
+ int i_sample = 1;
+ for (const int i_evaluated : IndexRange(this->evaluated_edges_size())) {
+ const float length = lengths[i_evaluated];
+
+ /* Add every sample that fits in this evaluated edge. */
+ while ((sample_length * i_sample) < length && i_sample < samples_size) {
+ const float factor = (sample_length * i_sample - prev_length) / (length - prev_length);
+ samples[i_sample] = i_evaluated + factor;
+ i_sample++;
+ }
+
+ prev_length = length;
+ }
+
+ if (!is_cyclic_) {
+ /* In rare cases this can prevent overflow of the stored index. */
+ samples.last() = lengths.size();
+ }
+
+ return samples;
+}
+
+Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
+{
+ const int points_len = this->evaluated_points_size();
+
+ if (is_cyclic_) {
+ if (index_factor < points_len) {
+ const int index = std::floor(index_factor);
+ const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ return LookupResult{index, next_index, index_factor - index};
+ }
+ return LookupResult{points_len - 1, 0, 1.0f};
+ }
+
+ if (index_factor < points_len - 1) {
+ const int index = std::floor(index_factor);
+ const int next_index = index + 1;
+ return LookupResult{index, next_index, index_factor - index};
+ }
+ return LookupResult{points_len - 2, points_len - 1, 1.0f};
+}
+
+void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
+{
+ Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
+ for (const float3 &position : positions) {
+ minmax_v3v3_v3(min, max, position);
+ }
+}
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
new file mode 100644
index 00000000000..58a8f46730a
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -0,0 +1,576 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr BezierSpline::copy() const
+{
+ return std::make_unique<BezierSpline>(*this);
+}
+
+int BezierSpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == handle_types_left_.size());
+ BLI_assert(size == handle_positions_left_.size());
+ BLI_assert(size == handle_types_right_.size());
+ BLI_assert(size == handle_positions_right_.size());
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ return size;
+}
+
+int BezierSpline::resolution() const
+{
+ return resolution_;
+}
+
+void BezierSpline::set_resolution(const int value)
+{
+ BLI_assert(value > 0);
+ resolution_ = value;
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::add_point(const float3 position,
+ const HandleType handle_type_start,
+ const float3 handle_position_start,
+ const HandleType handle_type_end,
+ const float3 handle_position_end,
+ const float radius,
+ const float tilt)
+{
+ handle_types_left_.append(handle_type_start);
+ handle_positions_left_.append(handle_position_start);
+ positions_.append(position);
+ handle_types_right_.append(handle_type_end);
+ handle_positions_right_.append(handle_position_end);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::resize(const int size)
+{
+ handle_types_left_.resize(size);
+ handle_positions_left_.resize(size);
+ positions_.resize(size);
+ handle_types_right_.resize(size);
+ handle_positions_right_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ this->mark_cache_invalid();
+}
+
+MutableSpan<float3> BezierSpline::positions()
+{
+ return positions_;
+}
+Span<float3> BezierSpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> BezierSpline::radii()
+{
+ return radii_;
+}
+Span<float> BezierSpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> BezierSpline::tilts()
+{
+ return tilts_;
+}
+Span<float> BezierSpline::tilts() const
+{
+ return tilts_;
+}
+Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const
+{
+ return handle_types_left_;
+}
+MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left()
+{
+ return handle_types_left_;
+}
+Span<float3> BezierSpline::handle_positions_left() const
+{
+ this->ensure_auto_handles();
+ return handle_positions_left_;
+}
+MutableSpan<float3> BezierSpline::handle_positions_left()
+{
+ this->ensure_auto_handles();
+ return handle_positions_left_;
+}
+Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const
+{
+ return handle_types_right_;
+}
+MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right()
+{
+ return handle_types_right_;
+}
+Span<float3> BezierSpline::handle_positions_right() const
+{
+ this->ensure_auto_handles();
+ return handle_positions_right_;
+}
+MutableSpan<float3> BezierSpline::handle_positions_right()
+{
+ this->ensure_auto_handles();
+ return handle_positions_right_;
+}
+
+static float3 previous_position(Span<float3> positions, const bool cyclic, const int i)
+{
+ if (i == 0) {
+ if (cyclic) {
+ return positions[positions.size() - 1];
+ }
+ return 2.0f * positions[i] - positions[i + 1];
+ }
+ return positions[i - 1];
+}
+
+static float3 next_position(Span<float3> positions, const bool cyclic, const int i)
+{
+ if (i == positions.size() - 1) {
+ if (cyclic) {
+ return positions[0];
+ }
+ return 2.0f * positions[i] - positions[i - 1];
+ }
+ return positions[i + 1];
+}
+
+/**
+ * Recalculate all #Auto and #Vector handles with positions automatically
+ * derived from the neighboring control points.
+ */
+void BezierSpline::ensure_auto_handles() const
+{
+ if (!auto_handles_dirty_) {
+ return;
+ }
+
+ std::lock_guard lock{auto_handle_mutex_};
+ if (!auto_handles_dirty_) {
+ return;
+ }
+
+ for (const int i : IndexRange(this->size())) {
+ if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) {
+ const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i);
+ const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i];
+ float prev_len = prev_diff.length();
+ float next_len = next_diff.length();
+ if (prev_len == 0.0f) {
+ prev_len = 1.0f;
+ }
+ if (next_len == 0.0f) {
+ next_len = 1.0f;
+ }
+ const float3 dir = next_diff / next_len + prev_diff / prev_len;
+
+ /* This magic number is unfortunate, but comes from elsewhere in Blender. */
+ const float len = dir.length() * 2.5614f;
+ if (len != 0.0f) {
+ if (handle_types_left_[i] == HandleType::Auto) {
+ const float prev_len_clamped = std::min(prev_len, next_len * 5.0f);
+ handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len);
+ }
+ if (handle_types_right_[i] == HandleType::Auto) {
+ const float next_len_clamped = std::min(next_len, prev_len * 5.0f);
+ handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len);
+ }
+ }
+ }
+
+ if (handle_types_left_[i] == HandleType::Vector) {
+ const float3 prev = previous_position(positions_, is_cyclic_, i);
+ handle_positions_left_[i] = float3::interpolate(positions_[i], prev, 1.0f / 3.0f);
+ }
+
+ if (handle_types_right_[i] == HandleType::Vector) {
+ const float3 next = next_position(positions_, is_cyclic_, i);
+ handle_positions_right_[i] = float3::interpolate(positions_[i], next, 1.0f / 3.0f);
+ }
+ }
+
+ auto_handles_dirty_ = false;
+}
+
+void BezierSpline::translate(const blender::float3 &translation)
+{
+ for (float3 &position : this->positions()) {
+ position += translation;
+ }
+ for (float3 &handle_position : this->handle_positions_left()) {
+ handle_position += translation;
+ }
+ for (float3 &handle_position : this->handle_positions_right()) {
+ handle_position += translation;
+ }
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::transform(const blender::float4x4 &matrix)
+{
+ for (float3 &position : this->positions()) {
+ position = matrix * position;
+ }
+ for (float3 &handle_position : this->handle_positions_left()) {
+ handle_position = matrix * handle_position;
+ }
+ for (float3 &handle_position : this->handle_positions_right()) {
+ handle_position = matrix * handle_position;
+ }
+ this->mark_cache_invalid();
+}
+
+bool BezierSpline::point_is_sharp(const int index) const
+{
+ return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) ||
+ ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free);
+}
+
+bool BezierSpline::segment_is_vector(const int index) const
+{
+ if (index == this->size() - 1) {
+ if (is_cyclic_) {
+ return handle_types_right_.last() == HandleType::Vector &&
+ handle_types_left_.first() == HandleType::Vector;
+ }
+ /* There is actually no segment in this case, but it's nice to avoid
+ * having a special case for the last segment in calling code. */
+ return true;
+ }
+ return handle_types_right_[index] == HandleType::Vector &&
+ handle_types_left_[index + 1] == HandleType::Vector;
+}
+
+void BezierSpline::mark_cache_invalid()
+{
+ offset_cache_dirty_ = true;
+ position_cache_dirty_ = true;
+ mapping_cache_dirty_ = true;
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+ auto_handles_dirty_ = true;
+}
+
+int BezierSpline::evaluated_points_size() const
+{
+ BLI_assert(this->size() > 0);
+ return this->control_point_offsets().last();
+}
+
+/**
+ * If the spline is not cyclic, the direction for the first and last points is just the
+ * direction formed by the corresponding handles and control points. In the unlikely situation
+ * that the handles define a zero direction, fallback to using the direction defined by the
+ * first and last evaluated segments already calculated in #Spline::evaluated_tangents().
+ */
+void BezierSpline::correct_end_tangents() const
+{
+ if (is_cyclic_) {
+ return;
+ }
+
+ MutableSpan<float3> tangents(evaluated_tangents_cache_);
+
+ if (handle_positions_left_.first() != positions_.first()) {
+ tangents.first() = (positions_.first() - handle_positions_left_.first()).normalized();
+ }
+ if (handle_positions_right_.last() != positions_.last()) {
+ tangents.last() = (handle_positions_right_.last() - positions_.last()).normalized();
+ }
+}
+
+static void bezier_forward_difference_3d(const float3 &point_0,
+ const float3 &point_1,
+ const float3 &point_2,
+ const float3 &point_3,
+ MutableSpan<float3> result)
+{
+ BLI_assert(result.size() > 0);
+ const float inv_len = 1.0f / static_cast<float>(result.size());
+ const float inv_len_squared = inv_len * inv_len;
+ const float inv_len_cubed = inv_len_squared * inv_len;
+
+ const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len;
+ const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared;
+ const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed;
+
+ float3 q0 = point_0;
+ float3 q1 = rt1 + rt2 + rt3;
+ float3 q2 = 2.0f * rt2 + 6.0f * rt3;
+ float3 q3 = 6.0f * rt3;
+ for (const int i : result.index_range()) {
+ result[i] = q0;
+ q0 += q1;
+ q1 += q2;
+ q2 += q3;
+ }
+}
+
+void BezierSpline::evaluate_bezier_segment(const int index,
+ const int next_index,
+ MutableSpan<float3> positions) const
+{
+ if (this->segment_is_vector(index)) {
+ BLI_assert(positions.size() == 1);
+ positions.first() = positions_[index];
+ }
+ else {
+ bezier_forward_difference_3d(positions_[index],
+ handle_positions_right_[index],
+ handle_positions_left_[next_index],
+ positions_[next_index],
+ positions);
+ }
+}
+
+/**
+ * Returns access to a cache of offsets into the evaluated point array for each control point.
+ * While most control point edges generate the number of edges specified by the resolution, vector
+ * segments only generate one edge.
+ *
+ * \note The length of the result is one greater than the number of points, so that the last item
+ * is the total number of evaluated points. This is useful to avoid recalculating the size of the
+ * last segment everywhere.
+ */
+Span<int> BezierSpline::control_point_offsets() const
+{
+ if (!offset_cache_dirty_) {
+ return offset_cache_;
+ }
+
+ std::lock_guard lock{offset_cache_mutex_};
+ if (!offset_cache_dirty_) {
+ return offset_cache_;
+ }
+
+ const int points_len = this->size();
+ offset_cache_.resize(points_len + 1);
+
+ MutableSpan<int> offsets = offset_cache_;
+
+ int offset = 0;
+ for (const int i : IndexRange(points_len)) {
+ offsets[i] = offset;
+ offset += this->segment_is_vector(i) ? 1 : resolution_;
+ }
+ offsets.last() = offset;
+
+ offset_cache_dirty_ = false;
+ return offsets;
+}
+
+static void calculate_mappings_linear_resolution(Span<int> offsets,
+ const int size,
+ const int resolution,
+ const bool is_cyclic,
+ MutableSpan<float> r_mappings)
+{
+ const float first_segment_len_inv = 1.0f / offsets[1];
+ for (const int i : IndexRange(0, offsets[1])) {
+ r_mappings[i] = i * first_segment_len_inv;
+ }
+
+ const int grain_size = std::max(2048 / resolution, 1);
+ parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
+ for (const int i_control_point : range) {
+ const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point];
+ const float segment_len_inv = 1.0f / segment_len;
+ for (const int i : IndexRange(segment_len)) {
+ r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv;
+ }
+ }
+ });
+
+ if (is_cyclic) {
+ const int last_segment_len = offsets[size] - offsets[size - 1];
+ const float last_segment_len_inv = 1.0f / last_segment_len;
+ for (const int i : IndexRange(last_segment_len)) {
+ r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv;
+ }
+ }
+ else {
+ r_mappings.last() = size - 1;
+ }
+}
+
+/**
+ * Returns non-owning access to an array of values containing the information necessary to
+ * interpolate values from the original control points to evaluated points. The control point
+ * index is the integer part of each value, and the factor used for interpolating to the next
+ * control point is the remaining factional part.
+ */
+Span<float> BezierSpline::evaluated_mappings() const
+{
+ if (!mapping_cache_dirty_) {
+ return evaluated_mapping_cache_;
+ }
+
+ std::lock_guard lock{mapping_cache_mutex_};
+ if (!mapping_cache_dirty_) {
+ return evaluated_mapping_cache_;
+ }
+
+ const int size = this->size();
+ const int eval_size = this->evaluated_points_size();
+ evaluated_mapping_cache_.resize(eval_size);
+ MutableSpan<float> mappings = evaluated_mapping_cache_;
+
+ if (eval_size == 1) {
+ mappings.first() = 0.0f;
+ mapping_cache_dirty_ = false;
+ return mappings;
+ }
+
+ Span<int> offsets = this->control_point_offsets();
+
+ calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings);
+
+ mapping_cache_dirty_ = false;
+ return mappings;
+}
+
+Span<float3> BezierSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ this->ensure_auto_handles();
+
+ const int size = this->size();
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ MutableSpan<float3> positions = evaluated_position_cache_;
+
+ Span<int> offsets = this->control_point_offsets();
+
+ const int grain_size = std::max(512 / resolution_, 1);
+ parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) {
+ for (const int i : range) {
+ this->evaluate_bezier_segment(
+ i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
+ }
+ });
+ if (is_cyclic_) {
+ this->evaluate_bezier_segment(
+ size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1]));
+ }
+ else {
+ /* Since evaluating the bezier segment doesn't add the final point,
+ * it must be added manually in the non-cyclic case. */
+ positions.last() = positions_.last();
+ }
+
+ position_cache_dirty_ = false;
+ return positions;
+}
+
+/**
+ * Convert the data encoded in #evaulated_mappings into its parts-- the information necessary
+ * to interpolate data from control points to evaluated points between them. The next control
+ * point index result will not overflow the size of the control point vectors.
+ */
+BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor(
+ const float index_factor) const
+{
+ const int points_len = this->size();
+
+ if (is_cyclic_) {
+ if (index_factor < points_len) {
+ const int index = std::floor(index_factor);
+ const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ return InterpolationData{index, next_index, index_factor - index};
+ }
+ return InterpolationData{points_len - 1, 0, 1.0f};
+ }
+
+ if (index_factor < points_len - 1) {
+ const int index = std::floor(index_factor);
+ const int next_index = index + 1;
+ return InterpolationData{index, next_index, index_factor - index};
+ }
+ return InterpolationData{points_len - 2, points_len - 1, 1.0f};
+}
+
+/* Use a spline argument to avoid adding this to the header. */
+template<typename T>
+static void interpolate_to_evaluated_points_impl(const BezierSpline &spline,
+ const blender::VArray<T> &source_data,
+ MutableSpan<T> result_data)
+{
+ Span<float> mappings = spline.evaluated_mappings();
+
+ for (const int i : result_data.index_range()) {
+ BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor(
+ mappings[i]);
+
+ const T &value = source_data[interp.control_point_index];
+ const T &next_value = source_data[interp.next_control_point_index];
+
+ result_data[i] = blender::attribute_math::mix2(interp.factor, value, next_value);
+ }
+}
+
+blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ const int eval_size = this->evaluated_points_size();
+ if (eval_size == 1) {
+ return source_data.shallow_copy();
+ }
+
+ blender::fn::GVArrayPtr new_varray;
+ blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
+ Array<T> values(eval_size);
+ interpolate_to_evaluated_points_impl<T>(*this, source_data.typed<T>(), values);
+ new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
+ std::move(values));
+ }
+ });
+
+ return new_varray;
+}
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
new file mode 100644
index 00000000000..7816f303e2e
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -0,0 +1,426 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr NURBSpline::copy() const
+{
+ return std::make_unique<NURBSpline>(*this);
+}
+
+int NURBSpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ BLI_assert(size == weights_.size());
+ return size;
+}
+
+int NURBSpline::resolution() const
+{
+ return resolution_;
+}
+
+void NURBSpline::set_resolution(const int value)
+{
+ BLI_assert(value > 0);
+ resolution_ = value;
+ this->mark_cache_invalid();
+}
+
+uint8_t NURBSpline::order() const
+{
+ return order_;
+}
+
+void NURBSpline::set_order(const uint8_t value)
+{
+ BLI_assert(value >= 2 && value <= 6);
+ order_ = value;
+ this->mark_cache_invalid();
+}
+
+void NURBSpline::add_point(const float3 position,
+ const float radius,
+ const float tilt,
+ const float weight)
+{
+ positions_.append(position);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ weights_.append(weight);
+ knots_dirty_ = true;
+ this->mark_cache_invalid();
+}
+
+void NURBSpline::resize(const int size)
+{
+ positions_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ weights_.resize(size);
+ this->mark_cache_invalid();
+}
+
+MutableSpan<float3> NURBSpline::positions()
+{
+ return positions_;
+}
+Span<float3> NURBSpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> NURBSpline::radii()
+{
+ return radii_;
+}
+Span<float> NURBSpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> NURBSpline::tilts()
+{
+ return tilts_;
+}
+Span<float> NURBSpline::tilts() const
+{
+ return tilts_;
+}
+MutableSpan<float> NURBSpline::weights()
+{
+ return weights_;
+}
+Span<float> NURBSpline::weights() const
+{
+ return weights_;
+}
+
+void NURBSpline::mark_cache_invalid()
+{
+ basis_cache_dirty_ = true;
+ position_cache_dirty_ = true;
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+}
+
+int NURBSpline::evaluated_points_size() const
+{
+ if (!this->check_valid_size_and_order()) {
+ return 0;
+ }
+ return resolution_ * this->segments_size();
+}
+
+void NURBSpline::correct_end_tangents() const
+{
+}
+
+bool NURBSpline::check_valid_size_and_order() const
+{
+ if (this->size() < order_) {
+ return false;
+ }
+
+ if (!is_cyclic_ && this->knots_mode == KnotsMode::Bezier) {
+ if (order_ == 4) {
+ if (this->size() < 5) {
+ return false;
+ }
+ }
+ else if (order_ != 3) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int NURBSpline::knots_size() const
+{
+ const int size = this->size() + order_;
+ return is_cyclic_ ? size + order_ - 1 : size;
+}
+
+void NURBSpline::calculate_knots() const
+{
+ const KnotsMode mode = this->knots_mode;
+ const int length = this->size();
+ const int order = order_;
+
+ knots_.resize(this->knots_size());
+
+ MutableSpan<float> knots = knots_;
+
+ if (mode == NURBSpline::KnotsMode::Normal || is_cyclic_) {
+ for (const int i : knots.index_range()) {
+ knots[i] = static_cast<float>(i);
+ }
+ }
+ else if (mode == NURBSpline::KnotsMode::EndPoint) {
+ float k = 0.0f;
+ for (const int i : IndexRange(1, knots.size())) {
+ knots[i - 1] = k;
+ if (i >= order && i <= length) {
+ k += 1.0f;
+ }
+ }
+ }
+ else if (mode == NURBSpline::KnotsMode::Bezier) {
+ BLI_assert(ELEM(order, 3, 4));
+ if (order == 3) {
+ float k = 0.6f;
+ for (const int i : knots.index_range()) {
+ if (i >= order && i <= length) {
+ k += 0.5f;
+ }
+ knots[i] = std::floor(k);
+ }
+ }
+ else {
+ float k = 0.34f;
+ for (const int i : knots.index_range()) {
+ knots[i] = std::floor(k);
+ k += 1.0f / 3.0f;
+ }
+ }
+ }
+
+ if (is_cyclic_) {
+ const int b = length + order - 1;
+ if (order > 2) {
+ for (const int i : IndexRange(1, order - 2)) {
+ if (knots[b] != knots[b - i]) {
+ if (i == order - 1) {
+ knots[length + order - 2] += 1.0f;
+ break;
+ }
+ }
+ }
+ }
+
+ int c = order;
+ for (int i = b; i < this->knots_size(); i++) {
+ knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
+ c--;
+ }
+ }
+}
+
+Span<float> NURBSpline::knots() const
+{
+ if (!knots_dirty_) {
+ BLI_assert(knots_.size() == this->size() + order_);
+ return knots_;
+ }
+
+ std::lock_guard lock{knots_mutex_};
+ if (!knots_dirty_) {
+ BLI_assert(knots_.size() == this->size() + order_);
+ return knots_;
+ }
+
+ this->calculate_knots();
+
+ knots_dirty_ = false;
+
+ return knots_;
+}
+
+static void calculate_basis_for_point(const float parameter,
+ const int points_len,
+ const int order,
+ Span<float> knots,
+ MutableSpan<float> basis_buffer,
+ NURBSpline::BasisCache &basis_cache)
+{
+ /* Clamp parameter due to floating point inaccuracy. */
+ const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]);
+
+ int start = 0;
+ int end = 0;
+ for (const int i : IndexRange(points_len + order - 1)) {
+ const bool knots_equal = knots[i] == knots[i + 1];
+ if (knots_equal || t < knots[i] || t > knots[i + 1]) {
+ basis_buffer[i] = 0.0f;
+ continue;
+ }
+
+ basis_buffer[i] = 1.0f;
+ start = std::max(i - order - 1, 0);
+ end = i;
+ basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f);
+ break;
+ }
+ basis_buffer[points_len + order - 1] = 0.0f;
+
+ for (const int i_order : IndexRange(2, order - 1)) {
+ if (end + i_order >= points_len + order) {
+ end = points_len + order - 1 - i_order;
+ }
+ for (const int i : IndexRange(start, end - start + 1)) {
+ float new_basis = 0.0f;
+ if (basis_buffer[i] != 0.0f) {
+ new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]);
+ }
+
+ if (basis_buffer[i + 1] != 0.0f) {
+ new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) /
+ (knots[i + i_order] - knots[i + 1]);
+ }
+
+ basis_buffer[i] = new_basis;
+ }
+ }
+
+ /* Shrink the range of calculated values to avoid storing unnecessary zeros. */
+ while (basis_buffer[start] == 0.0f && start < end) {
+ start++;
+ }
+ while (basis_buffer[end] == 0.0f && end > start) {
+ end--;
+ }
+
+ basis_cache.weights.clear();
+ basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1));
+ basis_cache.start_index = start;
+}
+
+void NURBSpline::calculate_basis_cache() const
+{
+ if (!basis_cache_dirty_) {
+ return;
+ }
+
+ std::lock_guard lock{basis_cache_mutex_};
+ if (!basis_cache_dirty_) {
+ return;
+ }
+
+ const int points_len = this->size();
+ const int eval_size = this->evaluated_points_size();
+ BLI_assert(this->evaluated_edges_size() > 0);
+ basis_cache_.resize(eval_size);
+
+ const int order = this->order();
+ Span<float> control_weights = this->weights();
+ Span<float> knots = this->knots();
+
+ MutableSpan<BasisCache> basis_cache(basis_cache_);
+
+ /* This buffer is reused by each basis calculation to store temporary values.
+ * Theoretically it could be optimized away in the future. */
+ Array<float> basis_buffer(this->knots_size());
+
+ const float start = knots[order - 1];
+ const float end = is_cyclic_ ? knots[points_len + order - 1] : knots[points_len];
+ const float step = (end - start) / this->evaluated_edges_size();
+ float parameter = start;
+ for (const int i : IndexRange(eval_size)) {
+ BasisCache &basis = basis_cache[i];
+ calculate_basis_for_point(
+ parameter, points_len + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis);
+ BLI_assert(basis.weights.size() <= order);
+
+ for (const int j : basis.weights.index_range()) {
+ const int point_index = (basis.start_index + j) % points_len;
+ basis.weights[j] *= control_weights[point_index];
+ }
+
+ parameter += step;
+ }
+
+ basis_cache_dirty_ = false;
+}
+
+template<typename T>
+void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights,
+ const blender::VArray<T> &source_data,
+ MutableSpan<T> result_data)
+{
+ const int points_len = source_data.size();
+ BLI_assert(result_data.size() == weights.size());
+ blender::attribute_math::DefaultMixer<T> mixer(result_data);
+
+ for (const int i : result_data.index_range()) {
+ Span<float> point_weights = weights[i].weights;
+ const int start_index = weights[i].start_index;
+
+ for (const int j : point_weights.index_range()) {
+ const int point_index = (start_index + j) % points_len;
+ mixer.mix_in(i, source_data[point_index], point_weights[j]);
+ }
+ }
+
+ mixer.finalize();
+}
+
+blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ this->calculate_basis_cache();
+ Span<BasisCache> weights(basis_cache_);
+
+ blender::fn::GVArrayPtr new_varray;
+ blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
+ Array<T> values(this->evaluated_points_size());
+ interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values);
+ new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
+ std::move(values));
+ }
+ });
+
+ return new_varray;
+}
+
+Span<float3> NURBSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ blender::fn::GVArray_Typed<float3> evaluated_positions{
+ this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span<float3>(positions_))};
+
+ evaluated_positions->materialize(evaluated_position_cache_);
+
+ position_cache_dirty_ = false;
+ return evaluated_position_cache_;
+}
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
new file mode 100644
index 00000000000..ab6f4704a88
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_poly.cc
@@ -0,0 +1,113 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_span.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_spline.hh"
+
+using blender::float3;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr PolySpline::copy() const
+{
+ return std::make_unique<PolySpline>(*this);
+}
+
+int PolySpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ return size;
+}
+
+void PolySpline::add_point(const float3 position, const float radius, const float tilt)
+{
+ positions_.append(position);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ this->mark_cache_invalid();
+}
+
+void PolySpline::resize(const int size)
+{
+ positions_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ this->mark_cache_invalid();
+}
+
+MutableSpan<float3> PolySpline::positions()
+{
+ return positions_;
+}
+Span<float3> PolySpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> PolySpline::radii()
+{
+ return radii_;
+}
+Span<float> PolySpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> PolySpline::tilts()
+{
+ return tilts_;
+}
+Span<float> PolySpline::tilts() const
+{
+ return tilts_;
+}
+
+void PolySpline::mark_cache_invalid()
+{
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+}
+
+int PolySpline::evaluated_points_size() const
+{
+ return this->size();
+}
+
+void PolySpline::correct_end_tangents() const
+{
+}
+
+Span<float3> PolySpline::evaluated_positions() const
+{
+ return this->positions();
+}
+
+/**
+ * Poly spline interpolation from control points to evaluated points is a special case, since
+ * the result data is the same as the input data. This function returns a GVArray that points to
+ * the original data. Therefore the lifetime of the returned virtual array must not be longer than
+ * the source data.
+ */
+blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ return source_data.shallow_copy();
+}
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 7b03839f659..c0ce57818d1 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -39,6 +39,7 @@
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
+#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -1003,13 +1004,11 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr
#endif
}
-static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
- struct Scene *scene,
- Object *object,
- Volume *volume_input)
+static void volume_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
+ GeometrySet &geometry_set)
{
- Volume *volume = volume_input;
-
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -1030,25 +1029,10 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
- if (mti->modifyVolume) {
- /* Ensure we are not modifying the input. */
- if (volume == volume_input) {
- volume = BKE_volume_copy_for_eval(volume, true);
- }
-
- Volume *volume_next = mti->modifyVolume(md, &mectx, volume);
-
- if (volume_next && volume_next != volume) {
- /* If the modifier returned a new volume, release the old one. */
- if (volume != volume_input) {
- BKE_id_free(nullptr, volume);
- }
- volume = volume_next;
- }
+ if (mti->modifyGeometrySet) {
+ mti->modifyGeometrySet(md, &mectx, &geometry_set);
}
}
-
- return volume;
}
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
@@ -1072,6 +1056,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
}
}
+static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set)
+{
+ if (!geometry_set.has<VolumeComponent>()) {
+ return nullptr;
+ }
+ VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
+ Volume *volume = volume_component.release();
+ if (volume != nullptr) {
+ /* Add back, but only as read-only non-owning component. */
+ volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
+ }
+ else {
+ /* The component was empty, we can remove it. */
+ geometry_set.remove<VolumeComponent>();
+ }
+ return volume;
+}
+
void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
{
/* Free any evaluated data and restore original data. */
@@ -1079,11 +1081,21 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
/* Evaluate modifiers. */
Volume *volume = (Volume *)object->data;
- Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume);
+ GeometrySet geometry_set;
+ geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly);
+ volume_evaluate_modifiers(depsgraph, scene, object, geometry_set);
+
+ Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set);
+
+ /* If the geometry set did not contain a volume, we still create an empty one. */
+ if (volume_eval == nullptr) {
+ volume_eval = BKE_volume_new_for_eval(volume);
+ }
/* Assign evaluated object. */
- const bool is_owned = (volume != volume_eval);
- BKE_object_eval_assign_data(object, &volume_eval->id, is_owned);
+ const bool eval_is_owned = (volume != volume_eval);
+ BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned);
+ object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index e5550cee124..39f65d76e3c 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -56,6 +56,7 @@
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libavutil/imgutils.h>
+# include <libavutil/opt.h>
# include <libavutil/rational.h>
# include <libavutil/samplefmt.h>
# include <libswscale/swscale.h>
@@ -80,6 +81,8 @@ typedef struct FFMpegContext {
int ffmpeg_preset; /* see eFFMpegPreset */
AVFormatContext *outfile;
+ AVCodecContext *video_codec;
+ AVCodecContext *audio_codec;
AVStream *video_stream;
AVStream *audio_stream;
AVFrame *current_frame; /* Image frame in output pixel format. */
@@ -91,10 +94,6 @@ typedef struct FFMpegContext {
uint8_t *audio_input_buffer;
uint8_t *audio_deinterleave_buffer;
int audio_input_samples;
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- uint8_t *audio_output_buffer;
- int audio_outbuf_size;
-# endif
double audio_time;
bool audio_deinterleave;
int audio_sample_size;
@@ -141,33 +140,22 @@ static int request_float_audio_buffer(int codec_id)
}
# ifdef WITH_AUDASPACE
+
static int write_audio_frame(FFMpegContext *context)
{
- AVCodecContext *c = NULL;
- AVPacket pkt;
AVFrame *frame = NULL;
- int got_output = 0;
-
- c = context->audio_stream->codec;
-
- av_init_packet(&pkt);
- pkt.size = 0;
- pkt.data = NULL;
+ AVCodecContext *c = context->audio_codec;
AUD_Device_read(
context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples);
context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate;
-# ifdef FFMPEG_HAVE_ENCODE_AUDIO2
frame = av_frame_alloc();
- av_frame_unref(frame);
frame->pts = context->audio_time / av_q2d(c->time_base);
frame->nb_samples = context->audio_input_samples;
frame->format = c->sample_fmt;
frame->channels = c->channels;
-# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
frame->channel_layout = c->channel_layout;
-# endif
if (context->audio_deinterleave) {
int channel, i;
@@ -195,61 +183,49 @@ static int write_audio_frame(FFMpegContext *context)
context->audio_input_samples * c->channels * context->audio_sample_size,
1);
- if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) {
- // XXX error("Error writing audio packet");
- return -1;
- }
+ int success = 0;
- if (!got_output) {
- av_frame_free(&frame);
- return 0;
+ int ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send audio frame: %s\n", av_err2str(ret));
+ success = -1;
}
-# else
- pkt.size = avcodec_encode_audio(c,
- context->audio_output_buffer,
- context->audio_outbuf_size,
- (short *)context->audio_input_buffer);
- if (pkt.size < 0) {
- // XXX error("Error writing audio packet");
- return -1;
- }
+ AVPacket *pkt = av_packet_alloc();
- pkt.data = context->audio_output_buffer;
- got_output = 1;
-# endif
+ while (ret >= 0) {
- if (got_output) {
- if (pkt.pts != AV_NOPTS_VALUE) {
- pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base);
+ ret = avcodec_receive_packet(c, pkt);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
}
- if (pkt.dts != AV_NOPTS_VALUE) {
- pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base);
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
+ success = -1;
}
- if (pkt.duration > 0) {
- pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base);
+
+ av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base);
+ if (pkt->duration > 0) {
+ pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base);
}
- pkt.stream_index = context->audio_stream->index;
+ pkt->stream_index = context->audio_stream->index;
- pkt.flags |= AV_PKT_FLAG_KEY;
+ pkt->flags |= AV_PKT_FLAG_KEY;
- if (av_interleaved_write_frame(context->outfile, &pkt) != 0) {
- fprintf(stderr, "Error writing audio packet!\n");
- if (frame) {
- av_frame_free(&frame);
- }
- return -1;
+ int write_ret = av_interleaved_write_frame(context->outfile, pkt);
+ if (write_ret != 0) {
+ fprintf(stderr, "Error writing audio packet: %s\n", av_err2str(write_ret));
+ success = -1;
+ break;
}
-
- av_free_packet(&pkt);
}
- if (frame) {
- av_frame_free(&frame);
- }
+ av_packet_free(&pkt);
+ av_frame_free(&frame);
- return 0;
+ return success;
}
# endif /* #ifdef WITH_AUDASPACE */
@@ -265,14 +241,15 @@ static AVFrame *alloc_picture(int pix_fmt, int width, int height)
if (!f) {
return NULL;
}
- size = avpicture_get_size(pix_fmt, width, height);
+ size = av_image_get_buffer_size(pix_fmt, width, height, 1);
/* allocate the actual picture buffer */
buf = MEM_mallocN(size, "AVFrame buffer");
if (!buf) {
free(f);
return NULL;
}
- avpicture_fill((AVPicture *)f, buf, pix_fmt, width, height);
+
+ av_image_fill_arrays(f->data, f->linesize, buf, pix_fmt, width, height, 1);
f->format = pix_fmt;
f->width = width;
f->height = height;
@@ -342,58 +319,57 @@ static const char **get_file_extensions(int format)
}
/* Write a frame to the output file */
-static int write_video_frame(
- FFMpegContext *context, const RenderData *rd, int cfra, AVFrame *frame, ReportList *reports)
+static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, ReportList *reports)
{
- int got_output;
int ret, success = 1;
- AVCodecContext *c = context->video_stream->codec;
- AVPacket packet = {0};
+ AVPacket *packet = av_packet_alloc();
- av_init_packet(&packet);
+ AVCodecContext *c = context->video_codec;
frame->pts = cfra;
- ret = avcodec_encode_video2(c, &packet, frame, &got_output);
+ ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret));
+ success = -1;
+ }
- if (ret >= 0 && got_output) {
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame PTS: %d\n", (int)packet.pts);
- }
- else {
- PRINT("Video Frame PTS: not set\n");
- }
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame DTS: %d\n", (int)packet.dts);
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, packet);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets available. */
+ break;
}
- else {
- PRINT("Video Frame DTS: not set\n");
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding frame: %s\n", av_err2str(ret));
+ break;
}
- packet.stream_index = context->video_stream->index;
- ret = av_interleaved_write_frame(context->outfile, &packet);
- success = (ret == 0);
- }
- else if (ret < 0) {
- success = 0;
+ packet->stream_index = context->video_stream->index;
+ av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+ if (av_interleaved_write_frame(context->outfile, packet) != 0) {
+ success = -1;
+ break;
+ }
}
if (!success) {
BKE_report(reports, RPT_ERROR, "Error writing frame");
+ PRINT("Error writing frame: %s\n", av_err2str(ret));
}
+ av_packet_free(&packet);
+
return success;
}
/* read and encode a frame of audio from the buffer */
-static AVFrame *generate_video_frame(FFMpegContext *context,
- const uint8_t *pixels,
- ReportList *reports)
+static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels)
{
- AVCodecContext *c = context->video_stream->codec;
- int height = c->height;
+ AVCodecParameters *codec = context->video_stream->codecpar;
+ int height = codec->height;
AVFrame *rgb_frame;
if (context->img_convert_frame != NULL) {
@@ -438,7 +414,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context,
(const uint8_t *const *)rgb_frame->data,
rgb_frame->linesize,
0,
- c->height,
+ codec->height,
context->current_frame->data,
context->current_frame->linesize);
}
@@ -446,9 +422,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context,
return context->current_frame;
}
-static void set_ffmpeg_property_option(AVCodecContext *c,
- IDProperty *prop,
- AVDictionary **dictionary)
+static void set_ffmpeg_property_option(IDProperty *prop, AVDictionary **dictionary)
{
char name[128];
char *param;
@@ -536,7 +510,7 @@ static void set_ffmpeg_properties(RenderData *rd,
for (curr = prop->data.group.first; curr; curr = curr->next) {
if (ffmpeg_proprty_valid(c, prop_name, curr)) {
- set_ffmpeg_property_option(c, curr, dictionary);
+ set_ffmpeg_property_option(curr, dictionary);
}
}
}
@@ -553,7 +527,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodecContext *c;
AVCodec *codec;
AVDictionary *opts = NULL;
@@ -567,7 +540,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
/* Set up the codec context */
- c = st->codec;
+ context->video_codec = avcodec_alloc_context3(NULL);
+ AVCodecContext *c = context->video_codec;
c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_VIDEO;
@@ -650,11 +624,9 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
- /* Deprecated and not doing anything since July 2015, deleted in recent ffmpeg */
- // c->me_method = ME_EPZS;
-
codec = avcodec_find_encoder(c->codec_id);
if (!codec) {
+ avcodec_free_context(&c);
return NULL;
}
@@ -714,7 +686,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
if ((of->oformat->flags & AVFMT_GLOBALHEADER)) {
PRINT("Using global header\n");
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
/* xasp & yasp got float lately... */
@@ -742,6 +714,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
if (avcodec_open2(c, codec, &opts) < 0) {
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
av_dict_free(&opts);
+ avcodec_free_context(&c);
return NULL;
}
av_dict_free(&opts);
@@ -769,6 +742,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
NULL);
}
+ avcodec_parameters_from_context(st->codecpar, c);
+
return st;
}
@@ -780,7 +755,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodecContext *c;
AVCodec *codec;
AVDictionary *opts = NULL;
@@ -792,7 +766,8 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
}
st->id = 1;
- c = st->codec;
+ context->audio_codec = avcodec_alloc_context3(NULL);
+ AVCodecContext *c = context->audio_codec;
c->thread_count = BLI_system_thread_count();
c->thread_type = FF_THREAD_SLICE;
@@ -804,7 +779,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->channels = rd->ffcodecdata.audio_channels;
-# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
switch (rd->ffcodecdata.audio_channels) {
case FFM_CHANNELS_MONO:
c->channel_layout = AV_CH_LAYOUT_MONO;
@@ -822,7 +796,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->channel_layout = AV_CH_LAYOUT_7POINT1;
break;
}
-# endif
if (request_float_audio_buffer(codec_id)) {
/* mainly for AAC codec which is experimental */
@@ -833,6 +806,7 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
codec = avcodec_find_encoder(c->codec_id);
if (!codec) {
// XXX error("Couldn't find a valid audio codec");
+ avcodec_free_context(&c);
return NULL;
}
@@ -844,13 +818,13 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
* Float samples in particular are not always supported. */
const enum AVSampleFormat *p = codec->sample_fmts;
for (; *p != -1; p++) {
- if (*p == st->codec->sample_fmt) {
+ if (*p == c->sample_fmt) {
break;
}
}
if (*p == -1) {
/* sample format incompatible with codec. Defaulting to a format known to work */
- st->codec->sample_fmt = codec->sample_fmts[0];
+ c->sample_fmt = codec->sample_fmts[0];
}
}
@@ -859,18 +833,18 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int best = 0;
int best_dist = INT_MAX;
for (; *p; p++) {
- int dist = abs(st->codec->sample_rate - *p);
+ int dist = abs(c->sample_rate - *p);
if (dist < best_dist) {
best_dist = dist;
best = *p;
}
}
/* best is the closest supported sample rate (same as selected if best_dist == 0) */
- st->codec->sample_rate = best;
+ c->sample_rate = best;
}
if (of->oformat->flags & AVFMT_GLOBALHEADER) {
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
set_ffmpeg_properties(rd, c, "audio", &opts);
@@ -879,32 +853,25 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
// XXX error("Couldn't initialize audio codec");
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
av_dict_free(&opts);
+ avcodec_free_context(&c);
return NULL;
}
av_dict_free(&opts);
/* need to prevent floating point exception when using vorbis audio codec,
* initialize this value in the same way as it's done in FFmpeg itself (sergey) */
- st->codec->time_base.num = 1;
- st->codec->time_base.den = st->codec->sample_rate;
-
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- context->audio_outbuf_size = FF_MIN_BUFFER_SIZE;
-# endif
+ c->time_base.num = 1;
+ c->time_base.den = c->sample_rate;
if (c->frame_size == 0) {
/* Used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD))
* not sure if that is needed anymore, so let's try out if there are any
* complaints regarding some FFmpeg versions users might have. */
- context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels;
+ context->audio_input_samples = AV_INPUT_BUFFER_MIN_SIZE * 8 / c->bits_per_coded_sample /
+ c->channels;
}
else {
context->audio_input_samples = c->frame_size;
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size) {
- context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4;
- }
-# endif
}
context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt);
@@ -913,10 +880,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
context->audio_input_buffer = (uint8_t *)av_malloc(context->audio_input_samples * c->channels *
context->audio_sample_size);
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- context->audio_output_buffer = (uint8_t *)av_malloc(context->audio_outbuf_size);
-# endif
-
if (context->audio_deinterleave) {
context->audio_deinterleave_buffer = (uint8_t *)av_malloc(
context->audio_input_samples * c->channels * context->audio_sample_size);
@@ -924,6 +887,8 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
context->audio_time = 0.0f;
+ avcodec_parameters_from_context(st->codecpar, c);
+
return st;
}
/* essential functions -- start, append, end */
@@ -949,7 +914,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va
static void ffmpeg_add_metadata_callback(void *data,
const char *propname,
char *propvalue,
- int len)
+ int UNUSED(len))
{
AVDictionary **metadata = (AVDictionary **)data;
av_dict_set(metadata, propname, propvalue, 0);
@@ -1040,7 +1005,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
fmt->audio_codec = context->ffmpeg_audio_codec;
- BLI_strncpy(of->filename, name, sizeof(of->filename));
+ of->url = av_strdup(name);
/* set the codec to the user's selection */
switch (context->ffmpeg_type) {
case FFMPEG_AVI:
@@ -1105,9 +1070,11 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!context->video_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
+ PRINT("Video stream error: %s\n", error);
}
else {
BKE_report(reports, RPT_ERROR, "Error initializing video stream");
+ PRINT("Error initializing video stream");
}
goto fail;
}
@@ -1119,9 +1086,11 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!context->audio_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
+ PRINT("Audio stream error: %s\n", error);
}
else {
BKE_report(reports, RPT_ERROR, "Error initializing audio stream");
+ PRINT("Error initializing audio stream");
}
goto fail;
}
@@ -1129,6 +1098,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!(fmt->flags & AVFMT_NOFILE)) {
if (avio_open(&of->pb, name, AVIO_FLAG_WRITE) < 0) {
BKE_report(reports, RPT_ERROR, "Could not open file for writing");
+ PRINT("Could not open file for writing\n");
goto fail;
}
}
@@ -1138,10 +1108,12 @@ static int start_ffmpeg_impl(FFMpegContext *context,
&of->metadata, context->stamp_data, ffmpeg_add_metadata_callback, false);
}
- if (avformat_write_header(of, NULL) < 0) {
+ int ret = avformat_write_header(of, NULL);
+ if (ret < 0) {
BKE_report(reports,
RPT_ERROR,
"Could not initialize streams, probably unsupported codec combination");
+ PRINT("Could not write media header: %s\n", av_err2str(ret));
goto fail;
}
@@ -1156,13 +1128,11 @@ fail:
avio_close(of->pb);
}
- if (context->video_stream && context->video_stream->codec) {
- avcodec_close(context->video_stream->codec);
+ if (context->video_stream) {
context->video_stream = NULL;
}
- if (context->audio_stream && context->audio_stream->codec) {
- avcodec_close(context->audio_stream->codec);
+ if (context->audio_stream) {
context->audio_stream = NULL;
}
@@ -1190,46 +1160,36 @@ fail:
*/
static void flush_ffmpeg(FFMpegContext *context)
{
- int ret = 0;
+ AVCodecContext *c = context->video_codec;
+ AVPacket *packet = av_packet_alloc();
- AVCodecContext *c = context->video_stream->codec;
- /* get the delayed frames */
- while (1) {
- int got_output;
- AVPacket packet = {0};
- av_init_packet(&packet);
+ avcodec_send_frame(c, NULL);
- ret = avcodec_encode_video2(c, &packet, NULL, &got_output);
- if (ret < 0) {
- fprintf(stderr, "Error encoding delayed frame %d\n", ret);
+ /* Get the packets frames. */
+ int ret = 1;
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, packet);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets to flush. */
break;
}
- if (!got_output) {
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding delayed frame: %s\n", av_err2str(ret));
break;
}
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame PTS: %d\n", (int)packet.pts);
- }
- else {
- PRINT("Video Frame PTS: not set\n");
- }
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame DTS: %d\n", (int)packet.dts);
- }
- else {
- PRINT("Video Frame DTS: not set\n");
- }
- packet.stream_index = context->video_stream->index;
- ret = av_interleaved_write_frame(context->outfile, &packet);
- if (ret != 0) {
- fprintf(stderr, "Error writing delayed frame %d\n", ret);
+ packet->stream_index = context->video_stream->index;
+ av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+
+ int write_ret = av_interleaved_write_frame(context->outfile, packet);
+ if (write_ret != 0) {
+ fprintf(stderr, "Error writing delayed frame: %s\n", av_err2str(write_ret));
break;
}
}
- avcodec_flush_buffers(context->video_stream->codec);
+
+ av_packet_free(&packet);
}
/* **********************************************************************
@@ -1327,7 +1287,8 @@ int BKE_ffmpeg_start(void *context_v,
success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
# ifdef WITH_AUDASPACE
if (context->audio_stream) {
- AVCodecContext *c = context->audio_stream->codec;
+ AVCodecContext *c = context->audio_codec;
+
AUD_DeviceSpecs specs;
specs.channels = c->channels;
@@ -1354,10 +1315,6 @@ int BKE_ffmpeg_start(void *context_v,
specs.rate = rd->ffcodecdata.audio_mixrate;
context->audio_mixdown_device = BKE_sound_mixdown(
scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume);
-# ifdef FFMPEG_CODEC_TIME_BASE
- c->time_base.den = specs.rate;
- c->time_base.num = 1;
-# endif
}
# endif
return success;
@@ -1398,8 +1355,8 @@ int BKE_ffmpeg_append(void *context_v,
// write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
if (context->video_stream) {
- avframe = generate_video_frame(context, (unsigned char *)pixels, reports);
- success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports));
+ avframe = generate_video_frame(context, (unsigned char *)pixels);
+ success = (avframe && write_video_frame(context, frame - start_frame, avframe, reports));
if (context->ffmpeg_autosplit) {
if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) {
@@ -1428,9 +1385,11 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
context->audio_mixdown_device = NULL;
}
}
+# else
+ UNUSED_VARS(is_autosplit);
# endif
- if (context->video_stream && context->video_stream->codec) {
+ if (context->video_stream) {
PRINT("Flushing delayed frames...\n");
flush_ffmpeg(context);
}
@@ -1441,14 +1400,12 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
/* Close the video codec */
- if (context->video_stream != NULL && context->video_stream->codec != NULL) {
- avcodec_close(context->video_stream->codec);
+ if (context->video_stream != NULL) {
PRINT("zero video stream %p\n", context->video_stream);
context->video_stream = NULL;
}
- if (context->audio_stream != NULL && context->audio_stream->codec != NULL) {
- avcodec_close(context->audio_stream->codec);
+ if (context->audio_stream != NULL) {
context->audio_stream = NULL;
}
@@ -1467,6 +1424,16 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
avio_close(context->outfile->pb);
}
}
+
+ if (context->video_codec != NULL) {
+ avcodec_free_context(&context->video_codec);
+ context->video_codec = NULL;
+ }
+ if (context->audio_codec != NULL) {
+ avcodec_free_context(&context->audio_codec);
+ context->audio_codec = NULL;
+ }
+
if (context->outfile != NULL) {
avformat_free_context(context->outfile);
context->outfile = NULL;
@@ -1475,12 +1442,6 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
av_free(context->audio_input_buffer);
context->audio_input_buffer = NULL;
}
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (context->audio_output_buffer != NULL) {
- av_free(context->audio_output_buffer);
- context->audio_output_buffer = NULL;
- }
-# endif
if (context->audio_deinterleave_buffer != NULL) {
av_free(context->audio_deinterleave_buffer);
@@ -1560,12 +1521,12 @@ static IDProperty *BKE_ffmpeg_property_add(RenderData *rd,
switch (o->type) {
case AV_OPT_TYPE_INT:
case AV_OPT_TYPE_INT64:
- val.i = FFMPEG_DEF_OPT_VAL_INT(o);
+ val.i = o->default_val.i64;
idp_type = IDP_INT;
break;
case AV_OPT_TYPE_DOUBLE:
case AV_OPT_TYPE_FLOAT:
- val.f = FFMPEG_DEF_OPT_VAL_DOUBLE(o);
+ val.f = o->default_val.dbl;
idp_type = IDP_FLOAT;
break;
case AV_OPT_TYPE_STRING:
@@ -1707,16 +1668,9 @@ static void ffmpeg_set_expert_options(RenderData *rd)
BKE_ffmpeg_property_add_string(rd, "video", "trellis:0");
BKE_ffmpeg_property_add_string(rd, "video", "weightb:1");
-# ifdef FFMPEG_HAVE_DEPRECATED_FLAGS2
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:dct8x8");
- BKE_ffmpeg_property_add_string(rd, "video", "directpred:3");
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:fastpskip");
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:wpred");
-# else
BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1");
BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1");
BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2");
-# endif
}
else if (codec_id == AV_CODEC_ID_DNXHD) {
if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) {
@@ -1871,14 +1825,12 @@ bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd)
{
int codec = rd->ffcodecdata.codec;
-# ifdef FFMPEG_FFV1_ALPHA_SUPPORTED
- /* Visual Studio 2019 doesn't like #ifdef within ELEM(). */
- if (codec == AV_CODEC_ID_FFV1) {
- return true;
- }
-# endif
-
- return ELEM(codec, AV_CODEC_ID_QTRLE, AV_CODEC_ID_PNG, AV_CODEC_ID_VP9, AV_CODEC_ID_HUFFYUV);
+ return ELEM(codec,
+ AV_CODEC_ID_FFV1,
+ AV_CODEC_ID_QTRLE,
+ AV_CODEC_ID_PNG,
+ AV_CODEC_ID_VP9,
+ AV_CODEC_ID_HUFFYUV);
}
void *BKE_ffmpeg_context_create(void)
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index 706bcac4f17..71b5a74ddf7 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -82,6 +82,10 @@ typedef struct NlaEvalChannelSnapshot {
/** For an upper snapshot channel, marks values that should be blended. */
NlaValidMask blend_domain;
+ /** Only used for keyframe remapping. Any values not in the \a remap_domain will not be used
+ * for keyframe remapping. */
+ NlaValidMask remap_domain;
+
int length; /* Number of values in the property. */
bool is_base; /* Base snapshot of the channel. */
@@ -196,6 +200,13 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
const float upper_influence,
NlaEvalSnapshot *r_blended_snapshot);
+void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *lower_snapshot,
+ NlaEvalSnapshot *blended_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_upper_snapshot);
+
#ifdef __cplusplus
}
#endif