diff options
Diffstat (limited to 'source')
512 files changed, 20592 insertions, 7782 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 68ae6963a3e..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; } @@ -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 825f660fa3a..109037bc95a 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 diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h index 680c4bc78da..4b5a7d671f2 100644 --- a/source/blender/blenlib/BLI_compiler_attrs.h +++ b/source/blender/blenlib/BLI_compiler_attrs.h @@ -98,3 +98,10 @@ #else # define ATTR_ALIGN(x) __attribute__((aligned(x))) #endif + +/* Alignment directive */ +#ifdef _WIN64 +# define ALIGN_STRUCT __declspec(align(64)) +#else +# define ALIGN_STRUCT +#endif diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh new file mode 100644 index 00000000000..89be4cad848 --- /dev/null +++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh @@ -0,0 +1,73 @@ +/* + * 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 + +#ifdef WITH_TBB +# include <tbb/enumerable_thread_specific.h> +#endif + +#include <atomic> +#include <mutex> + +#include "BLI_map.hh" +#include "BLI_utility_mixins.hh" + +namespace blender { + +namespace enumerable_thread_specific_utils { +inline std::atomic<int> next_id = 0; +inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed); +} // namespace enumerable_thread_specific_utils + +/** + * This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we + * want to be able to build without tbb. + * + * More features of the tbb version can be wrapped when they are used. + */ +template<typename T> class EnumerableThreadSpecific : NonCopyable, NonMovable { +#ifdef WITH_TBB + + private: + tbb::enumerable_thread_specific<T> values_; + + public: + T &local() + { + return values_.local(); + } + +#else /* WITH_TBB */ + + private: + std::mutex mutex_; + /* Maps thread ids to their corresponding values. The values are not embedded in the map, so that + * their addresses do not change when the map grows. */ + Map<int, std::unique_ptr<T>> values_; + + public: + T &local() + { + const int thread_id = enumerable_thread_specific_utils::thread_id; + std::lock_guard lock{mutex_}; + return *values_.lookup_or_add_cb(thread_id, []() { return std::make_unique<T>(); }); + } + +#endif /* WITH_TBB */ +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index cbc4d4ed366..04aae375889 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -245,6 +245,13 @@ struct float3 { return result; } + static float3 cross(const float3 &a, const float3 &b) + { + float3 result; + cross_v3_v3v3(result, a, b); + return result; + } + static float3 project(const float3 &a, const float3 &b) { float3 result; diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index f5ba91bc12c..396b0b1bd21 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -45,6 +45,37 @@ struct float4x4 { return mat; } + static float4x4 from_normalized_axis_data(const float3 location, + const float3 forward, + const float3 up) + { + BLI_ASSERT_UNIT_V3(forward); + BLI_ASSERT_UNIT_V3(up); + float4x4 matrix; + const float3 cross = float3::cross(forward, up); + matrix.values[0][0] = forward.x; + matrix.values[1][0] = cross.x; + matrix.values[2][0] = up.x; + matrix.values[3][0] = location.x; + + matrix.values[0][1] = forward.y; + matrix.values[1][1] = cross.y; + matrix.values[2][1] = up.y; + matrix.values[3][1] = location.y; + + matrix.values[0][2] = forward.z; + matrix.values[1][2] = cross.z; + matrix.values[2][2] = up.z; + matrix.values[3][2] = location.z; + + matrix.values[0][3] = 0.0f; + matrix.values[1][3] = 0.0f; + matrix.values[2][3] = 0.0f; + matrix.values[3][3] = 1.0f; + + return matrix; + } + static float4x4 identity() { float4x4 mat; @@ -116,6 +147,19 @@ struct float4x4 { return scale; } + void apply_scale(const float scale) + { + values[0][0] *= scale; + values[0][1] *= scale; + values[0][2] *= scale; + values[1][0] *= scale; + values[1][1] *= scale; + values[1][2] *= scale; + values[2][0] *= scale; + values[2][1] *= scale; + values[2][2] *= scale; + } + float4x4 inverted() const { float4x4 result; diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh index 4022c2baa1f..fbed321534c 100644 --- a/source/blender/blenlib/BLI_hash.hh +++ b/source/blender/blenlib/BLI_hash.hh @@ -85,9 +85,12 @@ namespace blender { /** - * If there is no other specialization of #DefaultHash for a given type, try to call `hash()` on - * the value. If there is no such method, this will result in a compiler error. Usually that means - * that you have to implement a hash function using one of three strategies listed above. + * If there is no other specialization of #DefaultHash for a given type, look for a hash function + * on the type itself. Implementing a `hash()` method on a type is often significantly easier than + * specializing #DefaultHash. + * + * To support heterogeneous lookup, a type can also implement a static `hash_as(const OtherType &)` + * function. * * In the case of an enum type, the default hash is just to cast the enum value to an integer. */ @@ -95,12 +98,25 @@ template<typename T> struct DefaultHash { uint64_t operator()(const T &value) const { if constexpr (std::is_enum_v<T>) { + /* For enums use the value as hash directly. */ return (uint64_t)value; } else { + /* Try to call the `hash()` function on the value. */ + /* If this results in a compiler error, no hash function for the type has been found. */ return value.hash(); } } + + template<typename U> uint64_t operator()(const U &value) const + { + /* Try calling the static `T::hash_as(value)` function with the given value. The returned hash + * should be "compatible" with `T::hash()`. Usually that means that if `value` is converted to + * `T` its hash does not change. */ + /* If this results in a compiler error, no hash function for the heterogeneous lookup has been + * found. */ + return T::hash_as(value); + } }; /** diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh index 6aa97d5c5e7..7de6bcfdd98 100644 --- a/source/blender/blenlib/BLI_linear_allocator.hh +++ b/source/blender/blenlib/BLI_linear_allocator.hh @@ -129,6 +129,21 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya } /** + * Construct multiple instances of a type in an array. The constructor of is called with the + * given arguments. The caller is responsible for calling the destructor (and not `delete`) on + * the constructed elements. + */ + template<typename T, typename... Args> + MutableSpan<T> construct_array(int64_t size, Args &&... args) + { + MutableSpan<T> array = this->allocate_array<T>(size); + for (const int64_t i : IndexRange(size)) { + new (&array[i]) T(std::forward<Args>(args)...); + } + return array; + } + + /** * Copy the given array into a memory buffer provided by this allocator. */ template<typename T> MutableSpan<T> construct_array_copy(Span<T> src) diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 9fa69853e44..4d254960f34 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -247,11 +247,11 @@ class Map { { this->add_new_as(std::move(key), std::move(value)); } - template<typename ForwardKey, typename ForwardValue> - void add_new_as(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + void add_new_as(ForwardKey &&key, ForwardValue &&... value) { this->add_new__impl( - std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key)); + std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...); } /** @@ -277,11 +277,11 @@ class Map { { return this->add_as(std::move(key), std::move(value)); } - template<typename ForwardKey, typename ForwardValue> - bool add_as(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + bool add_as(ForwardKey &&key, ForwardValue &&... value) { return this->add__impl( - std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key)); + std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...); } /** @@ -307,11 +307,11 @@ class Map { { return this->add_overwrite_as(std::move(key), std::move(value)); } - template<typename ForwardKey, typename ForwardValue> - bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + bool add_overwrite_as(ForwardKey &&key, ForwardValue &&... value) { return this->add_overwrite__impl( - std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key)); + std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...); } /** @@ -413,12 +413,12 @@ class Map { { return this->pop_default_as(key, std::move(default_value)); } - template<typename ForwardKey, typename ForwardValue> - Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value) + template<typename ForwardKey, typename... ForwardValue> + Value pop_default_as(const ForwardKey &key, ForwardValue &&... default_value) { Slot *slot = this->lookup_slot_ptr(key, hash_(key)); if (slot == nullptr) { - return std::forward<ForwardValue>(default_value); + return Value(std::forward<ForwardValue>(default_value)...); } Value value = std::move(*slot->value()); slot->remove(); @@ -525,15 +525,15 @@ class Map { { return this->lookup_default_as(key, default_value); } - template<typename ForwardKey, typename ForwardValue> - Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const + template<typename ForwardKey, typename... ForwardValue> + Value lookup_default_as(const ForwardKey &key, ForwardValue &&... default_value) const { const Value *ptr = this->lookup_ptr_as(key); if (ptr != nullptr) { return *ptr; } else { - return std::forward<ForwardValue>(default_value); + return Value(std::forward<ForwardValue>(default_value)...); } } @@ -557,11 +557,11 @@ class Map { { return this->lookup_or_add_as(std::move(key), std::move(value)); } - template<typename ForwardKey, typename ForwardValue> - Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&... value) { return this->lookup_or_add__impl( - std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key)); + std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...); } /** @@ -605,6 +605,37 @@ class Map { } /** + * Returns the key that is stored in the set that compares equal to the given key. This invokes + * undefined behavior when the key is not in the map. + */ + const Key &lookup_key(const Key &key) const + { + return this->lookup_key_as(key); + } + template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const + { + const Slot &slot = this->lookup_slot(key, hash_(key)); + return *slot.key(); + } + + /** + * Returns a pointer to the key that is stored in the map that compares equal to the given key. + * If the key is not in the map, null is returned. + */ + const Key *lookup_key_ptr(const Key &key) const + { + return this->lookup_key_ptr_as(key); + } + template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const + { + const Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + if (slot == nullptr) { + return nullptr; + } + return slot->key(); + } + + /** * Calls the provided callback for every key-value-pair in the map. The callback is expected * to take a `const Key &` as first and a `const Value &` as second parameter. */ @@ -621,19 +652,23 @@ class Map { } } - /** - * A utility iterator that reduces the amount of code when implementing the actual iterators. - * This uses the "curiously recurring template pattern" (CRTP). - */ - template<typename SubIterator> struct BaseIterator { + /* Common base class for all iterators below. */ + struct BaseIterator { + public: using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; + protected: + /* We could have separate base iterators for const and non-const iterators, but that would add + * more complexity than benefits right now. */ Slot *slots_; int64_t total_slots_; int64_t current_slot_; - BaseIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) + friend Map; + + public: + BaseIterator(const Slot *slots, const int64_t total_slots, const int64_t current_slot) : slots_(const_cast<Slot *>(slots)), total_slots_(total_slots), current_slot_(current_slot) { } @@ -667,11 +702,29 @@ class Map { return !(a != b); } + protected: + Slot ¤t_slot() const + { + return slots_[current_slot_]; + } + }; + + /** + * A utility iterator that reduces the amount of code when implementing the actual iterators. + * This uses the "curiously recurring template pattern" (CRTP). + */ + template<typename SubIterator> class BaseIteratorRange : public BaseIterator { + public: + BaseIteratorRange(const Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIterator(slots, total_slots, current_slot) + { + } + SubIterator begin() const { - for (int64_t i = 0; i < total_slots_; i++) { - if (slots_[i].is_occupied()) { - return SubIterator(slots_, total_slots_, i); + for (int64_t i = 0; i < this->total_slots_; i++) { + if (this->slots_[i].is_occupied()) { + return SubIterator(this->slots_, this->total_slots_, i); } } return this->end(); @@ -679,23 +732,18 @@ class Map { SubIterator end() const { - return SubIterator(slots_, total_slots_, total_slots_); - } - - Slot ¤t_slot() const - { - return slots_[current_slot_]; + return SubIterator(this->slots_, this->total_slots_, this->total_slots_); } }; - class KeyIterator final : public BaseIterator<KeyIterator> { + class KeyIterator final : public BaseIteratorRange<KeyIterator> { public: using value_type = Key; using pointer = const Key *; using reference = const Key &; KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<KeyIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<KeyIterator>(slots, total_slots, current_slot) { } @@ -705,14 +753,14 @@ class Map { } }; - class ValueIterator final : public BaseIterator<ValueIterator> { + class ValueIterator final : public BaseIteratorRange<ValueIterator> { public: using value_type = Value; using pointer = const Value *; using reference = const Value &; ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<ValueIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<ValueIterator>(slots, total_slots, current_slot) { } @@ -722,14 +770,14 @@ class Map { } }; - class MutableValueIterator final : public BaseIterator<MutableValueIterator> { + class MutableValueIterator final : public BaseIteratorRange<MutableValueIterator> { public: using value_type = Value; using pointer = Value *; using reference = Value &; - MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<MutableValueIterator>(slots, total_slots, current_slot) + MutableValueIterator(Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIteratorRange<MutableValueIterator>(slots, total_slots, current_slot) { } @@ -754,14 +802,14 @@ class Map { } }; - class ItemIterator final : public BaseIterator<ItemIterator> { + class ItemIterator final : public BaseIteratorRange<ItemIterator> { public: using value_type = Item; using pointer = Item *; using reference = Item &; ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<ItemIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<ItemIterator>(slots, total_slots, current_slot) { } @@ -772,14 +820,14 @@ class Map { } }; - class MutableItemIterator final : public BaseIterator<MutableItemIterator> { + class MutableItemIterator final : public BaseIteratorRange<MutableItemIterator> { public: using value_type = MutableItem; using pointer = MutableItem *; using reference = MutableItem &; - MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<MutableItemIterator>(slots, total_slots, current_slot) + MutableItemIterator(Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIteratorRange<MutableItemIterator>(slots, total_slots, current_slot) { } @@ -840,6 +888,19 @@ class Map { } /** + * Remove the key-value-pair that the iterator is currently pointing at. + * It is valid to call this method while iterating over the map. However, after this method has + * been called, the removed element must not be accessed anymore. + */ + void remove(const BaseIterator &iterator) + { + Slot &slot = iterator.current_slot(); + BLI_assert(slot.is_occupied()); + slot.remove(); + removed_slots_++; + } + + /** * Print common statistics like size and collision count. This is useful for debugging purposes. */ void print_stats(StringRef name = "") const @@ -982,7 +1043,7 @@ class Map { SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) { Slot &slot = new_slots[slot_index]; if (slot.is_empty()) { - slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash); + slot.occupy(std::move(*old_slot.key()), hash, std::move(*old_slot.value())); return; } } @@ -996,8 +1057,8 @@ class Map { new (this) Map(NoExceptConstructor(), allocator); } - template<typename ForwardKey, typename ForwardValue> - void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { BLI_assert(!this->contains_as(key)); @@ -1005,7 +1066,7 @@ class Map { MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash); + slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...); occupied_and_removed_slots_++; return; } @@ -1013,14 +1074,14 @@ class Map { MAP_SLOT_PROBING_END(); } - template<typename ForwardKey, typename ForwardValue> - bool add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { this->ensure_can_add(); MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash); + slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...); occupied_and_removed_slots_++; return true; } @@ -1075,7 +1136,7 @@ class Map { MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - slot.occupy(std::forward<ForwardKey>(key), create_value(), hash); + slot.occupy(std::forward<ForwardKey>(key), hash, create_value()); occupied_and_removed_slots_++; return *slot.value(); } @@ -1086,14 +1147,14 @@ class Map { MAP_SLOT_PROBING_END(); } - template<typename ForwardKey, typename ForwardValue> - Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { this->ensure_can_add(); MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash); + slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...); occupied_and_removed_slots_++; return *slot.value(); } @@ -1104,15 +1165,15 @@ class Map { MAP_SLOT_PROBING_END(); } - template<typename ForwardKey, typename ForwardValue> - bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { auto create_func = [&](Value *ptr) { - new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)); + new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)...); return true; }; auto modify_func = [&](Value *ptr) { - *ptr = std::forward<ForwardValue>(value); + *ptr = Value(std::forward<ForwardValue>(value)...); return false; }; return this->add_or_modify__impl( @@ -1221,16 +1282,18 @@ template<typename Key, typename Value> class StdUnorderedMapWrapper { map_.reserve(n); } - template<typename ForwardKey, typename ForwardValue> - void add_new(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + void add_new(ForwardKey &&key, ForwardValue &&... value) { - map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}); + map_.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)}); } - template<typename ForwardKey, typename ForwardValue> - bool add(ForwardKey &&key, ForwardValue &&value) + template<typename ForwardKey, typename... ForwardValue> + bool add(ForwardKey &&key, ForwardValue &&... value) { - return map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second; + return map_ + .insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)}) + .second; } bool contains(const Key &key) const diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh index c0cb3091a8e..1b4ca11af41 100644 --- a/source/blender/blenlib/BLI_map_slots.hh +++ b/source/blender/blenlib/BLI_map_slots.hh @@ -195,11 +195,11 @@ template<typename Key, typename Value> class SimpleMapSlot { * Change the state of this slot from empty/removed to occupied. The key/value has to be * constructed by calling the constructor with the given key/value as parameter. */ - template<typename ForwardKey, typename ForwardValue> - void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { BLI_assert(!this->is_occupied()); - new (&value_buffer_) Value(std::forward<ForwardValue>(value)); + new (&value_buffer_) Value(std::forward<ForwardValue>(value)...); this->occupy_no_value(std::forward<ForwardKey>(key), hash); state_ = Occupied; } @@ -315,12 +315,12 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot return is_equal(key, key_); } - template<typename ForwardKey, typename ForwardValue> - void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash) + template<typename ForwardKey, typename... ForwardValue> + void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value) { BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); - new (&value_buffer_) Value(std::forward<ForwardValue>(value)); + new (&value_buffer_) Value(std::forward<ForwardValue>(value)...); this->occupy_no_value(std::forward<ForwardKey>(key), hash); } diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index 26d2f1fcb29..a113f3a370c 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -105,8 +105,8 @@ int constrain_rgb(float *r, float *g, float *b); void minmax_rgb(short c[3]); void hsv_clamp_v(float hsv[3], float v_max); -void rgb_float_set_hue_float_offset(float *rgb, float hue_offset); -void rgb_byte_set_hue_float_offset(unsigned char *rgb, float hue_offset); +void rgb_float_set_hue_float_offset(float rgb[3], float hue_offset); +void rgb_byte_set_hue_float_offset(unsigned char rgb[3], float hue_offset); void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3]); void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4]); diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index d767c2924d1..c744c5d13d3 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -119,10 +119,10 @@ float dist_signed_to_plane_v3(const float p[3], const float plane[4]); float dist_to_plane_v3(const float p[3], const float plane[4]); /* plane3 versions */ -float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[4]); -float dist_squared_to_plane3_v3(const float p[3], const float plane[4]); -float dist_signed_to_plane3_v3(const float p[3], const float plane[4]); -float dist_to_plane3_v3(const float p[3], const float plane[4]); +float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[3]); +float dist_squared_to_plane3_v3(const float p[3], const float plane[3]); +float dist_signed_to_plane3_v3(const float p[3], const float plane[3]); +float dist_to_plane3_v3(const float p[3], const float plane[3]); float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]); float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]); @@ -778,7 +778,7 @@ MINLINE float dot_shsh(const float a[9], const float b[9]); MINLINE float eval_shv3(float r[9], const float v[3]); MINLINE float diffuse_shv3(float r[9], const float v[3]); MINLINE void vec_fac_to_sh(float r[9], const float v[3], const float f); -MINLINE void madd_sh_shfl(float r[9], const float sh[3], const float f); +MINLINE void madd_sh_shfl(float r[9], const float sh[9], const float f); /********************************* Form Factor *******************************/ diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 378095589e8..54df88ca541 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -362,7 +362,7 @@ void loc_quat_size_to_mat4(float R[4][4], const float size[3]); void loc_axisangle_size_to_mat4(float R[4][4], const float loc[3], - const float axis[4], + const float axis[3], const float angle, const float size[3]); diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h index 13481e27e2a..39a79efc7e2 100644 --- a/source/blender/blenlib/BLI_math_solvers.h +++ b/source/blender/blenlib/BLI_math_solvers.h @@ -41,7 +41,7 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3], float r_eigen_values[3], float r_eigen_vectors[3][3]); -void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]); +void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3][3]); /***************************** Simple Solvers ************************************/ diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index bb1e1a1c38d..b43f86af670 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -146,7 +146,7 @@ MINLINE void mul_v3_v3(float r[3], const float a[3]); MINLINE void mul_v3_v3v3(float r[3], const float a[3], const float b[3]); MINLINE void mul_v4_fl(float r[4], float f); MINLINE void mul_v4_v4(float r[4], const float a[4]); -MINLINE void mul_v4_v4fl(float r[3], const float a[4], float f); +MINLINE void mul_v4_v4fl(float r[4], const float a[4], float f); MINLINE void mul_v2_v2_cw(float r[2], const float mat[2], const float vec[2]); MINLINE void mul_v2_v2_ccw(float r[2], const float mat[2], const float vec[2]); MINLINE float mul_project_m4_v3_zfac(const float mat[4][4], @@ -177,7 +177,7 @@ MINLINE void negate_v2_v2(float r[2], const float a[2]); MINLINE void negate_v3(float r[3]); MINLINE void negate_v3_v3(float r[3], const float a[3]); MINLINE void negate_v4(float r[4]); -MINLINE void negate_v4_v4(float r[4], const float a[3]); +MINLINE void negate_v4_v4(float r[4], const float a[4]); MINLINE void negate_v3_short(short r[3]); MINLINE void negate_v3_db(double r[3]); @@ -323,11 +323,11 @@ void flip_v2_v2v2(float v[2], const float v1[2], const float v2[2]); /********************************* Comparison ********************************/ -MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; +MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; -bool is_finite_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; +bool is_finite_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index fe511793c46..c3876d4eaf8 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -94,7 +94,7 @@ template<typename T> class Span { using iterator = const T *; using size_type = int64_t; - private: + protected: const T *data_ = nullptr; int64_t size_ = 0; @@ -477,7 +477,7 @@ template<typename T> class MutableSpan { using iterator = T *; using size_type = int64_t; - private: + protected: T *data_; int64_t size_; @@ -662,6 +662,16 @@ template<typename T> class MutableSpan { } /** + * Return a reference to the first element in the array. This invokes undefined behavior when the + * array is empty. + */ + constexpr T &first() const + { + BLI_assert(size_ > 0); + return data_[0]; + } + + /** * Returns a reference to the last element. This invokes undefined behavior when the array is * empty. */ diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh index 19f77078c5b..d66316a95d9 100644 --- a/source/blender/blenlib/BLI_stack.hh +++ b/source/blender/blenlib/BLI_stack.hh @@ -232,13 +232,14 @@ class Stack { { this->push_as(std::move(value)); } - template<typename ForwardT> void push_as(ForwardT &&value) + /* This is similar to `std::stack::emplace`. */ + template<typename... ForwardT> void push_as(ForwardT &&... value) { if (top_ == top_chunk_->capacity_end) { this->activate_next_chunk(1); } try { - new (top_) T(std::forward<ForwardT>(value)); + new (top_) T(std::forward<ForwardT>(value)...); top_++; size_++; } diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 328d623787b..8bea2584ca7 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -437,13 +437,17 @@ class Vector { */ void append(const T &value) { - this->ensure_space_for_one(); - this->append_unchecked(value); + this->append_as(value); } void append(T &&value) { + this->append_as(std::move(value)); + } + /* This is similar to `std::vector::emplace_back`. */ + template<typename... ForwardValue> void append_as(ForwardValue &&... value) + { this->ensure_space_for_one(); - this->append_unchecked(std::move(value)); + this->append_unchecked_as(std::forward<ForwardValue>(value)...); } /** @@ -474,10 +478,18 @@ class Vector { * behavior when not enough capacity has been reserved beforehand. Only use this in performance * critical code. */ - template<typename ForwardT> void append_unchecked(ForwardT &&value) + void append_unchecked(const T &value) + { + this->append_unchecked_as(value); + } + void append_unchecked(T &&value) + { + this->append_unchecked_as(std::move(value)); + } + template<typename... ForwardT> void append_unchecked_as(ForwardT &&... value) { BLI_assert(end_ < capacity_end_); - new (end_) T(std::forward<ForwardT>(value)); + new (end_) T(std::forward<ForwardT>(value)...); end_++; UPDATE_VECTOR_SIZE(this); } @@ -657,6 +669,21 @@ class Vector { } /** + * Return a reference to the first element in the vector. + * This invokes undefined behavior when the vector is empty. + */ + const T &first() const + { + BLI_assert(this->size() > 0); + return *begin_; + } + T &first() + { + BLI_assert(this->size() > 0); + return *begin_; + } + + /** * Return how many values are currently stored in the vector. */ int64_t size() const @@ -713,11 +740,12 @@ class Vector { BLI_assert(index >= 0); BLI_assert(index < this->size()); T *element_to_remove = begin_ + index; - if (element_to_remove < end_) { - *element_to_remove = std::move(*(end_ - 1)); + T *last_element = end_ - 1; + if (element_to_remove < last_element) { + *element_to_remove = std::move(*last_element); } - end_--; - end_->~T(); + end_ = last_element; + last_element->~T(); UPDATE_VECTOR_SIZE(this); } diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh index 14b38d564cb..567e4fd8128 100644 --- a/source/blender/blenlib/BLI_vector_set.hh +++ b/source/blender/blenlib/BLI_vector_set.hh @@ -398,6 +398,55 @@ class VectorSet { } /** + * Return the index of the key in the vector. If the key is not in the set, add it and return its + * index. + */ + int64_t index_of_or_add(const Key &key) + { + return this->index_of_or_add_as(key); + } + int64_t index_of_or_add(Key &&key) + { + return this->index_of_or_add_as(std::move(key)); + } + template<typename ForwardKey> int64_t index_of_or_add_as(ForwardKey &&key) + { + return this->index_of_or_add__impl(std::forward<ForwardKey>(key), hash_(key)); + } + + /** + * Returns the key that is stored in the vector set that compares equal to the given key. This + * invokes undefined behavior when the key is not in the set. + */ + const Key &lookup_key(const Key &key) const + { + return this->lookup_key_as(key); + } + template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const + { + const Key *key_ptr = this->lookup_key_ptr_as(key); + BLI_assert(key_ptr != nullptr); + return *key_ptr; + } + + /** + * Returns a pointer to the key that is stored in the vector set that compares equal to the given + * key. If the key is not in the set, null is returned. + */ + const Key *lookup_key_ptr(const Key &key) const + { + return this->lookup_key_ptr_as(key); + } + template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const + { + const int64_t index = this->index_of_try__impl(key, hash_(key)); + if (index >= 0) { + return keys_ + index; + } + return nullptr; + } + + /** * Get a pointer to the beginning of the array containing all keys. */ const Key *data() const @@ -484,6 +533,14 @@ class VectorSet { } /** + * Remove all keys from the vector set. + */ + void clear() + { + this->noexcept_reset(); + } + + /** * Get the number of collisions that the probing strategy has to go through to find the key or * determine that it is not in the set. */ @@ -652,6 +709,26 @@ class VectorSet { VECTOR_SET_SLOT_PROBING_END(); } + template<typename ForwardKey> + int64_t index_of_or_add__impl(ForwardKey &&key, const uint64_t hash) + { + this->ensure_can_add(); + + VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) { + if (slot.contains(key, is_equal_, hash, keys_)) { + return slot.index(); + } + if (slot.is_empty()) { + const int64_t index = this->size(); + new (keys_ + index) Key(std::forward<ForwardKey>(key)); + slot.occupy(index, hash); + occupied_and_removed_slots_++; + return index; + } + } + VECTOR_SET_SLOT_PROBING_END(); + } + Key pop__impl() { BLI_assert(this->size() > 0); diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index f9b0aaa7de6..1c02bce8411 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -37,6 +37,8 @@ * see of the increased compile time and binary size is worth it. */ +#include "BLI_array.hh" +#include "BLI_index_mask.hh" #include "BLI_span.hh" namespace blender { @@ -71,6 +73,11 @@ template<typename T> class VArray { return size_ == 0; } + IndexRange index_range() const + { + return IndexRange(size_); + } + /* Returns true when the virtual array is stored as a span internally. */ bool is_span() const { @@ -82,13 +89,13 @@ template<typename T> class VArray { /* Returns the internally used span of the virtual array. This invokes undefined behavior is the * virtual array is not stored as a span internally. */ - Span<T> get_span() const + Span<T> get_internal_span() const { BLI_assert(this->is_span()); if (size_ == 0) { return {}; } - return this->get_span_impl(); + return this->get_internal_span_impl(); } /* Returns true when the virtual array returns the same value for every index. */ @@ -102,20 +109,46 @@ template<typename T> class VArray { /* Returns the value that is returned for every index. This invokes undefined behavior if the * virtual array would not return the same value for every index. */ - T get_single() const + T get_internal_single() const { BLI_assert(this->is_single()); if (size_ == 1) { return this->get(0); } - return this->get_single_impl(); + return this->get_internal_single_impl(); } + /* Get the element at a specific index. Note that this operator cannot be used to assign values + * to an index, because the return value is not a reference. */ T operator[](const int64_t index) const { return this->get(index); } + /* Copy the entire virtual array into a span. */ + void materialize(MutableSpan<T> r_span) const + { + this->materialize(IndexMask(size_), r_span); + } + + /* Copy some indices of the virtual array into a span. */ + void materialize(IndexMask mask, MutableSpan<T> r_span) const + { + BLI_assert(mask.min_array_size() <= size_); + this->materialize_impl(mask, r_span); + } + + void materialize_to_uninitialized(MutableSpan<T> r_span) const + { + this->materialize_to_uninitialized(IndexMask(size_), r_span); + } + + void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const + { + BLI_assert(mask.min_array_size() <= size_); + this->materialize_to_uninitialized_impl(mask, r_span); + } + protected: virtual T get_impl(const int64_t index) const = 0; @@ -124,7 +157,7 @@ template<typename T> class VArray { return false; } - virtual Span<T> get_span_impl() const + virtual Span<T> get_internal_span_impl() const { BLI_assert_unreachable(); return {}; @@ -135,56 +168,196 @@ template<typename T> class VArray { return false; } - virtual T get_single_impl() const + virtual T get_internal_single_impl() const { /* Provide a default implementation, so that subclasses don't have to provide it. This method * should never be called because `is_single_impl` returns false by default. */ BLI_assert_unreachable(); return T(); } + + virtual void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const + { + T *dst = r_span.data(); + if (this->is_span()) { + const T *src = this->get_internal_span().data(); + mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; }); + } + else if (this->is_single()) { + const T single = this->get_internal_single(); + mask.foreach_index([&](const int64_t i) { dst[i] = single; }); + } + else { + mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); }); + } + } + + virtual void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const + { + T *dst = r_span.data(); + if (this->is_span()) { + const T *src = this->get_internal_span().data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); }); + } + else if (this->is_single()) { + const T single = this->get_internal_single(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); }); + } + else { + mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); }); + } + } +}; + +/* Similar to VArray, but the elements are mutable. */ +template<typename T> class VMutableArray : public VArray<T> { + public: + VMutableArray(const int64_t size) : VArray<T>(size) + { + } + + void set(const int64_t index, T value) + { + BLI_assert(index >= 0); + BLI_assert(index < this->size_); + this->set_impl(index, std::move(value)); + } + + /* Copy the values from the source span to all elements in the virtual array. */ + void set_all(Span<T> src) + { + BLI_assert(src.size() == this->size_); + this->set_all_impl(src); + } + + MutableSpan<T> get_internal_span() + { + BLI_assert(this->is_span()); + Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span(); + return MutableSpan<T>(const_cast<T *>(span.data()), span.size()); + } + + protected: + virtual void set_impl(const int64_t index, T value) = 0; + + virtual void set_all_impl(Span<T> src) + { + if (this->is_span()) { + const MutableSpan<T> span = this->get_internal_span(); + initialized_copy_n(src.data(), this->size_, span.data()); + } + else { + const int64_t size = this->size_; + for (int64_t i = 0; i < size; i++) { + this->set(i, src[i]); + } + } + } }; +template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>; +template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>; + /** - * A virtual array implementation for a span. This class is final so that it can be devirtualized - * by the compiler in some cases (e.g. when #devirtualize_varray is used). + * A virtual array implementation for a span. Methods in this class are final so that it can be + * devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used). */ -template<typename T> class VArrayForSpan final : public VArray<T> { - private: - const T *data_; +template<typename T> class VArray_For_Span : public VArray<T> { + protected: + const T *data_ = nullptr; public: - VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data()) + VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data()) { } protected: - T get_impl(const int64_t index) const override + VArray_For_Span(const int64_t size) : VArray<T>(size) + { + } + + T get_impl(const int64_t index) const final + { + return data_[index]; + } + + bool is_span_impl() const final + { + return true; + } + + Span<T> get_internal_span_impl() const final + { + return Span<T>(data_, this->size_); + } +}; + +template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> { + protected: + T *data_ = nullptr; + + public: + VMutableArray_For_MutableSpan(const MutableSpan<T> data) + : VMutableArray<T>(data.size()), data_(data.data()) + { + } + + protected: + VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size) + { + } + + T get_impl(const int64_t index) const final { return data_[index]; } + void set_impl(const int64_t index, T value) final + { + data_[index] = value; + } + bool is_span_impl() const override { return true; } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { return Span<T>(data_, this->size_); } }; /** + * A variant of `VArray_For_Span` that owns the underlying data. + * The `Container` type has to implement a `size()` and `data()` method. + * The `data()` method has to return a pointer to the first element in the continuous array of + * elements. + */ +template<typename Container, typename T = typename Container::value_type> +class VArray_For_ArrayContainer : public VArray_For_Span<T> { + private: + Container container_; + + public: + VArray_For_ArrayContainer(Container container) + : VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container)) + { + this->data_ = container_.data(); + } +}; + +/** * A virtual array implementation that returns the same value for every index. This class is final * so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is * used). */ -template<typename T> class VArrayForSingle final : public VArray<T> { +template<typename T> class VArray_For_Single final : public VArray<T> { private: T value_; public: - VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value)) + VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value)) { } @@ -199,7 +372,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> { return this->size_ == 1; } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { return Span<T>(&value_, 1); } @@ -209,13 +382,207 @@ template<typename T> class VArrayForSingle final : public VArray<T> { return true; } - T get_single_impl() const override + T get_internal_single_impl() const override { return value_; } }; /** + * In many cases a virtual array is a span internally. In those cases, access to individual could + * be much more efficient than calling a virtual method. When the underlying virtual array is not a + * span, this class allocates a new array and copies the values over. + * + * This should be used in those cases: + * - All elements in the virtual array are accessed multiple times. + * - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit + * from faster access. + * - An API is called, that does not accept virtual arrays, but only spans. + */ +template<typename T> class VArray_Span final : public Span<T> { + private: + const VArray<T> &varray_; + Array<T> owned_data_; + + public: + VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray) + { + this->size_ = varray_.size(); + if (varray_.is_span()) { + this->data_ = varray_.get_internal_span().data(); + } + else { + owned_data_.~Array(); + new (&owned_data_) Array<T>(varray_.size(), NoInitialization{}); + varray_.materialize_to_uninitialized(owned_data_); + this->data_ = owned_data_.data(); + } + } +}; + +/** + * Same as VArray_Span, but for a mutable span. + * The important thing to note is that when changing this span, the results might not be + * immediately reflected in the underlying virtual array (only when the virtual array is a span + * internally). The #save method can be used to write all changes to the underlying virtual array, + * if necessary. + */ +template<typename T> class VMutableArray_Span final : public MutableSpan<T> { + private: + VMutableArray<T> &varray_; + Array<T> owned_data_; + bool save_has_been_called_ = false; + bool show_not_saved_warning_ = true; + + public: + /* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If + * not, a new array has to be allocated as a wrapper for the underlying virtual array. */ + VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true) + : MutableSpan<T>(), varray_(varray) + { + this->size_ = varray_.size(); + if (varray_.is_span()) { + this->data_ = varray_.get_internal_span().data(); + } + else { + if (copy_values_to_span) { + owned_data_.~Array(); + new (&owned_data_) Array<T>(varray_.size(), NoInitialization{}); + varray_.materialize_to_uninitialized(owned_data_); + } + else { + owned_data_.reinitialize(varray_.size()); + } + this->data_ = owned_data_.data(); + } + } + + ~VMutableArray_Span() + { + if (show_not_saved_warning_) { + if (!save_has_been_called_) { + std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; + } + } + } + + /* Write back all values from a temporary allocated array to the underlying virtual array. */ + void save() + { + save_has_been_called_ = true; + if (this->data_ != owned_data_.data()) { + return; + } + varray_.set_all(owned_data_); + } + + void disable_not_applied_warning() + { + show_not_saved_warning_ = false; + } +}; + +/** + * This class makes it easy to create a virtual array for an existing function or lambda. The + * `GetFunc` should take a single `index` argument and return the value at that index. + */ +template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> { + private: + GetFunc get_func_; + + public: + VArray_For_Func(const int64_t size, GetFunc get_func) + : VArray<T>(size), get_func_(std::move(get_func)) + { + } + + private: + T get_impl(const int64_t index) const override + { + return get_func_(index); + } + + void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const override + { + T *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = get_func_(i); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const override + { + T *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(get_func_(i)); }); + } +}; + +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +class VArray_For_DerivedSpan : public VArray<ElemT> { + private: + const StructT *data_; + + public: + VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data()) + { + } + + private: + ElemT get_impl(const int64_t index) const override + { + return GetFunc(data_[index]); + } + + void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); }); + } +}; + +template<typename StructT, + typename ElemT, + ElemT (*GetFunc)(const StructT &), + void (*SetFunc)(StructT &, ElemT)> +class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> { + private: + StructT *data_; + + public: + VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data) + : VMutableArray<ElemT>(data.size()), data_(data.data()) + { + } + + private: + ElemT get_impl(const int64_t index) const override + { + return GetFunc(data_[index]); + } + + void set_impl(const int64_t index, ElemT value) override + { + SetFunc(data_[index], std::move(value)); + } + + void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); }); + } +}; + +/** * Generate multiple versions of the given function optimized for different virtual arrays. * One has to be careful with nesting multiple devirtualizations, because that results in an * exponential number of function instantiations (increasing compile time and binary size). @@ -229,14 +596,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool /* Support disabling the devirtualization to simplify benchmarking. */ if (enable) { if (varray.is_single()) { - /* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */ - const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()}; + /* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */ + const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()}; func(varray_single); return; } if (varray.is_span()) { - /* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */ - const VArrayForSpan<T> varray_span{varray.get_span()}; + /* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */ + const VArray_For_Span<T> varray_span{varray.get_internal_span()}; func(varray_span); return; } @@ -262,26 +629,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1, const bool is_single1 = varray1.is_single(); const bool is_single2 = varray2.is_single(); if (is_span1 && is_span2) { - const VArrayForSpan<T1> varray1_span{varray1.get_span()}; - const VArrayForSpan<T2> varray2_span{varray2.get_span()}; + const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()}; + const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()}; func(varray1_span, varray2_span); return; } if (is_span1 && is_single2) { - const VArrayForSpan<T1> varray1_span{varray1.get_span()}; - const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()}; + const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()}; + const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()}; func(varray1_span, varray2_single); return; } if (is_single1 && is_span2) { - const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()}; - const VArrayForSpan<T2> varray2_span{varray2.get_span()}; + const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()}; + const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()}; func(varray1_single, varray2_span); return; } if (is_single1 && is_single2) { - const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()}; - const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()}; + const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()}; + const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()}; func(varray1_single, varray2_single); return; } diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e66049c9bd6..ce3515ac153 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -185,6 +185,7 @@ set(SRC BLI_edgehash.h BLI_endian_switch.h BLI_endian_switch_inline.h + BLI_enumerable_thread_specific.hh BLI_expr_pylike_eval.h BLI_fileops.h BLI_fileops_types.h diff --git a/source/blender/blenlib/intern/BLI_dial_2d.c b/source/blender/blenlib/intern/BLI_dial_2d.c index b5f1d7818cf..786fdd219a3 100644 --- a/source/blender/blenlib/intern/BLI_dial_2d.c +++ b/source/blender/blenlib/intern/BLI_dial_2d.c @@ -78,7 +78,7 @@ float BLI_dial_angle(Dial *dial, const float current_position[2]) cosval = dot_v2v2(current_direction, dial->initial_direction); sinval = cross_v2v2(current_direction, dial->initial_direction); - /* clamp to avoid nans in #acos */ + /* Clamp to avoid NAN's in #acos */ angle = atan2f(sinval, cosval); /* change of sign, we passed the 180 degree threshold. This means we need to add a turn. diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 01cda6c9e4a..bd1fa0fbb8b 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -5393,7 +5393,7 @@ void accumulate_vertex_normals_poly_v3(float **vertnos, void tangent_from_uv_v3(const float uv1[2], const float uv2[2], - const float uv3[3], + const float uv3[2], const float co1[3], const float co2[3], const float co3[3], diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 3558dfad81d..636209883c3 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -1744,9 +1744,7 @@ static inline std::pair<int, int> sorted_int_pair(std::pair<int, int> pair) if (pair.first <= pair.second) { return pair; } - else { - return std::pair<int, int>(pair.second, pair.first); - } + return std::pair<int, int>(pair.second, pair.first); } /** diff --git a/source/blender/blenlib/intern/uvproject.c b/source/blender/blenlib/intern/uvproject.c index 329c4d48fe8..093d08e643d 100644 --- a/source/blender/blenlib/intern/uvproject.c +++ b/source/blender/blenlib/intern/uvproject.c @@ -134,7 +134,7 @@ void BLI_uvproject_from_view(float target[2], /* 'rotmat' can be `obedit->obmat` when uv project is used. * 'winx' and 'winy' can be from `scene->r.xsch/ysch` */ -ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float (*rotmat)[4], float winx, float winy) +ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float rotmat[4][4], float winx, float winy) { ProjCameraInfo uci; Camera *camera = ob->data; diff --git a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc index 977e5dba497..0e0145e592a 100644 --- a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc +++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc @@ -136,4 +136,17 @@ TEST(linear_allocator, ManyAllocations) } } +TEST(linear_allocator, ConstructArray) +{ + LinearAllocator<> allocator; + MutableSpan<std::string> strings = allocator.construct_array<std::string>(4, "hello"); + EXPECT_EQ(strings[0], "hello"); + EXPECT_EQ(strings[1], "hello"); + EXPECT_EQ(strings[2], "hello"); + EXPECT_EQ(strings[3], "hello"); + for (std::string &string : strings) { + string.~basic_string(); + } +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc index f1ae8fb3921..679a10e9ce0 100644 --- a/source/blender/blenlib/tests/BLI_map_test.cc +++ b/source/blender/blenlib/tests/BLI_map_test.cc @@ -604,6 +604,55 @@ TEST(map, GenericAlgorithms) EXPECT_EQ(std::count(map.keys().begin(), map.keys().end(), 7), 1); } +TEST(map, AddAsVariadic) +{ + Map<int, StringRef> map; + map.add_as(3, "hello", 2); + map.add_as(2, "test", 1); + EXPECT_EQ(map.lookup(3), "he"); + EXPECT_EQ(map.lookup(2), "t"); +} + +TEST(map, RemoveDuringIteration) +{ + Map<int, int> map; + map.add(2, 1); + map.add(5, 2); + map.add(1, 2); + map.add(6, 0); + map.add(3, 3); + + EXPECT_EQ(map.size(), 5); + + using Iter = Map<int, int>::MutableItemIterator; + Iter begin = map.items().begin(); + Iter end = map.items().end(); + for (Iter iter = begin; iter != end; ++iter) { + Map<int, int>::MutableItem item = *iter; + if (item.value == 2) { + map.remove(iter); + } + } + + EXPECT_EQ(map.size(), 3); + EXPECT_EQ(map.lookup(2), 1); + EXPECT_EQ(map.lookup(6), 0); + EXPECT_EQ(map.lookup(3), 3); +} + +TEST(map, LookupKey) +{ + Map<std::string, int> map; + map.add("a", 0); + map.add("b", 1); + map.add("c", 2); + EXPECT_EQ(map.lookup_key("a"), "a"); + EXPECT_EQ(map.lookup_key_as("c"), "c"); + EXPECT_EQ(map.lookup_key_ptr_as("d"), nullptr); + EXPECT_EQ(map.lookup_key_ptr_as("b")->size(), 1); + EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a")); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc index f1fcdae3a52..b3108381d78 100644 --- a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc +++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc @@ -93,6 +93,15 @@ TEST(stack, Push) EXPECT_EQ(stack.size(), 2); } +TEST(stack, PushAs) +{ + Stack<StringRef> stack; + stack.push_as("hello", 3); + stack.push_as("world", 1); + EXPECT_EQ(stack.pop(), "w"); + EXPECT_EQ(stack.pop(), "hel"); +} + TEST(stack, PushMultiple) { Stack<int> stack; diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc index bbbe96f9b7e..c4016ca75e1 100644 --- a/source/blender/blenlib/tests/BLI_vector_set_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc @@ -232,4 +232,43 @@ TEST(vector_set, PopExceptions) EXPECT_EQ(set.size(), 4); } +TEST(vector_set, IndexOfOrAdd) +{ + VectorSet<int> set; + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(2), 1); + EXPECT_EQ(set.index_of_or_add(0), 2); + EXPECT_EQ(set.index_of_or_add(2), 1); + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(5), 3); + EXPECT_EQ(set.index_of_or_add(8), 4); + EXPECT_EQ(set.index_of_or_add(5), 3); +} + +TEST(vector_set, Clear) +{ + VectorSet<int> set = {4, 6, 2, 4}; + EXPECT_EQ(set.size(), 3); + set.clear(); + EXPECT_EQ(set.size(), 0); + set.add_multiple({4, 1, 6, 8, 3, 6, 9, 3}); + EXPECT_EQ(set.size(), 6); + set.clear(); + EXPECT_EQ(set.size(), 0); +} + +TEST(vector_set, LookupKey) +{ + VectorSet<std::string> set; + set.add("a"); + set.add("b"); + set.add("c"); + EXPECT_EQ(set.lookup_key("a"), "a"); + EXPECT_EQ(set.lookup_key_as("c"), "c"); + EXPECT_EQ(set.lookup_key_ptr_as("d"), nullptr); + EXPECT_EQ(set.lookup_key_ptr_as("b")->size(), 1); + EXPECT_EQ(set.lookup_key_ptr("a"), set.lookup_key_ptr_as("a")); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index 462f13c15ab..e8636168308 100644 --- a/source/blender/blenlib/tests/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -248,6 +248,15 @@ TEST(vector, Append) EXPECT_EQ(vec[2], 7); } +TEST(vector, AppendAs) +{ + Vector<StringRef> vec; + vec.append_as("hello", 2); + vec.append_as("world", 3); + EXPECT_EQ(vec[0], "he"); + EXPECT_EQ(vec[1], "wor"); +} + TEST(vector, AppendAndGetIndex) { Vector<int> vec; diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc index ac25229cd69..a6d2ca10315 100644 --- a/source/blender/blenlib/tests/BLI_virtual_array_test.cc +++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc @@ -1,26 +1,29 @@ /* Apache License, Version 2.0 */ +#include "BLI_array.hh" #include "BLI_strict_flags.h" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" #include "BLI_virtual_array.hh" #include "testing/testing.h" namespace blender::tests { -TEST(virtual_array, ForSpan) +TEST(virtual_array, Span) { std::array<int, 5> data = {3, 4, 5, 6, 7}; - VArrayForSpan<int> varray{data}; + VArray_For_Span<int> varray{data}; EXPECT_EQ(varray.size(), 5); EXPECT_EQ(varray.get(0), 3); EXPECT_EQ(varray.get(4), 7); EXPECT_TRUE(varray.is_span()); EXPECT_FALSE(varray.is_single()); - EXPECT_EQ(varray.get_span().data(), data.data()); + EXPECT_EQ(varray.get_internal_span().data(), data.data()); } -TEST(virtual_array, ForSingle) +TEST(virtual_array, Single) { - VArrayForSingle<int> varray{10, 4}; + VArray_For_Single<int> varray{10, 4}; EXPECT_EQ(varray.size(), 4); EXPECT_EQ(varray.get(0), 10); EXPECT_EQ(varray.get(3), 10); @@ -28,4 +31,124 @@ TEST(virtual_array, ForSingle) EXPECT_TRUE(varray.is_single()); } +TEST(virtual_array, Array) +{ + Array<int> array = {1, 2, 3, 5, 8}; + { + VArray_For_ArrayContainer varray{array}; + EXPECT_EQ(varray.size(), 5); + EXPECT_EQ(varray[0], 1); + EXPECT_EQ(varray[2], 3); + EXPECT_EQ(varray[3], 5); + EXPECT_TRUE(varray.is_span()); + } + { + VArray_For_ArrayContainer varray{std::move(array)}; + EXPECT_EQ(varray.size(), 5); + EXPECT_EQ(varray[0], 1); + EXPECT_EQ(varray[2], 3); + EXPECT_EQ(varray[3], 5); + EXPECT_TRUE(varray.is_span()); + } + { + VArray_For_ArrayContainer varray{array}; /* NOLINT: bugprone-use-after-move */ + EXPECT_TRUE(varray.is_empty()); + } +} + +TEST(virtual_array, Vector) +{ + Vector<int> vector = {9, 8, 7, 6}; + VArray_For_ArrayContainer varray{std::move(vector)}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 9); + EXPECT_EQ(varray[3], 6); +} + +TEST(virtual_array, StdVector) +{ + std::vector<int> vector = {5, 6, 7, 8}; + VArray_For_ArrayContainer varray{std::move(vector)}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 5); + EXPECT_EQ(varray[1], 6); +} + +TEST(virtual_array, StdArray) +{ + std::array<int, 4> array = {2, 3, 4, 5}; + VArray_For_ArrayContainer varray{array}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 2); + EXPECT_EQ(varray[1], 3); +} + +TEST(virtual_array, VectorSet) +{ + VectorSet<int> vector_set = {5, 3, 7, 3, 3, 5, 1}; + VArray_For_ArrayContainer varray{std::move(vector_set)}; + EXPECT_TRUE(vector_set.is_empty()); /* NOLINT: bugprone-use-after-move. */ + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 5); + EXPECT_EQ(varray[1], 3); + EXPECT_EQ(varray[2], 7); + EXPECT_EQ(varray[3], 1); +} + +TEST(virtual_array, Func) +{ + auto func = [](int64_t index) { return (int)(index * index); }; + VArray_For_Func<int, decltype(func)> varray{10, func}; + EXPECT_EQ(varray.size(), 10); + EXPECT_EQ(varray[0], 0); + EXPECT_EQ(varray[3], 9); + EXPECT_EQ(varray[9], 81); +} + +TEST(virtual_array, AsSpan) +{ + auto func = [](int64_t index) { return (int)(10 * index); }; + VArray_For_Func<int, decltype(func)> func_varray{10, func}; + VArray_Span span_varray{func_varray}; + EXPECT_EQ(span_varray.size(), 10); + Span<int> span = span_varray; + EXPECT_EQ(span.size(), 10); + EXPECT_EQ(span[0], 0); + EXPECT_EQ(span[3], 30); + EXPECT_EQ(span[6], 60); +} + +static int get_x(const std::array<int, 3> &item) +{ + return item[0]; +} + +static void set_x(std::array<int, 3> &item, int value) +{ + item[0] = value; +} + +TEST(virtual_array, DerivedSpan) +{ + Vector<std::array<int, 3>> vector; + vector.append({3, 4, 5}); + vector.append({1, 1, 1}); + { + VArray_For_DerivedSpan<std::array<int, 3>, int, get_x> varray{vector}; + EXPECT_EQ(varray.size(), 2); + EXPECT_EQ(varray[0], 3); + EXPECT_EQ(varray[1], 1); + } + { + VMutableArray_For_DerivedSpan<std::array<int, 3>, int, get_x, set_x> varray{vector}; + EXPECT_EQ(varray.size(), 2); + EXPECT_EQ(varray[0], 3); + EXPECT_EQ(varray[1], 1); + varray.set(0, 10); + varray.set(1, 20); + EXPECT_EQ(vector[0][0], 10); + EXPECT_EQ(vector[1][0], 20); + } +} + } // namespace blender::tests diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index ee9b9a49768..36802fc8842 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -57,6 +57,7 @@ set(SRC intern/versioning_270.c intern/versioning_280.c intern/versioning_290.c + intern/versioning_300.c intern/versioning_cycles.c intern/versioning_defaults.c intern/versioning_dna.c diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 19ae0014bb8..fe7d50bfa15 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -111,6 +111,7 @@ #include "SEQ_iterator.h" #include "SEQ_modifier.h" #include "SEQ_sequencer.h" +#include "SEQ_utils.h" #include "readfile.h" @@ -2696,7 +2697,7 @@ static int lib_link_seq_clipboard_cb(Sequence *seq, void *arg_pt) static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map) { /* update IDs stored in sequencer clipboard */ - SEQ_iterator_seqbase_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map); + SEQ_seqbase_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map); } static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data) @@ -3868,6 +3869,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) blo_do_versions_270(fd, lib, main); blo_do_versions_280(fd, lib, main); blo_do_versions_290(fd, lib, main); + blo_do_versions_300(fd, lib, main); blo_do_versions_cycles(fd, lib, main); /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ @@ -3891,6 +3893,7 @@ static void do_versions_after_linking(Main *main, ReportList *reports) do_versions_after_linking_270(main); do_versions_after_linking_280(main, reports); do_versions_after_linking_290(main, reports); + do_versions_after_linking_300(main, reports); do_versions_after_linking_cycles(main); main->is_locked_for_linking = false; diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index 9682b5456d2..d1d4e0b3256 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -210,6 +210,7 @@ void blo_do_versions_260(struct FileData *fd, struct Library *lib, struct Main * void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *bmain); void blo_do_versions_280(struct FileData *fd, struct Library *lib, struct Main *bmain); void blo_do_versions_290(struct FileData *fd, struct Library *lib, struct Main *bmain); +void blo_do_versions_300(struct FileData *fd, struct Library *lib, struct Main *bmain); void blo_do_versions_cycles(struct FileData *fd, struct Library *lib, struct Main *bmain); void do_versions_after_linking_250(struct Main *bmain); @@ -217,6 +218,7 @@ void do_versions_after_linking_260(struct Main *bmain); void do_versions_after_linking_270(struct Main *bmain); void do_versions_after_linking_280(struct Main *bmain, struct ReportList *reports); void do_versions_after_linking_290(struct Main *bmain, struct ReportList *reports); +void do_versions_after_linking_300(struct Main *bmain, struct ReportList *reports); void do_versions_after_linking_cycles(struct Main *bmain); /* This is rather unfortunate to have to expose this here, but better use that nasty hack in diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 97fe9fb04c4..2b6f44c694b 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -2069,19 +2069,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Set default value for the new bisect_threshold parameter in the mirror modifier. */ - if (!MAIN_VERSION_ATLEAST(bmain, 293, 19)) { - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { - if (md->type == eModifierType_Mirror) { - MirrorModifierData *mmd = (MirrorModifierData *)md; - /* This was the previous hard-coded value. */ - mmd->bisect_threshold = 0.001f; - } - } - } - } - /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c new file mode 100644 index 00000000000..8c5e86eadd3 --- /dev/null +++ b/source/blender/blenloader/intern/versioning_300.c @@ -0,0 +1,135 @@ +/* + * 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. + */ + +/** \file + * \ingroup blenloader + */ +/* allow readfile to use deprecated functionality */ +#define DNA_DEPRECATED_ALLOW + +#include "BLI_listbase.h" +#include "BLI_math_vector.h" +#include "BLI_utildefines.h" + +#include "DNA_brush_types.h" +#include "DNA_genfile.h" +#include "DNA_modifier_types.h" +#include "DNA_text_types.h" + +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_node.h" + +#include "BLO_readfile.h" +#include "readfile.h" + +void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) +{ + if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) { + /* Set zero user text objects to have a fake user. */ + LISTBASE_FOREACH (Text *, text, &bmain->texts) { + if (text->id.us == 0) { + id_fake_user_set(&text->id); + } + } + } + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_300 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + + /* Use new texture socket in Attribute Sample Texture node. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) { + continue; + } + if (node->id == NULL) { + continue; + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->type == SOCK_TEXTURE) { + bNodeSocketValueTexture *socket_value = (bNodeSocketValueTexture *) + socket->default_value; + socket_value->value = (Tex *)node->id; + break; + } + } + node->id = NULL; + } + } + } +} + +/* NOLINTNEXTLINE: readability-function-size */ +void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) +{ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 1)) { + /* Set default value for the new bisect_threshold parameter in the mirror modifier. */ + if (!DNA_struct_elem_find(fd->filesdna, "MirrorModifierData", "float", "bisect_threshold")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + /* This was the previous hard-coded value. */ + mmd->bisect_threshold = 0.001f; + } + } + } + } + /* Grease Pencil: Set default value for dilate pixels. */ + if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "int", "dilate_pixels")) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->gpencil_settings) { + brush->gpencil_settings->dilate_pixels = 1; + } + } + } + } + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose == NULL) { + continue; + } + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); + } + } + } + } +} diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index f2e73e161ca..56b2f18f8a6 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1278,12 +1278,6 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) if (ob->soft->physics_speed == 0.0f) { ob->soft->physics_speed = 1.0f; } - - if (ob->soft->interval == 0) { - ob->soft->interval = 2; - ob->soft->sfra = 1; - ob->soft->efra = 100; - } } if (ob->soft && ob->soft->vertgroup == 0) { bDeformGroup *locGroup = BKE_object_defgroup_find_name(ob, "SOFTGOAL"); diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index dcf9717465c..0754564fa47 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -106,7 +106,7 @@ static void normalize_v2_m3_v3v3(float out[2], /** * \note Be sure to update #bm_face_split_edgenet_find_loop_pair_exists - * when making changed to edge picking logic. + * when making changes to edge picking logic. */ static bool bm_face_split_edgenet_find_loop_pair(BMVert *v_init, const float face_normal[3], @@ -456,8 +456,8 @@ static bool bm_face_split_edgenet_find_loop(BMVert *v_init, * Splits a face into many smaller faces defined by an edge-net. * handle customdata and degenerate cases. * - * - isolated holes or unsupported face configurations, will be ignored. - * - customdata calculations aren't efficient + * - Isolated holes or unsupported face configurations, will be ignored. + * - Customdata calculations aren't efficient * (need to calculate weights for each vert). */ bool BM_face_split_edgenet(BMesh *bm, @@ -593,7 +593,7 @@ bool BM_face_split_edgenet(BMesh *bm, BMIter iter; BMLoop *l_other; - /* see: #BM_loop_interp_from_face for similar logic */ + /* See: #BM_loop_interp_from_face for similar logic. */ void **blocks = BLI_array_alloca(blocks, f->len); float(*cos_2d)[2] = BLI_array_alloca(cos_2d, f->len); float *w = BLI_array_alloca(w, f->len); @@ -1064,7 +1064,7 @@ static int bm_face_split_edgenet_find_connection(const struct EdgeGroup_FindConn #ifdef USE_PARTIAL_CONNECT /** - * Used to identify edges that get split off when making island from partial connection. + * Used to identify edges that get split off when making island from partial connection. * fptr should be a BMFace*, but is a void* for general interface to BM_vert_separate_tested_edges */ static bool test_tagged_and_notface(BMEdge *e, void *fptr) @@ -1211,7 +1211,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap, const int v_a_index, const int v_b_index) { - /* connected to eachother */ + /* Connected to each other. */ if (UNLIKELY((remap[v_a_index] == v_b_index) || (remap[v_b_index] == v_a_index))) { return true; } @@ -1226,7 +1226,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap, * \param use_partial_connect: Support for handling islands connected by only a single edge, * \note that this is quite slow so avoid using where possible. * \param mem_arena: Avoids many small allocs & should be cleared after each use. - * take care since \a r_edge_net_new is stored in \a r_edge_net_new. + * take care since \a edge_net_new is stored in \a r_edge_net_new. */ bool BM_face_split_edgenet_connect_islands(BMesh *bm, BMFace *f, @@ -1246,7 +1246,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm, * * Keep the first part fast since it will run very often for edge-nets that have no holes. * - * \note Don't use the mem_arena unless he have holes to fill. + * \note Don't use the mem_arena unless we have holes to fill. * (avoid thrashing the area when the initial check isn't so intensive on the stack). */ @@ -1572,7 +1572,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm, if (g->has_prev_edge == false) { BMVert *v_origin = g->vert_span.min; - + /* Index of BMVert for the edge group connection with `v_origin`. */ const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, false); // BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len); @@ -1598,7 +1598,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm, { BMVert *v_origin = g->vert_span.max; - + /* Index of BMVert for the edge group connection with `v_origin`. */ const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, true); // BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len); @@ -1660,7 +1660,7 @@ finally: struct TempVertPair *tvp = temp_vert_pairs.list; do { /* its _very_ unlikely the edge exists, - * however splicing may case this. see: T48012 */ + * however splicing may cause this. see: T48012 */ if (!BM_edge_exists(tvp->v_orig, tvp->v_temp)) { BM_vert_splice(bm, tvp->v_orig, tvp->v_temp); } diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c index f840a3770ff..e7d0fe6a0c6 100644 --- a/source/blender/bmesh/tools/bmesh_bisect_plane.c +++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c @@ -39,12 +39,13 @@ #include "BLI_utildefines_stack.h" #include "bmesh.h" -#include "bmesh_bisect_plane.h" /* own include */ +#include "bmesh_bisect_plane.h" /* Own include. */ -#include "BLI_strict_flags.h" /* keep last */ +#include "BLI_strict_flags.h" /* Keep last. */ /* -------------------------------------------------------------------- */ -/* Math utils */ +/** \name Math Functions + * \{ */ static short plane_point_test_v3(const float plane[4], const float co[3], @@ -63,10 +64,15 @@ static short plane_point_test_v3(const float plane[4], return 0; } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Wrappers to hide internal data-structure abuse, +/** \name BMesh Element Accessors + * + * Wrappers to hide internal data-structure abuse, * later we may want to move this into some hash lookup - * to a separate struct, but for now we can store in BMesh data */ + * to a separate struct, but for now we can store in #BMesh data. + * \{ */ #define BM_VERT_DIR(v) ((short *)(&(v)->head.index))[0] /* Direction -1/0/1 */ #define BM_VERT_SKIP(v) ((short *)(&(v)->head.index))[1] /* Skip Vert 0/1 */ @@ -75,12 +81,16 @@ static short plane_point_test_v3(const float plane[4], #define BM_VERT_LOOPINDEX(v) /* The verts index within a face (temp var) */ \ (*((uint *)(&(v)->no[2]))) -/** +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name BMesh Flag Accessors + * * Hide flag access - * (for more readable code since same flag is used differently for vert/edgeface)... + * (for more readable code since same flag is used differently for vert/edge-face). */ -/* enable when vertex is in the center and its faces have been added to the stack */ +/** Enable when vertex is in the center and its faces have been added to the stack. */ BLI_INLINE void vert_is_center_enable(BMVert *v) { BM_elem_flag_enable(v, BM_ELEM_TAG); @@ -100,7 +110,7 @@ BLI_INLINE bool vert_pair_adjacent_in_orig_face(BMVert *v_a, BMVert *v_b, const return ELEM(delta, 1, (uint)(f_len_orig - 1)); } -/* enable when the edge can be cut */ +/** Enable when the edge can be cut. */ BLI_INLINE void edge_is_cut_enable(BMEdge *e) { BM_elem_flag_enable(e, BM_ELEM_TAG); @@ -114,7 +124,7 @@ BLI_INLINE bool edge_is_cut_test(BMEdge *e) return (BM_elem_flag_test(e, BM_ELEM_TAG) != 0); } -/* enable when the faces are added to the stack */ +/** Enable when the faces are added to the stack. */ BLI_INLINE void face_in_stack_enable(BMFace *f) { BM_elem_flag_disable(f, BM_ELEM_TAG); @@ -128,8 +138,11 @@ BLI_INLINE bool face_in_stack_test(BMFace *f) return (BM_elem_flag_test(f, BM_ELEM_TAG) == 0); } +/** \} */ + /* -------------------------------------------------------------------- */ -/* BMesh utils */ +/** \name BMesh Face Bisect + * \{ */ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v) { @@ -148,7 +161,7 @@ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v) static void bm_face_bisect_verts( BMesh *bm, BMFace *f, const float plane[4], const short oflag_center, const short oflag_new) { - /* unlikely more than 2 verts are needed */ + /* Unlikely more than 2 verts are needed. */ const uint f_len_orig = (uint)f->len; BMVert **vert_split_arr = BLI_array_alloca(vert_split_arr, f_len_orig); STACK_DECLARE(vert_split_arr); @@ -163,16 +176,14 @@ static void bm_face_bisect_verts( l_first = BM_FACE_FIRST_LOOP(f); - /* add plane-aligned verts to the stack - * and check we have verts from both sides in this face, - * ... that the face doesn't only have boundary verts on the plane for eg. */ + /* Add plane-aligned verts to the stack and check we have verts from both sides in this face + * (that the face doesn't only have boundary verts on the plane for eg). */ l_iter = l_first; do { if (vert_is_center_test(l_iter->v)) { BLI_assert(BM_VERT_DIR(l_iter->v) == 0); - /* if both are -1 or 1, or both are zero: - * don't flip 'inside' var while walking */ + /* If both are -1 or 1, or both are zero: don't flip 'inside' var while walking. */ BM_VERT_SKIP(l_iter->v) = (((BM_VERT_DIR(l_iter->prev->v) ^ BM_VERT_DIR(l_iter->next->v))) == 0); @@ -195,7 +206,7 @@ static void bm_face_bisect_verts( l_a = BM_face_vert_share_loop(f, vert_split_arr[0]); l_b = BM_face_vert_share_loop(f, vert_split_arr[1]); - /* common case, just cut the face once */ + /* Common case, just cut the face once. */ BM_face_split(bm, f, l_a, l_b, &l_new, NULL, true); if (l_new) { if (oflag_center | oflag_new) { @@ -207,6 +218,7 @@ static void bm_face_bisect_verts( } } else { + /* Less common case, _complicated_ we need to calculate how to do multiple cuts. */ uint i = 0; @@ -263,7 +275,6 @@ static void bm_face_bisect_verts( } while ((l_iter = l_iter->next) != l_first_non_center); } - /* less common case, _complicated_ we need to calculate how to do multiple cuts */ float(*face_verts_proj_2d)[2] = BLI_array_alloca(face_verts_proj_2d, f_len_orig); float axis_mat[3][3]; @@ -275,10 +286,8 @@ static void bm_face_bisect_verts( /* ---- */ /* Calculate the direction to sort verts in the face intersecting the plane */ - /* exact dir isn't so important, - * just need a dir for sorting verts across face, - * 'sort_dir' could be flipped either way, it not important, we only need to order the array - */ + /* The exact direction isn't important, vertices just need to be sorted across the face. + * (`sort_dir` could be flipped either way). */ cross_v3_v3v3(sort_dir, f->no, plane); if (UNLIKELY(normalize_v3(sort_dir) == 0.0f)) { /* find any 2 verts and get their direction */ @@ -289,8 +298,8 @@ static void bm_face_bisect_verts( } } if (UNLIKELY(i == STACK_SIZE(vert_split_arr))) { - /* ok, we can't do anything useful here, - * face has no area or so, bail out, this is highly unlikely but not impossible */ + /* Ok, we can't do anything useful here, + * face has no area or so, bail out, this is highly unlikely but not impossible. */ goto finally; } } @@ -298,7 +307,7 @@ static void bm_face_bisect_verts( /* ---- */ /* Calculate 2d coords to use for intersection checks */ - /* get the faces 2d coords */ + /* Get the faces 2d coords. */ BLI_assert(BM_face_is_normal_valid(f)); axis_dominant_v3_to_m3(axis_mat, f->no); @@ -310,7 +319,7 @@ static void bm_face_bisect_verts( } while ((l_iter = l_iter->next) != l_first); /* ---- */ - /* Sort the verts across the face from one side to another */ + /* Sort the verts across the face from one side to another. */ for (i = 0; i < STACK_SIZE(vert_split_arr); i++) { BMVert *v = vert_split_arr[i]; BM_VERT_SORTVAL(v) = dot_v3v3(sort_dir, v->co); @@ -320,9 +329,9 @@ static void bm_face_bisect_verts( vert_split_arr, STACK_SIZE(vert_split_arr), sizeof(*vert_split_arr), bm_vert_sortval_cb); /* ---- */ - /* Split the face across sorted splits */ + /* Split the face across sorted splits. */ - /* note: we don't know which face gets which splits, + /* NOTE: we don't know which face gets which splits, * so at the moment we have to search all faces for the vert pair, * while not all that nice, typically there are < 5 resulting faces, * so its not _that_ bad. */ @@ -350,8 +359,8 @@ static void bm_face_bisect_verts( uint j; for (j = 0; j < STACK_SIZE(face_split_arr); j++) { - /* would be nice to avoid loop lookup here, - * but we need to know which face the verts are in */ + /* It would be nice to avoid loop lookup here, + * but we need to know which face the verts are in. */ if ((l_a = BM_face_vert_share_loop(face_split_arr[j], v_a)) && (l_b = BM_face_vert_share_loop(face_split_arr[j], v_b))) { found = true; @@ -359,11 +368,10 @@ static void bm_face_bisect_verts( } } - /* ideally wont happen, but it can for self intersecting faces */ + /* Ideally wont happen, but it can for self intersecting faces. */ // BLI_assert(found == true); - /* in fact this simple test is good enough, - * test if the loops are adjacent */ + /* In fact this simple test is good enough, test if the loops are adjacent. */ if (found && !BM_loop_is_adjacent(l_a, l_b)) { BMLoop *l_new; BMFace *f_tmp; @@ -397,8 +405,11 @@ finally: (void)vert_split_arr; } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Main logic */ +/** \name Public BMesh Bisect Function + * \{ */ /** * \param use_snap_center: Snap verts onto the plane. @@ -425,25 +436,25 @@ void BM_mesh_bisect_plane(BMesh *bm, BMIter iter; if (use_tag) { - /* build tagged edge array */ + /* Build tagged edge array. */ BMEdge *e; einput_len = 0; - /* flush edge tags to verts */ + /* Flush edge tags to verts. */ BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); - /* keep face tags as is */ + /* Keep face tags as is. */ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { if (edge_is_cut_test(e)) { edges_arr[einput_len++] = e; - /* flush edge tags to verts */ + /* Flush edge tags to verts. */ BM_elem_flag_enable(e->v1, BM_ELEM_TAG); BM_elem_flag_enable(e->v2, BM_ELEM_TAG); } } - /* face tags are set by caller */ + /* Face tags are set by caller. */ } else { BMEdge *e; @@ -463,7 +474,7 @@ void BM_mesh_bisect_plane(BMesh *bm, if (use_tag && !BM_elem_flag_test(v, BM_ELEM_TAG)) { vert_is_center_disable(v); - /* these should never be accessed */ + /* These should never be accessed. */ BM_VERT_DIR(v) = 0; BM_VERT_DIST(v) = 0.0f; @@ -483,11 +494,11 @@ void BM_mesh_bisect_plane(BMesh *bm, } } - /* store a stack of faces to be evaluated for splitting */ + /* Store a stack of faces to be evaluated for splitting. */ BLI_LINKSTACK_INIT(face_stack); for (i = 0; i < einput_len; i++) { - /* we could check edge_is_cut_test(e) but there is no point */ + /* We could check `edge_is_cut_test(e)` but there is no point. */ BMEdge *e = edges_arr[i]; const int side[2] = {BM_VERT_DIR(e->v1), BM_VERT_DIR(e->v2)}; const float dist[2] = {BM_VERT_DIST(e->v1), BM_VERT_DIST(e->v2)}; @@ -524,8 +535,8 @@ void BM_mesh_bisect_plane(BMesh *bm, BM_VERT_DIST(v_new) = 0.0f; } else if (side[0] == 0 || side[1] == 0) { - /* check if either edge verts are aligned, - * if so - tag and push all faces that use it into the stack */ + /* Check if either edge verts are aligned, + * if so - tag and push all faces that use it into the stack. */ uint j; BM_ITER_ELEM_INDEX (v, &iter, e, BM_VERTS_OF_EDGE, j) { if (side[j] == 0) { @@ -545,7 +556,7 @@ void BM_mesh_bisect_plane(BMesh *bm, } } - /* if both verts are on the center - tag it */ + /* If both verts are on the center - tag it. */ if (oflag_center) { if (side[0] == 0 && side[1] == 0) { BMO_edge_flag_enable(bm, e, oflag_center); @@ -560,9 +571,11 @@ void BM_mesh_bisect_plane(BMesh *bm, bm_face_bisect_verts(bm, f, plane, oflag_center, oflag_new); } - /* Caused by access macros: BM_VERT_DIR, BM_VERT_SKIP. */ + /* Caused by access macros: #BM_VERT_DIR, #BM_VERT_SKIP. */ bm->elem_index_dirty |= BM_VERT; - /* now we have all faces to split in the stack */ + /* Now we have all faces to split in the stack. */ BLI_LINKSTACK_FREE(face_stack); } + +/** \} */ diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index c5ff2b3adc6..ef889807030 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -55,7 +55,6 @@ constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType // configurable items // chunk size determination -// #define COM_DEBUG // chunk order /** diff --git a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc index 79afcc9deea..b8e19fc2c34 100644 --- a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc +++ b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc @@ -30,4 +30,4 @@ double ChunkOrderHotspot::calc_distance(int x, int y) return result; } -} // namespace blender::compositor
\ No newline at end of file +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 648ef7519d1..dfb4f53fee5 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -42,8 +42,6 @@ extern "C" { namespace blender::compositor { -#ifdef COM_DEBUG - int DebugInfo::m_file_index = 0; DebugInfo::NodeNameMap DebugInfo::m_node_names; DebugInfo::OpNameMap DebugInfo::m_op_names; @@ -69,50 +67,6 @@ std::string DebugInfo::operation_name(const NodeOperation *op) return ""; } -void DebugInfo::convert_started() -{ - m_op_names.clear(); -} - -void DebugInfo::execute_started(const ExecutionSystem *system) -{ - m_file_index = 1; - m_group_states.clear(); - for (ExecutionGroup *execution_group : system->m_groups) { - m_group_states[execution_group] = EG_WAIT; - } -} - -void DebugInfo::node_added(const Node *node) -{ - m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : ""); -} - -void DebugInfo::node_to_operations(const Node *node) -{ - m_current_node_name = m_node_names[node]; -} - -void DebugInfo::operation_added(const NodeOperation *operation) -{ - m_op_names[operation] = m_current_node_name; -} - -void DebugInfo::operation_read_write_buffer(const NodeOperation *operation) -{ - m_current_op_name = m_op_names[operation]; -} - -void DebugInfo::execution_group_started(const ExecutionGroup *group) -{ - m_group_states[group] = EG_RUNNING; -} - -void DebugInfo::execution_group_finished(const ExecutionGroup *group) -{ - m_group_states[group] = EG_FINISHED; -} - int DebugInfo::graphviz_operation(const ExecutionSystem *system, NodeOperation *operation, const ExecutionGroup *group, @@ -442,6 +396,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma void DebugInfo::graphviz(const ExecutionSystem *system) { + if (!COM_EXPORT_GRAPHVIZ) { + return; + } char str[1000000]; if (graphviz_system(system, str, sizeof(str) - 1)) { char basename[FILE_MAX]; @@ -459,44 +416,4 @@ void DebugInfo::graphviz(const ExecutionSystem *system) } } -#else - -std::string DebugInfo::node_name(const Node * /*node*/) -{ - return ""; -} -std::string DebugInfo::operation_name(const NodeOperation * /*op*/) -{ - return ""; -} -void DebugInfo::convert_started() -{ -} -void DebugInfo::execute_started(const ExecutionSystem * /*system*/) -{ -} -void DebugInfo::node_added(const Node * /*node*/) -{ -} -void DebugInfo::node_to_operations(const Node * /*node*/) -{ -} -void DebugInfo::operation_added(const NodeOperation * /*operation*/) -{ -} -void DebugInfo::operation_read_write_buffer(const NodeOperation * /*operation*/) -{ -} -void DebugInfo::execution_group_started(const ExecutionGroup * /*group*/) -{ -} -void DebugInfo::execution_group_finished(const ExecutionGroup * /*group*/) -{ -} -void DebugInfo::graphviz(const ExecutionSystem * /*system*/) -{ -} - -#endif - } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index bf7b981fbd5..e1aea69e481 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -21,11 +21,13 @@ #include <map> #include <string> +#include "COM_ExecutionSystem.h" #include "COM_NodeOperation.h" #include "COM_defines.h" namespace blender::compositor { +static constexpr bool COM_EXPORT_GRAPHVIZ = false; class Node; class ExecutionSystem; class ExecutionGroup; @@ -41,20 +43,81 @@ class DebugInfo { static std::string node_name(const Node *node); static std::string operation_name(const NodeOperation *op); - static void convert_started(); - static void execute_started(const ExecutionSystem *system); + private: + static int m_file_index; + /** Map nodes to usable names for debug output. */ + static NodeNameMap m_node_names; + /** Map operations to usable names for debug output. */ + static OpNameMap m_op_names; + /** Base name for all operations added by a node. */ + static std::string m_current_node_name; + /** Base name for automatic sub-operations. */ + static std::string m_current_op_name; + /** For visualizing group states. */ + static GroupStateMap m_group_states; + + public: + static void convert_started() + { + if (COM_EXPORT_GRAPHVIZ) { + m_op_names.clear(); + } + } + + static void execute_started(const ExecutionSystem *system) + { + if (COM_EXPORT_GRAPHVIZ) { + m_file_index = 1; + m_group_states.clear(); + for (ExecutionGroup *execution_group : system->m_groups) { + m_group_states[execution_group] = EG_WAIT; + } + } + }; + + static void node_added(const Node *node) + { + if (COM_EXPORT_GRAPHVIZ) { + m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : ""); + } + } - static void node_added(const Node *node); - static void node_to_operations(const Node *node); - static void operation_added(const NodeOperation *operation); - static void operation_read_write_buffer(const NodeOperation *operation); + static void node_to_operations(const Node *node) + { + if (COM_EXPORT_GRAPHVIZ) { + m_current_node_name = m_node_names[node]; + } + } - static void execution_group_started(const ExecutionGroup *group); - static void execution_group_finished(const ExecutionGroup *group); + static void operation_added(const NodeOperation *operation) + { + if (COM_EXPORT_GRAPHVIZ) { + m_op_names[operation] = m_current_node_name; + } + }; + + static void operation_read_write_buffer(const NodeOperation *operation) + { + if (COM_EXPORT_GRAPHVIZ) { + m_current_op_name = m_op_names[operation]; + } + }; + + static void execution_group_started(const ExecutionGroup *group) + { + if (COM_EXPORT_GRAPHVIZ) { + m_group_states[group] = EG_RUNNING; + } + }; + static void execution_group_finished(const ExecutionGroup *group) + { + if (COM_EXPORT_GRAPHVIZ) { + m_group_states[group] = EG_FINISHED; + } + }; static void graphviz(const ExecutionSystem *system); -#ifdef COM_DEBUG protected: static int graphviz_operation(const ExecutionSystem *system, NodeOperation *operation, @@ -68,20 +131,6 @@ class DebugInfo { const char *name, const char *color, const char *style, char *str, int maxlen); static int graphviz_legend(char *str, int maxlen); static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen); - - private: - static int m_file_index; - /** Map nodes to usable names for debug output. */ - static NodeNameMap m_node_names; - /** Map operations to usable names for debug output. */ - static OpNameMap m_op_names; - /** Base name for all operations added by a node. */ - static std::string m_current_node_name; - /** Base name for automatic sub-operations. */ - static std::string m_current_op_name; - /** For visualizing group states. */ - static GroupStateMap m_group_states; -#endif }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 68e39b19eaf..8c30d3215d7 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -25,29 +25,49 @@ namespace blender::compositor { MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state) { m_rect = rect; + this->m_is_a_single_elem = false; this->m_memoryProxy = memoryProxy; this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType()); this->m_buffer = (float *)MEM_mallocN_aligned( sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer"); this->m_state = state; this->m_datatype = memoryProxy->getDataType(); + + set_strides(); } -MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect) +MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem) { m_rect = rect; + this->m_is_a_single_elem = is_a_single_elem; this->m_memoryProxy = nullptr; this->m_num_channels = COM_data_type_num_channels(dataType); this->m_buffer = (float *)MEM_mallocN_aligned( sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer"); this->m_state = MemoryBufferState::Temporary; this->m_datatype = dataType; + + set_strides(); } MemoryBuffer::MemoryBuffer(const MemoryBuffer &src) - : MemoryBuffer(src.m_memoryProxy, src.m_rect, MemoryBufferState::Temporary) + : MemoryBuffer(src.m_datatype, src.m_rect, false) +{ + m_memoryProxy = src.m_memoryProxy; + /* src may be single elem buffer */ + fill_from(src); +} + +void MemoryBuffer::set_strides() { - memcpy(m_buffer, src.m_buffer, buffer_len() * m_num_channels * sizeof(float)); + if (m_is_a_single_elem) { + this->elem_stride = 0; + this->row_stride = 0; + } + else { + this->elem_stride = m_num_channels; + this->row_stride = getWidth() * m_num_channels; + } } void MemoryBuffer::clear() @@ -100,6 +120,8 @@ MemoryBuffer::~MemoryBuffer() void MemoryBuffer::fill_from(const MemoryBuffer &src) { + BLI_assert(!this->is_a_single_elem()); + unsigned int otherY; unsigned int minX = MAX2(this->m_rect.xmin, src.m_rect.xmin); unsigned int maxX = MIN2(this->m_rect.xmax, src.m_rect.xmax); @@ -109,10 +131,8 @@ void MemoryBuffer::fill_from(const MemoryBuffer &src) int otherOffset; for (otherY = minY; otherY < maxY; otherY++) { - otherOffset = ((otherY - src.m_rect.ymin) * src.getWidth() + minX - src.m_rect.xmin) * - this->m_num_channels; - offset = ((otherY - this->m_rect.ymin) * getWidth() + minX - this->m_rect.xmin) * - this->m_num_channels; + otherOffset = src.get_coords_offset(minX, otherY); + offset = this->get_coords_offset(minX, otherY); memcpy(&this->m_buffer[offset], &src.m_buffer[otherOffset], (maxX - minX) * this->m_num_channels * sizeof(float)); @@ -123,8 +143,7 @@ void MemoryBuffer::writePixel(int x, int y, const float color[4]) { if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin && y < this->m_rect.ymax) { - const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) * - this->m_num_channels; + const int offset = get_coords_offset(x, y); memcpy(&this->m_buffer[offset], color, sizeof(float) * this->m_num_channels); } } @@ -133,8 +152,7 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4]) { if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin && y < this->m_rect.ymax) { - const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) * - this->m_num_channels; + const int offset = get_coords_offset(x, y); float *dst = &this->m_buffer[offset]; const float *src = color; for (int i = 0; i < this->m_num_channels; i++, dst++, src++) { @@ -151,26 +169,31 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4] void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2]) { - BLI_assert(this->m_datatype == DataType::Color); - float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); - /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, - * but compositor uses pixel space. For now let's just divide the values and - * switch compositor to normalized space for EWA later. - */ - float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height}; - float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height}; - float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height}; - - BLI_ewa_filter(this->getWidth(), - this->getHeight(), - false, - true, - uv_normal, - du_normal, - dv_normal, - read_ewa_pixel_sampled, - this, - result); + if (m_is_a_single_elem) { + memcpy(result, m_buffer, sizeof(float) * this->m_num_channels); + } + else { + BLI_assert(this->m_datatype == DataType::Color); + float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); + /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, + * but compositor uses pixel space. For now let's just divide the values and + * switch compositor to normalized space for EWA later. + */ + float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height}; + float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height}; + float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height}; + + BLI_ewa_filter(this->getWidth(), + this->getHeight(), + false, + true, + uv_normal, + du_normal, + dv_normal, + read_ewa_pixel_sampled, + this, + result); + } } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 060a67f8797..97b220508e0 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -50,6 +50,25 @@ class MemoryProxy; * \brief a MemoryBuffer contains access to the data of a chunk */ class MemoryBuffer { + public: + /** + * Offset between elements. + * + * Should always be used for the x dimension when calculating buffer offsets. + * It will be 0 when is_a_single_elem=true. + * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride + */ + int elem_stride; + + /** + * Offset between rows. + * + * Should always be used for the y dimension when calculating buffer offsets. + * It will be 0 when is_a_single_elem=true. + * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride + */ + int row_stride; + private: /** * \brief proxy of the memory (same for all chunks in the same buffer) @@ -82,6 +101,11 @@ class MemoryBuffer { */ uint8_t m_num_channels; + /** + * Whether buffer is a single element in memory. + */ + bool m_is_a_single_elem; + public: /** * \brief construct new temporarily MemoryBuffer for an area @@ -91,7 +115,7 @@ class MemoryBuffer { /** * \brief construct new temporarily MemoryBuffer for an area */ - MemoryBuffer(DataType datatype, const rcti &rect); + MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false); /** * Copy constructor @@ -103,6 +127,102 @@ class MemoryBuffer { */ ~MemoryBuffer(); + /** + * Whether buffer is a single element in memory independently of its resolution. True for set + * operations buffers. + */ + bool is_a_single_elem() const + { + return m_is_a_single_elem; + } + + float &operator[](int index) + { + BLI_assert(m_is_a_single_elem ? index < m_num_channels : + index < get_coords_offset(getWidth(), getHeight())); + return m_buffer[index]; + } + + const float &operator[](int index) const + { + BLI_assert(m_is_a_single_elem ? index < m_num_channels : + index < get_coords_offset(getWidth(), getHeight())); + return m_buffer[index]; + } + + /** + * Get offset needed to jump from buffer start to given coordinates. + */ + int get_coords_offset(int x, int y) const + { + return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride; + } + + /** + * Get buffer element at given coordinates. + */ + float *get_elem(int x, int y) + { + BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax); + return m_buffer + get_coords_offset(x, y); + } + + /** + * Get buffer element at given coordinates. + */ + const float *get_elem(int x, int y) const + { + BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax); + return m_buffer + get_coords_offset(x, y); + } + + /** + * Get channel value at given coordinates. + */ + float &get_value(int x, int y, int channel) + { + BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax && + channel >= 0 && channel < m_num_channels); + return m_buffer[get_coords_offset(x, y) + channel]; + } + + /** + * Get channel value at given coordinates. + */ + const float &get_value(int x, int y, int channel) const + { + BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax && + channel >= 0 && channel < m_num_channels); + return m_buffer[get_coords_offset(x, y) + channel]; + } + + /** + * Get the buffer row end. + */ + const float *get_row_end(int y) const + { + BLI_assert(y >= 0 && y < getHeight()); + return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y)); + } + + /** + * Get the number of elements in memory for a row. For single element buffers it will always + * be 1. + */ + int get_memory_width() const + { + return is_a_single_elem() ? 1 : getWidth(); + } + + /** + * Get number of elements in memory for a column. For single element buffers it will + * always be 1. + */ + int get_memory_height() const + { + return is_a_single_elem() ? 1 : getHeight(); + } + uint8_t get_num_channels() { return this->m_num_channels; @@ -216,7 +336,7 @@ class MemoryBuffer { int u = x; int v = y; this->wrap_pixel(u, v, extend_x, extend_y); - const int offset = (getWidth() * y + x) * this->m_num_channels; + const int offset = get_coords_offset(u, v); float *buffer = &this->m_buffer[offset]; memcpy(result, buffer, sizeof(float) * this->m_num_channels); } @@ -232,7 +352,7 @@ class MemoryBuffer { int v = y; this->wrap_pixel(u, v, extend_x, extend_y); - const int offset = (getWidth() * v + u) * this->m_num_channels; + const int offset = get_coords_offset(u, v); BLI_assert(offset >= 0); BLI_assert(offset < this->buffer_len() * this->m_num_channels); @@ -258,15 +378,20 @@ class MemoryBuffer { copy_vn_fl(result, this->m_num_channels, 0.0f); return; } - BLI_bilinear_interpolation_wrap_fl(this->m_buffer, - result, - getWidth(), - getHeight(), - this->m_num_channels, - u, - v, - extend_x == MemoryBufferExtend::Repeat, - extend_y == MemoryBufferExtend::Repeat); + if (m_is_a_single_elem) { + memcpy(result, m_buffer, sizeof(float) * this->m_num_channels); + } + else { + BLI_bilinear_interpolation_wrap_fl(this->m_buffer, + result, + getWidth(), + getHeight(), + this->m_num_channels, + u, + v, + extend_x == MemoryBufferExtend::Repeat, + extend_y == MemoryBufferExtend::Repeat); + } } void readEWA(float *result, const float uv[2], const float derivatives[2][2]); @@ -321,9 +446,10 @@ class MemoryBuffer { float get_max_value(const rcti &rect) const; private: + void set_strides(); const int buffer_len() const { - return getWidth() * getHeight(); + return get_memory_width() * get_memory_height(); } #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index ee3a6dedd44..d578ac24a4a 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -263,10 +263,10 @@ static void opencl_initialize(const bool use_opencl) if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error2, clewErrorString(error2)); } - g_work_scheduler.opencl.devices.append(OpenCLDevice(g_work_scheduler.opencl.context, - device, - g_work_scheduler.opencl.program, - vendorID)); + g_work_scheduler.opencl.devices.append_as(g_work_scheduler.opencl.context, + device, + g_work_scheduler.opencl.program, + vendorID); } } MEM_freeN(cldevices); @@ -368,7 +368,7 @@ static void threading_model_queue_initialize(const int num_cpu_threads) /* Initialize CPU threads. */ if (!g_work_scheduler.queue.initialized) { for (int index = 0; index < num_cpu_threads; index++) { - g_work_scheduler.queue.devices.append(CPUDevice(index)); + g_work_scheduler.queue.devices.append_as(index); } BLI_thread_local_create(g_thread_device); g_work_scheduler.queue.initialized = true; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index ec5037fb29c..d0fe91bc96d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1557,6 +1557,12 @@ void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket) else if (socket->type == SOCK_COLLECTION) { build_id((ID *)((bNodeSocketValueCollection *)socket->default_value)->value); } + else if (socket->type == SOCK_TEXTURE) { + build_id((ID *)((bNodeSocketValueTexture *)socket->default_value)->value); + } + else if (socket->type == SOCK_MATERIAL) { + build_id((ID *)((bNodeSocketValueMaterial *)socket->default_value)->value); + } } void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc index 13caba67713..00c78b8edce 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc @@ -169,7 +169,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible) } /* Speed optimization for animation lookups. */ if (object->pose != nullptr) { - BKE_pose_channels_hash_make(object->pose); + BKE_pose_channels_hash_ensure(object->pose); if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(object->pose); } @@ -318,7 +318,7 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object, bool is_object_visibl /* Armature. */ build_armature(armature); /* speed optimization for animation lookups */ - BKE_pose_channels_hash_make(object->pose); + BKE_pose_channels_hash_ensure(object->pose); if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(object->pose); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 0f8b613f7ac..8a02228146a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2401,6 +2401,18 @@ void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket) build_collection(nullptr, nullptr, collection); } } + else if (socket->type == SOCK_TEXTURE) { + Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value; + if (texture != nullptr) { + build_texture(texture); + } + } + else if (socket->type == SOCK_MATERIAL) { + Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value; + if (material != nullptr) { + build_material(material); + } + } } void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 196b46953c4..27167ce839b 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -478,6 +478,7 @@ add_dependencies(bf_draw bf_dna) if(WITH_GTESTS) if(WITH_OPENGL_DRAW_TESTS) set(TEST_SRC + tests/draw_testing.cc tests/shaders_test.cc ) set(TEST_INC diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c index 0b66141e51a..21d55357a2a 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -424,22 +424,26 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx GPUShader *sh = GPENCIL_shader_fx_glow_get(); - float ref_col[3]; + float ref_col[4]; if (fx->mode == eShaderFxGlowMode_Luminance) { + /* Only pass in the first value for luminance. */ ref_col[0] = fx->threshold; ref_col[1] = -1.0f; ref_col[2] = -1.0f; + ref_col[3] = -1.0f; } else { + /* First three values are the RGB for the selected color, last value the threshold. */ copy_v3_v3(ref_col, fx->select_color); + ref_col[3] = fx->threshold; } DRWState state = DRW_STATE_WRITE_COLOR; grp = gpencil_vfx_pass_create("Fx Glow H", state, iter, sh); DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){fx->blur[0] * c, fx->blur[0] * s}); DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0]))); - DRW_shgroup_uniform_vec3_copy(grp, "threshold", ref_col); + DRW_shgroup_uniform_vec4_copy(grp, "threshold", ref_col); DRW_shgroup_uniform_vec4_copy(grp, "glowColor", fx->glow_color); DRW_shgroup_uniform_bool_copy(grp, "glowUnder", use_glow_under); DRW_shgroup_uniform_bool_copy(grp, "firstPass", true); @@ -473,7 +477,7 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx grp = gpencil_vfx_pass_create("Fx Glow V", state, iter, sh); DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-fx->blur[1] * s, fx->blur[1] * c}); DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0]))); - DRW_shgroup_uniform_vec3_copy(grp, "threshold", (float[3]){-1.0f, -1.0f, -1.0f}); + DRW_shgroup_uniform_vec4_copy(grp, "threshold", (float[4]){-1.0f, -1.0f, -1.0f, -1.0}); DRW_shgroup_uniform_vec4_copy(grp, "glowColor", (float[4]){1.0f, 1.0f, 1.0f, fx->glow_color[3]}); DRW_shgroup_uniform_bool_copy(grp, "firstPass", false); DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->blend_mode); diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl index bb905f8694b..269ed49c4d0 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl @@ -145,7 +145,7 @@ void main() uniform vec4 glowColor; uniform vec2 offset; uniform int sampCount; -uniform vec3 threshold; +uniform vec4 threshold; uniform bool firstPass; uniform bool glowUnder; uniform int blendMode; @@ -168,7 +168,7 @@ void main() vec3 rev = texture(revealBuf, uv).rgb; if (threshold.x > -1.0) { if (threshold.y > -1.0) { - if (any(greaterThan(abs(col - threshold), vec3(0.05)))) { + if (any(greaterThan(abs(col - vec3(threshold)), vec3(threshold.w)))) { weight = 0.0; } } diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index 54224071d23..fbad60ff4ab 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -1031,7 +1031,7 @@ static void pchan_draw_data_init(bPoseChannel *pchan) static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *pchan) { float ebmat[4][4]; - float length; + float bone_scale[3]; float(*bone_mat)[4]; float(*disp_mat)[4]; float(*disp_tail_mat)[4]; @@ -1040,23 +1040,23 @@ static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel * * and not be tight to the draw pass creation. * This would refresh armature without invalidating the draw cache */ if (pchan) { - length = pchan->bone->length; bone_mat = pchan->pose_mat; disp_mat = pchan->disp_mat; disp_tail_mat = pchan->disp_tail_mat; + copy_v3_fl(bone_scale, pchan->bone->length); } else { eBone->length = len_v3v3(eBone->tail, eBone->head); ED_armature_ebone_to_mat4(eBone, ebmat); - length = eBone->length; + copy_v3_fl(bone_scale, eBone->length); bone_mat = ebmat; disp_mat = eBone->disp_mat; disp_tail_mat = eBone->disp_tail_mat; } copy_m4_m4(disp_mat, bone_mat); - rescale_m4(disp_mat, (float[3]){length, length, length}); + rescale_m4(disp_mat, bone_scale); copy_m4_m4(disp_tail_mat, disp_mat); translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f); } @@ -1255,19 +1255,27 @@ static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pc static void draw_bone_update_disp_matrix_custom(bPoseChannel *pchan) { - float length; + float bone_scale[3]; float(*bone_mat)[4]; float(*disp_mat)[4]; float(*disp_tail_mat)[4]; + float rot_mat[3][3]; /* See TODO above */ - length = PCHAN_CUSTOM_DRAW_SIZE(pchan); + mul_v3_v3fl(bone_scale, pchan->custom_scale_xyz, PCHAN_CUSTOM_BONE_LENGTH(pchan)); bone_mat = pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat; disp_mat = pchan->disp_mat; disp_tail_mat = pchan->disp_tail_mat; + eulO_to_mat3(rot_mat, pchan->custom_rotation_euler, ROT_MODE_XYZ); + copy_m4_m4(disp_mat, bone_mat); - rescale_m4(disp_mat, (float[3]){length, length, length}); + translate_m4(disp_mat, + pchan->custom_translation[0], + pchan->custom_translation[1], + pchan->custom_translation[2]); + mul_m4_m4m3(disp_mat, disp_mat, rot_mat); + rescale_m4(disp_mat, bone_scale); copy_m4_m4(disp_tail_mat, disp_mat); translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f); } diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index c07271a0d33..49b5e0fecd3 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -348,7 +348,14 @@ static void gpencil_buffer_add_stroke(gpStrokeVert *verts, } /* Draw line to first point to complete the loop for cyclic strokes. */ if (is_cyclic) { - gpencil_buffer_add_point(verts, cols, gps, &pts[0], v++, false); + gpencil_buffer_add_point(verts, cols, gps, &pts[0], v, false); + /* UV factor needs to be adjusted for the last point to not be equal to the UV factor of the + * first point. It should be the factor of the last point plus the distance from the last point + * to the first. + */ + gpStrokeVert *vert = &verts[v]; + vert->u_stroke = verts[v - 1].u_stroke + len_v3v3(&pts[pts_len - 1].x, &pts[0].x); + v++; } /* Last adjacency point (not drawn). */ adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2); diff --git a/source/blender/draw/tests/draw_testing.cc b/source/blender/draw/tests/draw_testing.cc new file mode 100644 index 00000000000..0104437e921 --- /dev/null +++ b/source/blender/draw/tests/draw_testing.cc @@ -0,0 +1,18 @@ +/* Apache License, Version 2.0 */ + +#include "draw_testing.hh" + +#include "GPU_shader.h" + +#include "draw_manager_testing.h" + +namespace blender::draw { + +/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */ +void DrawTest::SetUp() +{ + GPUTest::SetUp(); + DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT); +} + +} // namespace blender::draw diff --git a/source/blender/draw/tests/draw_testing.hh b/source/blender/draw/tests/draw_testing.hh new file mode 100644 index 00000000000..ec0b15b611e --- /dev/null +++ b/source/blender/draw/tests/draw_testing.hh @@ -0,0 +1,13 @@ +/* Apache License, Version 2.0 */ + +#include "gpu_testing.hh" + +namespace blender::draw { + +/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */ +class DrawTest : public blender::gpu::GPUTest { + public: + void SetUp() override; +}; + +} // namespace blender::draw diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index 96d544fd855..c96f22859ca 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -2,12 +2,12 @@ #include "testing/testing.h" +#include "draw_testing.hh" #include "intern/draw_manager_testing.h" #include "GPU_context.h" #include "GPU_init_exit.h" #include "GPU_shader.h" -#include "gpu_testing.hh" #include "engines/eevee/eevee_private.h" #include "engines/gpencil/gpencil_engine.h" @@ -17,19 +17,9 @@ namespace blender::draw { -/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */ -class DrawTest : public blender::gpu::GPUTest { - void SetUp() override - { - GPUTest::SetUp(); - DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT); - } -}; - TEST_F(DrawTest, workbench_glsl_shaders) { workbench_shader_library_ensure(); - DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT); const int MAX_WPD = 6; WORKBENCH_PrivateData wpds[MAX_WPD]; diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index 98c050950be..0030e78002b 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -17,6 +17,7 @@ set(INC ../include + ../../blenfont ../../blenkernel ../../blenlib ../../blentranslation diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 68fff1091af..3902f6613a1 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -339,7 +339,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) } BKE_pose_channels_hash_free(ob->pose); - BKE_pose_channels_hash_make(ob->pose); + BKE_pose_channels_hash_ensure(ob->pose); GHash *name_map = BLI_ghash_str_new(__func__); @@ -390,7 +390,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone, bConstraint *curcon; ListBase *conlist; - if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) { + if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name))) { if ((conlist = &pchan->constraints)) { for (curcon = conlist->first; curcon; curcon = curcon->next) { /* does this constraint have a subtarget in @@ -825,7 +825,7 @@ static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig bConstraint *curcon; ListBase *conlist; - if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL || + if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name)) == NULL || (conlist = &pchan->constraints) == NULL) { return; } @@ -855,7 +855,7 @@ static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Obj return; } bPoseChannel *pchan; - pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name); + pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name); if (pchan->custom != NULL) { Main *bmain = CTX_data_main(C); @@ -885,12 +885,12 @@ static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, O if (src_ob->pose) { bPoseChannel *chanold, *channew; - chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name); + chanold = BKE_pose_channel_ensure(src_ob->pose, src_bone->name); if (chanold) { /* WARNING: this creates a new posechannel, but there will not be an attached bone * yet as the new bones created here are still 'EditBones' not 'Bones'. */ - channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name); + channew = BKE_pose_channel_ensure(dst_ob->pose, dst_bone->name); if (channew) { BKE_pose_channel_copy_data(channew, chanold); @@ -1193,7 +1193,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) * is synchronized. */ bPoseChannel *pchan; /* Make sure we clean up the old data before overwriting it */ - pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name); + pchan = BKE_pose_channel_ensure(obedit->pose, ebone_iter->temp.ebone->name); BKE_pose_channel_free(pchan); /* Sync pchan data */ copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit); diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 93d36abe792..f7b54b79601 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -33,6 +33,7 @@ #include "DNA_armature_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_vec_types.h" #include "BKE_fcurve.h" #include "BKE_nla.h" @@ -41,6 +42,7 @@ #include "BKE_layer.h" #include "BKE_object.h" #include "BKE_report.h" +#include "BKE_screen.h" #include "BKE_unit.h" #include "RNA_access.h" @@ -50,15 +52,28 @@ #include "WM_types.h" #include "UI_interface.h" +#include "UI_resources.h" #include "ED_armature.h" #include "ED_keyframes_draw.h" #include "ED_markers.h" #include "ED_numinput.h" #include "ED_screen.h" +#include "ED_space_api.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" #include "armature_intern.h" +#include "BLF_api.h" + +/* Pixel distance from 0% to 100%. */ +#define SLIDE_PIXEL_DISTANCE (300 * U.pixelsize) +#define OVERSHOOT_RANGE_DELTA 0.2f + /* **************************************************** */ /* == POSE 'SLIDING' TOOLS == * @@ -110,15 +125,36 @@ typedef struct tPoseSlideOp { /** unused for now, but can later get used for storing runtime settings.... */ short flag; + /* Store overlay settings when invoking the operator. Bones will be temporarily hidden. */ + int overlay_flag; + /** which transforms/channels are affected (ePoseSlide_Channels) */ short channels; /** axis-limits for transforms (ePoseSlide_AxisLock) */ short axislock; - /** 0-1 value for determining the influence of whatever is relevant */ + /* Allow overshoot or clamp between 0% and 100%. */ + bool overshoot; + + /* Reduces percentage delta from mouse movement. */ + bool precision; + + /* Move percentage in 10% steps. */ + bool increments; + + /* Draw callback handler. */ + void *draw_handle; + + /* Accumulative, unclamped and unrounded percentage. */ + float raw_percentage; + + /* 0-1 value for determining the influence of whatever is relevant. */ float percentage; - /** numeric input */ + /* Last cursor position in screen space used for mouse movement delta calculation. */ + int last_cursor_x; + + /* Numeric input. */ NumInput num; struct tPoseSlideObject *ob_data_array; @@ -187,6 +223,240 @@ static const EnumPropertyItem prop_axis_lock_types[] = { /* ------------------------------------ */ +static void draw_overshoot_triangle(const uint8_t color[4], + const bool facing_right, + const float x, + const float y) +{ + const uint shdr_pos_2d = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); + GPU_polygon_smooth(true); + immUniformColor3ubvAlpha(color, 225); + const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize; + const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize; + + immBegin(GPU_PRIM_TRIS, 3); + immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y); + immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2); + immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2); + immEnd(); + + GPU_polygon_smooth(false); + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); +} + +static void draw_ticks(const float start_percentage, + const float end_percentage, + const struct vec2f line_start, + const float base_tick_height, + const float line_width, + const uint8_t color_overshoot[4], + const uint8_t color_line[4]) +{ + /* Use percentage represented as 0-100 int to avoid floating point precision problems. */ + const int tick_increment = 10; + + /* Round initial_tick_percentage up to the next tick_increment. */ + int tick_percentage = ceil((start_percentage * 100) / tick_increment) * tick_increment; + float tick_height = base_tick_height; + + while (tick_percentage <= (int)(end_percentage * 100)) { + /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit + * smaller and the rest is the minimum size. */ + if (tick_percentage % 100 == 0) { + tick_height = base_tick_height; + } + else if (tick_percentage % 50 == 0) { + tick_height = base_tick_height * 0.8; + } + else { + tick_height = base_tick_height * 0.5; + } + + const float x = line_start.x + + (((float)tick_percentage / 100) - start_percentage) * SLIDE_PIXEL_DISTANCE; + const struct rctf tick_rect = {.xmin = x - (line_width / 2), + .xmax = x + (line_width / 2), + .ymin = line_start.y - (tick_height / 2), + .ymax = line_start.y + (tick_height / 2)}; + + if (tick_percentage < 0 || tick_percentage > 100) { + UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255); + } + else { + UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255); + } + tick_percentage += tick_increment; + } +} + +static void draw_main_line(const struct rctf main_line_rect, + const float percentage, + const bool overshoot, + const uint8_t color_overshoot[4], + const uint8_t color_line[4]) +{ + if (overshoot) { + /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */ + const float line_zero_percent = main_line_rect.xmin - + ((percentage - 0.5f - OVERSHOOT_RANGE_DELTA) * + SLIDE_PIXEL_DISTANCE); + + const float clamped_line_zero_percent = clamp_f( + line_zero_percent, main_line_rect.xmin, main_line_rect.xmax); + const float clamped_line_hundred_percent = clamp_f( + line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect.xmin, main_line_rect.xmax); + + const struct rctf left_overshoot_line_rect = {.xmin = main_line_rect.xmin, + .xmax = clamped_line_zero_percent, + .ymin = main_line_rect.ymin, + .ymax = main_line_rect.ymax}; + const struct rctf right_overshoot_line_rect = {.xmin = clamped_line_hundred_percent, + .xmax = main_line_rect.xmax, + .ymin = main_line_rect.ymin, + .ymax = main_line_rect.ymax}; + UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255); + UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255); + + const struct rctf non_overshoot_line_rect = {.xmin = clamped_line_zero_percent, + .xmax = clamped_line_hundred_percent, + .ymin = main_line_rect.ymin, + .ymax = main_line_rect.ymax}; + UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255); + } + else { + UI_draw_roundbox_3ub_alpha(&main_line_rect, true, 0, color_line, 255); + } +} + +static void draw_backdrop(const int fontid, + const struct rctf main_line_rect, + const float color_bg[4], + const short region_y_size, + const float base_tick_height) +{ + float string_pixel_size[2]; + const char *percentage_placeholder = "000%%"; + BLF_width_and_height(fontid, + percentage_placeholder, + sizeof(percentage_placeholder), + &string_pixel_size[0], + &string_pixel_size[1]); + const struct vec2f pad = {.x = (region_y_size - base_tick_height) / 2, .y = 2.0f * U.pixelsize}; + const struct rctf backdrop_rect = {.xmin = main_line_rect.xmin - string_pixel_size[0] - pad.x, + .xmax = main_line_rect.xmax + pad.x, + .ymin = pad.y, + .ymax = region_y_size - pad.y}; + UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); +} + +/* Draw an on screen Slider for a Pose Slide Operator. */ +static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg) +{ + tPoseSlideOp *pso = arg; + + /* Only draw in region from which the Operator was started. */ + if (region != pso->region) { + return; + } + + uint8_t color_text[4]; + uint8_t color_line[4]; + uint8_t color_handle[4]; + uint8_t color_overshoot[4]; + float color_bg[4]; + + /* Get theme colors. */ + UI_GetThemeColor4ubv(TH_TEXT, color_text); + UI_GetThemeColor4ubv(TH_TEXT, color_line); + UI_GetThemeColor4ubv(TH_TEXT, color_overshoot); + UI_GetThemeColor4ubv(TH_ACTIVE, color_handle); + UI_GetThemeColor3fv(TH_BACK, color_bg); + + color_bg[3] = 0.5f; + color_overshoot[0] = color_overshoot[0] * 0.7; + color_overshoot[1] = color_overshoot[1] * 0.7; + color_overshoot[2] = color_overshoot[2] * 0.7; + + /* Get the default font. */ + const uiStyle *style = UI_style_get(); + const uiFontStyle *fstyle = &style->widget; + const int fontid = fstyle->uifont_id; + BLF_color3ubv(fontid, color_text); + BLF_rotation(fontid, 0.0f); + + const float line_width = 1.5 * U.pixelsize; + const float base_tick_height = 12.0 * U.pixelsize; + const float line_y = region->winy / 2; + + struct rctf main_line_rect = {.xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2), + .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2), + .ymin = line_y - line_width / 2, + .ymax = line_y + line_width / 2}; + float line_start_percentage = 0; + int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->percentage; + + if (pso->overshoot) { + main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; + main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; + line_start_percentage = pso->percentage - 0.5f - OVERSHOOT_RANGE_DELTA; + handle_pos_x = region->winx / 2; + } + + draw_backdrop(fontid, main_line_rect, color_bg, pso->region->winy, base_tick_height); + + draw_main_line(main_line_rect, pso->percentage, pso->overshoot, color_overshoot, color_line); + + const float percentage_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1; + const struct vec2f line_start_position = {.x = main_line_rect.xmin, .y = line_y}; + draw_ticks(line_start_percentage, + line_start_percentage + percentage_range, + line_start_position, + base_tick_height, + line_width, + color_overshoot, + color_line); + + /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100% + * range.*/ + if (pso->overshoot) { + if (pso->percentage > 1 + OVERSHOOT_RANGE_DELTA + 0.5) { + draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y); + } + if (pso->percentage < 0 - OVERSHOOT_RANGE_DELTA - 0.5) { + draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y); + } + } + + char percentage_string[256]; + + /* Draw handle indicating current percentage. */ + const struct rctf handle_rect = {.xmin = handle_pos_x - (line_width), + .xmax = handle_pos_x + (line_width), + .ymin = line_y - (base_tick_height / 2), + .ymax = line_y + (base_tick_height / 2)}; + + UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255); + BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->percentage * 100); + + /* Draw percentage string. */ + float percentage_pixel_size[2]; + BLF_width_and_height(fontid, + percentage_string, + sizeof(percentage_string), + &percentage_pixel_size[0], + &percentage_pixel_size[1]); + + BLF_position(fontid, + main_line_rect.xmin - 24.0 * U.pixelsize - percentage_pixel_size[0] / 2, + (region->winy / 2) - percentage_pixel_size[1] / 2, + 0.0f); + BLF_draw(fontid, percentage_string, sizeof(percentage_string)); +} + /* operator init */ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) { @@ -205,6 +475,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /* set range info from property values - these may get overridden for the invoke() */ pso->percentage = RNA_float_get(op->ptr, "percentage"); + pso->raw_percentage = pso->percentage; pso->prevFrame = RNA_int_get(op->ptr, "prev_frame"); pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); @@ -257,6 +528,14 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) pso->num.val_flag[0] |= NUM_NO_NEGATIVE; pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */ + /* Register UI drawing callback. */ + ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER); + if (region_header != NULL) { + pso->region = region_header; + pso->draw_handle = ED_region_draw_cb_activate( + region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL); + } + /* return status is whether we've got all the data we were requested to get */ return 1; } @@ -266,6 +545,13 @@ static void pose_slide_exit(wmOperator *op) { tPoseSlideOp *pso = op->customdata; + /* Hide Bone Overlay. */ + View3D *v3d = pso->area->spacedata.first; + v3d->overlay.flag = pso->overlay_flag; + + /* Remove UI drawing callback. */ + ED_region_draw_cb_exit(pso->region->type, pso->draw_handle); + /* if data exists, clear its data and exit */ if (pso) { /* free the temp pchan links and their data */ @@ -602,7 +888,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], float default_value) { - /* We only slide to the rest pose. So only use the default rest pose value */ + /* We only slide to the rest pose. So only use the default rest pose value. */ const int lock = pso->axislock; for (int idx = 0; idx < 3; idx++) { if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) || @@ -621,7 +907,7 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4], bool quat) { - /* We only slide to the rest pose. So only use the default rest pose value */ + /* We only slide to the rest pose. So only use the default rest pose value. */ float default_values[] = {1.0f, 0.0f, 0.0f, 0.0f}; if (!quat) { /* Axis Angle */ @@ -789,14 +1075,18 @@ static void pose_slide_reset(tPoseSlideOp *pso) /* ------------------------------------ */ -/* draw percentage indicator in header */ +/* Draw percentage indicator in workspace footer. */ /* TODO: Include hints about locks here... */ -static void pose_slide_draw_status(tPoseSlideOp *pso) +static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) { char status_str[UI_MAX_DRAW_STR]; char limits_str[UI_MAX_DRAW_STR]; char axis_str[50]; char mode_str[32]; + char overshoot_str[50]; + char precision_str[50]; + char increments_str[50]; + char bone_vis_str[50]; switch (pso->mode) { case POSESLIDE_PUSH: @@ -871,25 +1161,51 @@ static void pose_slide_draw_status(tPoseSlideOp *pso) break; } + if (pso->overshoot) { + BLI_strncpy(overshoot_str, TIP_("[E] - Disable overshoot"), sizeof(overshoot_str)); + } + else { + BLI_strncpy(overshoot_str, TIP_("E - Enable overshoot"), sizeof(overshoot_str)); + } + + if (pso->precision) { + BLI_strncpy(precision_str, TIP_("[Shift] - Precision active"), sizeof(precision_str)); + } + else { + BLI_strncpy(precision_str, TIP_("Shift - Hold for precision"), sizeof(precision_str)); + } + + if (pso->increments) { + BLI_strncpy(increments_str, TIP_("[Ctrl] - Increments active"), sizeof(increments_str)); + } + else { + BLI_strncpy(increments_str, TIP_("Ctrl - Hold for 10% increments"), sizeof(increments_str)); + } + + BLI_strncpy(bone_vis_str, TIP_("[H] - Toggle bone visibility"), sizeof(increments_str)); + if (hasNumInput(&pso->num)) { Scene *scene = pso->scene; - char str_ofs[NUM_STR_REP_LEN]; + char str_offs[NUM_STR_REP_LEN]; - outputNumInput(&pso->num, str_ofs, &scene->unit); + outputNumInput(&pso->num, str_offs, &scene->unit); - BLI_snprintf( - status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_ofs, limits_str); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str); } else { BLI_snprintf(status_str, sizeof(status_str), - "%s: %d %% | %s", + "%s: %s | %s | %s | %s | %s", mode_str, - (int)(pso->percentage * 100.0f), - limits_str); + limits_str, + overshoot_str, + precision_str, + increments_str, + bone_vis_str); } - ED_area_status_text(pso->area, status_str); + ED_workspace_status_text(C, status_str); + ED_area_status_text(pso->area, ""); } /* common code for invoke() methods */ @@ -975,21 +1291,40 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL); /* header print */ - pose_slide_draw_status(pso); + pose_slide_draw_status(C, pso); /* add a modal handler for this operator */ WM_event_add_modal_handler(C, op); + + /* Hide Bone Overlay. */ + View3D *v3d = pso->area->spacedata.first; + pso->overlay_flag = v3d->overlay.flag; + v3d->overlay.flag |= V3D_OVERLAY_HIDE_BONES; + return OPERATOR_RUNNING_MODAL; } -/* calculate percentage based on position of mouse (we only use x-axis for now. - * since this is more convenient for users to do), and store new percentage value +/* Calculate percentage based on mouse movement, clamp or round to increments if + * enabled by the user. Store the new percentage value. */ static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event) { - pso->percentage = (event->x - pso->region->winrct.xmin) / ((float)pso->region->winx); + const float percentage_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE)); + /* Reduced percentage delta in precision mode (shift held). */ + pso->raw_percentage += pso->precision ? (percentage_delta / 8) : percentage_delta; + pso->percentage = pso->raw_percentage; + pso->last_cursor_x = event->x; + + if (!pso->overshoot) { + pso->percentage = clamp_f(pso->percentage, 0, 1); + } + + if (pso->increments) { + pso->percentage = round(pso->percentage * 10) / 10; + } + RNA_float_set(op->ptr, "percentage", pso->percentage); } @@ -1056,9 +1391,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) case EVT_PADENTER: { if (event->val == KM_PRESS) { /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); ED_area_status_text(pso->area, NULL); WM_cursor_modal_restore(win); + /* Depsgraph updates + redraws. Redraw needed to remove UI. */ + pose_slide_refresh(C, pso); + /* insert keyframes as required... */ pose_slide_autoKeyframe(C, pso); pose_slide_exit(op); @@ -1073,13 +1412,14 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) case RIGHTMOUSE: { if (event->val == KM_PRESS) { /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); ED_area_status_text(pso->area, NULL); WM_cursor_modal_restore(win); /* reset transforms back to original state */ pose_slide_reset(pso); - /* depsgraph updates + redraws */ + /* Depsgraph updates + redraws.*/ pose_slide_refresh(C, pso); /* clean up temp data */ @@ -1178,10 +1518,58 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } + /* Overshoot. */ + case EVT_EKEY: { + pso->overshoot = !pso->overshoot; + do_pose_update = true; + break; + } + + /* Precision mode. */ + case EVT_LEFTSHIFTKEY: + case EVT_RIGHTSHIFTKEY: { + pso->precision = true; + do_pose_update = true; + break; + } + + /* Increments mode. */ + case EVT_LEFTCTRLKEY: + case EVT_RIGHTCTRLKEY: { + pso->increments = true; + do_pose_update = true; + break; + } + + /* Toggle Bone visibility. */ + case EVT_HKEY: { + View3D *v3d = pso->area->spacedata.first; + v3d->overlay.flag ^= V3D_OVERLAY_HIDE_BONES; + } + default: /* Some other unhandled key... */ break; } } + /* Precision and stepping only active while button is held. */ + else if (event->val == KM_RELEASE) { + switch (event->type) { + case EVT_LEFTSHIFTKEY: + case EVT_RIGHTSHIFTKEY: { + pso->precision = false; + do_pose_update = true; + break; + } + case EVT_LEFTCTRLKEY: + case EVT_RIGHTCTRLKEY: { + pso->increments = false; + do_pose_update = true; + break; + } + default: + break; + } + } else { /* unhandled event - maybe it was some view manipulation? */ /* allow to pass through */ @@ -1193,8 +1581,10 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Perform pose updates - in response to some user action * (e.g. pressing a key or moving the mouse). */ if (do_pose_update) { + pose_slide_mouse_update_percentage(pso, op, event); + /* update percentage indicator in header */ - pose_slide_draw_status(pso); + pose_slide_draw_status(C, pso); /* reset transforms (to avoid accumulation errors) */ pose_slide_reset(pso); @@ -1247,11 +1637,11 @@ static void pose_slide_opdef_properties(wmOperatorType *ot) PropertyRNA *prop; prop = RNA_def_float_factor(ot->srna, - "factor", + "percentage", 0.5f, 0.0f, 1.0f, - "Factor", + "Percentage", "Weighting factor for which keyframe is favored more", 0.0, 1.0); @@ -1310,6 +1700,8 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1349,7 +1741,7 @@ void POSE_OT_push(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); @@ -1370,6 +1762,8 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1409,7 +1803,7 @@ void POSE_OT_relax(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); @@ -1429,6 +1823,8 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1468,7 +1864,7 @@ void POSE_OT_push_rest(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); @@ -1489,6 +1885,8 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1528,7 +1926,7 @@ void POSE_OT_relax_rest(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); @@ -1549,6 +1947,8 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1588,7 +1988,7 @@ void POSE_OT_breakdown(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 767d822aa39..535ccaa06fd 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5576,7 +5576,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL, - .use_object_edit_cage = false, + .edit_mode_type = SNAP_GEOM_FINAL, }, mval, NULL, diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index 364444f99ae..68322ed56af 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -292,7 +292,7 @@ static int gizmo_move_modal(bContext *C, (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE), &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_EDIT, .use_occlusion_test = true, }, mval_fl, diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 3cff07dde3f..b2d3a2e1576 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -68,7 +68,9 @@ typedef struct SnapGizmo3D { struct { int x; int y; +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK short shift, ctrl, alt, oskey; +#endif } last_eventstate; #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK @@ -99,28 +101,30 @@ static bool eventstate_has_changed(SnapGizmo3D *snap_gizmo, const wmWindowManage if (wm && wm->winactive) { const wmEvent *event = wm->winactive->eventstate; if ((event->x != snap_gizmo->last_eventstate.x) || - (event->y != snap_gizmo->last_eventstate.y) || - (event->ctrl != snap_gizmo->last_eventstate.ctrl) || - (event->shift != snap_gizmo->last_eventstate.shift) || - (event->alt != snap_gizmo->last_eventstate.alt) || - (event->oskey != snap_gizmo->last_eventstate.oskey)) { + (event->y != snap_gizmo->last_eventstate.y)) { return true; } +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) { + if ((event->ctrl != snap_gizmo->last_eventstate.ctrl) || + (event->shift != snap_gizmo->last_eventstate.shift) || + (event->alt != snap_gizmo->last_eventstate.alt) || + (event->oskey != snap_gizmo->last_eventstate.oskey)) { + return true; + } + } +#endif } return false; } /* Copies the current eventstate. */ -static void eventstate_save(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) +static void eventstate_save_xy(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) { if (wm && wm->winactive) { const wmEvent *event = wm->winactive->eventstate; snap_gizmo->last_eventstate.x = event->x; snap_gizmo->last_eventstate.y = event->y; - snap_gizmo->last_eventstate.ctrl = event->ctrl; - snap_gizmo->last_eventstate.shift = event->shift; - snap_gizmo->last_eventstate.alt = event->alt; - snap_gizmo->last_eventstate.oskey = event->oskey; } } @@ -140,6 +144,12 @@ static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) return snap_gizmo->invert_snap; } + /* Save new eventstate. */ + snap_gizmo->last_eventstate.ctrl = event->ctrl; + snap_gizmo->last_eventstate.shift = event->shift; + snap_gizmo->last_eventstate.alt = event->alt; + snap_gizmo->last_eventstate.oskey = event->oskey; + const int snap_on = snap_gizmo->snap_on; wmKeyMap *keymap = WM_keymap_active(wm, snap_gizmo->keymap); @@ -328,23 +338,17 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz, Scene *scene = DEG_get_input_scene(depsgraph); #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if ((snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE) == 0) { - bool invert_snap_toggle = invert_snap(snap_gizmo, wm); - if (invert_snap_toggle != snap_gizmo->invert_snap) { - snap_gizmo->invert_snap = invert_snap_toggle; - - /* Status has changed, be sure to save before early return. */ - eventstate_save(snap_gizmo, wm); - } + if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) { + snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm); const ToolSettings *ts = scene->toolsettings; - if (invert_snap_toggle != !(ts->snap_flag & SCE_SNAP)) { + if (snap_gizmo->invert_snap != !(ts->snap_flag & SCE_SNAP)) { snap_gizmo->snap_elem = 0; return 0; } } #endif - eventstate_save(snap_gizmo, wm); + eventstate_save_xy(snap_gizmo, wm); snap_gizmo->is_enabled = true; @@ -364,25 +368,39 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz, snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; } + eSnapSelect snap_select = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_ONLY_ACTIVE) ? + SNAP_ONLY_ACTIVE : + SNAP_ALL; + + eSnapEditType edit_mode_type = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL) ? + SNAP_GEOM_FINAL : + (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ? + SNAP_GEOM_CAGE : + SNAP_GEOM_EDIT; + + bool use_occlusion_test = (snap_gizmo->flag & ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE) ? false : + true; + float dist_px = 12.0f * U.pixelsize; ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, gz); - snap_elem = ED_transform_snap_object_project_view3d_ex(snap_gizmo->snap_context_v3d, - depsgraph, - snap_elements, - &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, - .use_object_edit_cage = true, - .use_occlusion_test = true, - }, - mval_fl, - prev_co, - &dist_px, - co, - no, - &index, - NULL, - NULL); + snap_elem = ED_transform_snap_object_project_view3d_ex( + snap_gizmo->snap_context_v3d, + depsgraph, + snap_elements, + &(const struct SnapObjectParams){ + .snap_select = snap_select, + .edit_mode_type = edit_mode_type, + .use_occlusion_test = use_occlusion_test, + }, + mval_fl, + prev_co, + &dist_px, + co, + no, + &index, + NULL, + NULL); } if (snap_elem == 0) { @@ -596,8 +614,8 @@ static void snap_gizmo_draw(const bContext *C, wmGizmo *gz) GPU_line_width(1.0f); - const float *prev_point = snap_gizmo_snap_elements(snap_gizmo) & - SCE_SNAP_MODE_EDGE_PERPENDICULAR ? + const float *prev_point = (snap_gizmo_snap_elements(snap_gizmo) & + SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? snap_gizmo->prevpoint : NULL; diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 47ae90acb74..f7ab72a8a43 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -36,6 +36,7 @@ set(SRC annotate_paint.c drawgpencil.c editaction_gpencil.c + gpencil_add_blank.c gpencil_add_lineart.c gpencil_add_monkey.c gpencil_add_stroke.c diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 5c40bc8e418..c155587e95a 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1548,7 +1548,7 @@ static void annotation_paint_initstroke(tGPsdata *p, if (p->gpl == NULL) { /* tag for annotations */ p->gpd->flag |= GP_DATA_ANNOTATIONS; - p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true, false); if (p->custom_color[3]) { copy_v3_v3(p->gpl->color, p->custom_color); diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c new file mode 100644 index 00000000000..3aa16e54597 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_add_blank.c @@ -0,0 +1,101 @@ +/* + * 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. + * + * The Original Code is Copyright (C) 2017 Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup edgpencil + */ + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" + +#include "ED_gpencil.h" + +/* Definition of the most important info from a color */ +typedef struct ColorTemplate { + const char *name; + float line[4]; + float fill[4]; +} ColorTemplate; + +/* Add color an ensure duplications (matched by name) */ +static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct) +{ + int index; + Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index); + + copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); + + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba); + + return index; +} + +/* ***************************************************************** */ +/* Stroke Geometry */ + +/* ***************************************************************** */ +/* Color Data */ + +static const ColorTemplate gp_stroke_material_black = { + "Black", + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, +}; + +/* ***************************************************************** */ +/* Blank API */ + +/* Add a Simple empty object with one layer and one color. */ +void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4])) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + bGPdata *gpd = (bGPdata *)ob->data; + + /* create colors */ + int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black); + + /* set first color as active and in brushes */ + ob->actcol = color_black + 1; + + /* layers */ + bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false); + + /* frames */ + BKE_gpencil_frame_addnew(layer, CFRA); + + /* update depsgraph */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; +} diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c index 6b28c6ec13e..ac0da0ad1db 100644 --- a/source/blender/editors/gpencil/gpencil_add_lineart.c +++ b/source/blender/editors/gpencil/gpencil_add_lineart.c @@ -96,7 +96,7 @@ void ED_gpencil_create_lineart(bContext *C, Object *ob) ob->actcol = color_black + 1; /* layers */ - bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ BKE_gpencil_frame_addnew(lines, 0); diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index 4497d963c6d..d8734c4ae6b 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -837,8 +837,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4]) /* layers */ /* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */ - bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false); - bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false, false); + bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ /* NOTE: No need to check for existing, as this will take care of it for us */ diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index 26237636526..e95496b51ee 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -225,8 +225,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4]) ob->actcol = color_black + 1; /* layers */ - bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false); - bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false, false); + bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA); diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index ac75ae44c8a..8ab413e907c 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1857,7 +1857,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op) /* Add layer and frame. */ bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true); + bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false); bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA); done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask); diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index b269fd84d5f..d9a807d17ab 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -129,7 +129,7 @@ static int gpencil_data_add_exec(bContext *C, wmOperator *op) gpd->flag |= GP_DATA_ANNOTATIONS; /* add new layer (i.e. a "note") */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -231,7 +231,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op) /* mark as annotation */ (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false); gpd = *gpd_ptr; } else { @@ -239,7 +239,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); if ((ob != NULL) && (ob->type == OB_GPENCIL)) { gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); /* Add a new frame to make it visible in Dopesheet. */ if (gpl != NULL) { gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); @@ -524,7 +524,6 @@ enum { static bool gpencil_layer_duplicate_object_poll(bContext *C) { - ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = CTX_data_active_object(C); if ((ob == NULL) || (ob->type != OB_GPENCIL)) { return false; @@ -537,90 +536,75 @@ static bool gpencil_layer_duplicate_object_poll(bContext *C) return false; } - /* check there are more grease pencil objects */ - LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - if ((base->object != ob) && (base->object->type == OB_GPENCIL)) { - return true; - } - } - - return false; + return true; } static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "object", name); - - if (name[0] == '\0') { - return OPERATOR_CANCELLED; - } - - Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name); - - int mode = RNA_enum_get(op->ptr, "mode"); + const bool only_active = RNA_boolean_get(op->ptr, "only_active"); + const int mode = RNA_enum_get(op->ptr, "mode"); Object *ob_src = CTX_data_active_object(C); bGPdata *gpd_src = (bGPdata *)ob_src->data; - bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src); + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd_src); - /* Sanity checks. */ - if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) { - return OPERATOR_CANCELLED; - } - /* Cannot copy itself and check destination type. */ - if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) { - return OPERATOR_CANCELLED; - } - - bGPdata *gpd_dst = (bGPdata *)ob_dst->data; - - /* Create new layer. */ - bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true); - /* Need to copy some variables (not all). */ - gpl_dst->onion_flag = gpl_src->onion_flag; - gpl_dst->thickness = gpl_src->thickness; - gpl_dst->line_change = gpl_src->line_change; - copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor); - gpl_dst->opacity = gpl_src->opacity; - - /* Create all frames. */ - LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { - - if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if ((ob == ob_src) || (ob->type != OB_GPENCIL)) { continue; } + bGPdata *gpd_dst = (bGPdata *)ob->data; + LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl_src, &gpd_src->layers) { + if ((only_active) && (gpl_src != gpl_active)) { + continue; + } + /* Create new layer (adding at head of the list). */ + bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true, true); + /* Need to copy some variables (not all). */ + gpl_dst->onion_flag = gpl_src->onion_flag; + gpl_dst->thickness = gpl_src->thickness; + gpl_dst->line_change = gpl_src->line_change; + copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor); + gpl_dst->opacity = gpl_src->opacity; + + /* Create all frames. */ + LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { + + if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { + continue; + } - /* Create new frame. */ - bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum); + /* Create new frame. */ + bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum); - /* Copy strokes. */ - LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { + /* Copy strokes. */ + LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { - /* Make copy of source stroke. */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true); + /* Make copy of source stroke. */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true); - /* Check if material is in destination object, - * otherwise add the slot with the material. */ - Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1); - if (ma_src != NULL) { - int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); + /* Check if material is in destination object, + * otherwise add the slot with the material. */ + Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1); + if (ma_src != NULL) { + int idx = BKE_gpencil_object_material_ensure(bmain, ob, ma_src); - /* Reassign the stroke material to the right slot in destination object. */ - gps_dst->mat_nr = idx; - } + /* Reassign the stroke material to the right slot in destination object. */ + gps_dst->mat_nr = idx; + } - /* Add new stroke to frame. */ - BLI_addtail(&gpf_dst->strokes, gps_dst); + /* Add new stroke to frame. */ + BLI_addtail(&gpf_dst->strokes, gps_dst); + } + } } + /* notifiers */ + DEG_id_tag_update(&gpd_dst->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } + CTX_DATA_END; - /* notifiers */ - DEG_id_tag_update(&gpd_dst->id, - ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -628,6 +612,8 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op) void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) { + PropertyRNA *prop; + static const EnumPropertyItem copy_mode[] = { {GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""}, {GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""}, @@ -637,7 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) /* identifiers */ ot->name = "Duplicate Layer to New Object"; ot->idname = "GPENCIL_OT_layer_duplicate_object"; - ot->description = "Make a copy of the active Grease Pencil layer to new object"; + ot->description = "Make a copy of the active Grease Pencil layer to selected object"; /* callbacks */ ot->exec = gpencil_layer_duplicate_object_exec; @@ -646,11 +632,14 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_string( - ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object"); - RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->prop = RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", ""); - RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", ""); + prop = RNA_def_boolean(ot->srna, + "only_active", + true, + "Only Active", + "Copy only active Layer, uncheck to append all layers"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* ********************* Duplicate Frame ************************** */ @@ -1443,7 +1432,7 @@ static int gpencil_layer_change_exec(bContext *C, wmOperator *op) /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); } else { /* Try to get layer */ @@ -3589,6 +3578,79 @@ void GPENCIL_OT_set_active_material(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ********************* Append Materials in a new object ************************** */ +static bool gpencil_materials_copy_to_object_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + short *totcolp = BKE_object_material_len_p(ob); + if (*totcolp == 0) { + return false; + } + + return true; +} + +static int gpencil_materials_copy_to_object_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + const bool only_active = RNA_boolean_get(op->ptr, "only_active"); + Object *ob_src = CTX_data_active_object(C); + Material *ma_active = BKE_gpencil_material(ob_src, ob_src->actcol); + + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if ((ob == ob_src) || (ob->type != OB_GPENCIL)) { + continue; + } + /* Duplicate materials. */ + for (int i = 0; i < ob_src->totcol; i++) { + Material *ma_src = BKE_object_material_get(ob_src, i + 1); + if (only_active && ma_src != ma_active) { + continue; + } + + if (ma_src != NULL) { + BKE_gpencil_object_material_ensure(bmain, ob, ma_src); + } + } + + /* notifiers */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Copy Materials to Selected Object"; + ot->idname = "GPENCIL_OT_materials_copy_to_object"; + ot->description = "Append Materials of the active Grease Pencil to other object"; + + /* callbacks */ + ot->exec = gpencil_materials_copy_to_object_exec; + ot->poll = gpencil_materials_copy_to_object_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_boolean(ot->srna, + "only_active", + true, + "Only Active", + "Append only active material, uncheck to append all materials"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + /* Parent GPencil object to Lattice */ bool ED_gpencil_add_lattice_modifier(const bContext *C, ReportList *reports, @@ -3745,3 +3807,51 @@ void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot) ot->exec = gpencil_layer_mask_remove_exec; ot->poll = gpencil_active_layer_poll; } + +static int gpencil_layer_mask_move_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + const int direction = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) { + return OPERATOR_CANCELLED; + } + if (gpl->act_mask > 0) { + bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1); + if (mask != NULL) { + BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ + if (BLI_listbase_link_move(&gpl->mask_layers, mask, direction)) { + gpl->act_mask += direction; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + } + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_mask_move(wmOperatorType *ot) +{ + static const EnumPropertyItem slot_move[] = { + {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""}, + {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Grease Pencil Layer Mask"; + ot->idname = "GPENCIL_OT_layer_mask_move"; + ot->description = "Move the active Grease Pencil mask layer up/down in the list"; + + /* api callbacks */ + ot->exec = gpencil_layer_mask_move_exec; + ot->poll = gpencil_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 66d50e2fd12..e65afa1abff 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1677,7 +1677,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op) if (gpl == NULL) { /* no active layer - let's just create one */ - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); } else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) { BKE_report( @@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; - Scene *scene = CTX_data_scene(C); bGPDlayer *target_layer = NULL; ListBase strokes = {NULL, NULL}; int layer_num = RNA_int_get(op->ptr, "layer"); const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - - /* if autolock enabled, disabled now */ + /* If autolock enabled, disabled now. */ if (use_autolock) { gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS; } @@ -1840,7 +1835,7 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) } else { /* Create a new layer. */ - target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true); + target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false); } if (target_layer == NULL) { @@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Extract all strokes to move to this layer - * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes - * getting repeatedly moved - */ - CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - - /* skip if no frame with strokes, or if this is the layer we're moving strokes to */ - if ((gpl == target_layer) || (gpf == NULL)) { + /* Extract all strokes to move to this layer. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) { + /* Skip if this is the layer we're moving strokes to. */ + if (gpl_src == target_layer) { continue; } + bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe; + for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) { + if ((gpf_src == gpl_src->actframe) || + ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf_src == NULL) { + continue; + } - /* make copies of selected strokes, and deselect these once we're done */ - LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + bGPDstroke *gpsn = NULL; + BLI_listbase_clear(&strokes); + for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) { + continue; + } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } + if (gps->flag & GP_STROKE_SELECT) { + BLI_remlink(&gpf_src->strokes, gps); + BLI_addtail(&strokes, gps); + } + } + /* Paste them all in one go. */ + if (strokes.first) { + bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get( + target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW); - /* Check if the color is editable. */ - if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { - continue; + BLI_movelisttolist(&gpf_dst->strokes, &strokes); + BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); + } } - - /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */ - if (gps->flag & GP_STROKE_SELECT) { - BLI_remlink(&gpf->strokes, gps); - BLI_addtail(&strokes, gps); + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } - - /* if new layer and autolock, lock old layer */ + /* If new layer and autolock, lock old layer. */ if ((layer_num == -1) && (use_autolock)) { - gpl->flag |= GP_LAYER_LOCKED; + gpl_src->flag |= GP_LAYER_LOCKED; } } CTX_DATA_END; - /* Paste them all in one go */ - if (strokes.first) { - bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW); - - BLI_movelisttolist(&gpf->strokes, &strokes); - BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); - } - /* back autolock status */ if (use_autolock) { gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; @@ -3762,6 +3763,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); const bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); /* Init snap context for geometry projection. */ SnapObjectContext *sctx = NULL; @@ -3774,36 +3776,55 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) int cfra_prv = INT_MIN; /* Go through each editable + selected stroke, adjusting each of its points one by one... */ - GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { - bool curve_select = false; - if (is_curve_edit && gps->editcurve != NULL) { - curve_select = gps->editcurve->flag & GP_CURVE_SELECT; - } + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - if (gps->flag & GP_STROKE_SELECT || curve_select) { + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { + continue; + } + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + bool curve_select = false; + if (is_curve_edit && gps->editcurve != NULL) { + curve_select = gps->editcurve->flag & GP_CURVE_SELECT; + } - /* update frame to get the new location of objects */ - if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) { - cfra_prv = gpf_->framenum; - CFRA = gpf_->framenum; - BKE_scene_graph_update_for_newframe(depsgraph); - } + if (gps->flag & GP_STROKE_SELECT || curve_select) { - ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original); + /* update frame to get the new location of objects */ + if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) { + cfra_prv = gpf->framenum; + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph); + } - if (is_curve_edit && gps->editcurve != NULL) { - BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps); - /* Update the selection from the stroke to the curve. */ - BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve); + ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original); - gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; - BKE_gpencil_stroke_geometry_update(gpd, gps); - } + if (is_curve_edit && gps->editcurve != NULL) { + BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps); + /* Update the selection from the stroke to the curve. */ + BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve); - changed = true; + gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + BKE_gpencil_stroke_geometry_update(gpd, gps); + } + + changed = true; + } + } + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } } } - GP_EDITABLE_STROKES_END(gpstroke_iter); + CTX_DATA_END; /* return frame state and DB to original state */ CFRA = oldframe; @@ -3832,7 +3853,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) "VIEW", 0, "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " + "Reproject the strokes to end up on the same plane, as if drawn from the current " + "viewpoint " "using 'Cursor' Stroke Placement"}, {GP_REPROJECT_SURFACE, "SURFACE", @@ -3851,7 +3873,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) ot->name = "Reproject Strokes"; ot->idname = "GPENCIL_OT_reproject"; ot->description = - "Reproject the selected strokes from the current viewpoint as if they had been newly drawn " + "Reproject the selected strokes from the current viewpoint as if they had been newly " + "drawn " "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, " "or for matching deforming geometry)"; @@ -4208,7 +4231,8 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) ot->name = "Subdivide Stroke"; ot->idname = "GPENCIL_OT_stroke_subdivide"; ot->description = - "Subdivide between continuous selected points of the stroke adding a point half way between " + "Subdivide between continuous selected points of the stroke adding a point half way " + "between " "them"; /* api callbacks */ @@ -4593,7 +4617,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) if (gps->flag & GP_STROKE_SELECT) { /* add layer if not created before */ if (gpl_dst == NULL) { - gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false); + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false); } /* add frame if not created before */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index bb0a2916d2d..f74e211dd65 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1247,6 +1247,7 @@ static bool dilate_shape(ImBuf *ibuf) static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) { ImBuf *ibuf; + Brush *brush = tgpf->brush; float rgba[4]; void *lock; int v[2]; @@ -1279,7 +1280,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) /* Dilate. */ if (dilate) { - dilate_shape(ibuf); + for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) { + dilate_shape(ibuf); + } } for (int idx = imagesize - 1; idx != 0; idx--) { @@ -1686,7 +1689,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op) tgpf->gpd = gpd; tgpf->gpl = BKE_gpencil_layer_active_get(gpd); if (tgpf->gpl == NULL) { - tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true); + tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true, false); } tgpf->lock_axis = ts->gp_sculpt.lock_axis; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index c6f74c39beb..9f48c81c8f1 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -421,6 +421,7 @@ void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot); void GPENCIL_OT_layer_mask_add(struct wmOperatorType *ot); void GPENCIL_OT_layer_mask_remove(struct wmOperatorType *ot); +void GPENCIL_OT_layer_mask_move(struct wmOperatorType *ot); void GPENCIL_OT_hide(struct wmOperatorType *ot); void GPENCIL_OT_reveal(struct wmOperatorType *ot); @@ -537,6 +538,7 @@ void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot); void GPENCIL_OT_material_select(struct wmOperatorType *ot); void GPENCIL_OT_material_set(struct wmOperatorType *ot); void GPENCIL_OT_set_active_material(struct wmOperatorType *ot); +void GPENCIL_OT_materials_copy_to_object(struct wmOperatorType *ot); /* convert old 2.7 files to 2.8 */ void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 7ca53779522..0062e363cdf 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -1434,36 +1434,32 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); uiLayout *col, *row; - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "step", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "step", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "layers", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "layers", 0, NULL, ICON_NONE); if (CTX_data_mode_enum(C) == CTX_MODE_EDIT_GPENCIL) { row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "interpolate_selected_only", 0, NULL, ICON_NONE); } row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "flip", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "flip", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "smooth_factor", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "smooth_steps", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "type", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE); if (type == GP_IPO_CURVEMAP) { /* Get an RNA pointer to ToolSettings to give to the custom curve. */ @@ -1477,16 +1473,16 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op) } else if (type != GP_IPO_LINEAR) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "easing", 0, NULL, ICON_NONE); if (type == GP_IPO_BACK) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "back", 0, NULL, ICON_NONE); } else if (type == GP_IPO_ELASTIC) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "amplitude", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "period", 0, NULL, ICON_NONE); } } } diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 1a6cb5670c4..698d784c3b0 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -601,6 +601,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_layer_mask_add); WM_operatortype_append(GPENCIL_OT_layer_mask_remove); + WM_operatortype_append(GPENCIL_OT_layer_mask_move); WM_operatortype_append(GPENCIL_OT_hide); WM_operatortype_append(GPENCIL_OT_reveal); @@ -650,6 +651,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_material_to_vertex_color); WM_operatortype_append(GPENCIL_OT_extract_palette_vertex); + WM_operatortype_append(GPENCIL_OT_materials_copy_to_object); WM_operatortype_append(GPENCIL_OT_transform_fill); WM_operatortype_append(GPENCIL_OT_reset_transform_fill); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 9bd929aa2af..30d85b09974 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -2120,7 +2120,7 @@ static void gpencil_paint_initstroke(tGPsdata *p, /* get active layer (or add a new one if non-existent) */ p->gpl = BKE_gpencil_layer_active_get(p->gpd); if (p->gpl == NULL) { - p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true, false); changed = true; if (p->custom_color[3]) { copy_v3_v3(p->gpl->color, p->custom_color); diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 3d20e32ed49..1b0a40b1be1 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -314,7 +314,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) /* if layer doesn't exist, create a new one */ if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true); + gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true, false); } tgpi->gpl = gpl; diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c index d2e5fa3db32..cd75f9313ad 100644 --- a/source/blender/editors/gpencil/gpencil_trace_ops.c +++ b/source/blender/editors/gpencil/gpencil_trace_ops.c @@ -207,7 +207,7 @@ static void trace_initialize_job_data(TraceJob *trace_job) trace_job->gpd = (bGPdata *)trace_job->ob_gpencil->data; trace_job->gpl = BKE_gpencil_layer_active_get(trace_job->gpd); if (trace_job->gpl == NULL) { - trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true); + trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true, false); } } diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 983ae94b637..8118e3c6c69 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -110,7 +110,7 @@ struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceF void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile); void ED_fileselect_params_to_userdef(struct SpaceFile *sfile, - const int temp_win_size[], + const int temp_win_size[2], const bool is_maximized); void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region); diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h index 58b2cb80bbe..571519e52f7 100644 --- a/source/blender/editors/include/ED_gizmo_library.h +++ b/source/blender/editors/include/ED_gizmo_library.h @@ -263,6 +263,11 @@ struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *sce typedef enum { ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE = 1 << 0, + ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE = 1 << 1, + ED_SNAPGIZMO_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */ + ED_SNAPGIZMO_SNAP_ONLY_ACTIVE = 1 << 3, + ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL = 1 << 4, + ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE = 1 << 5, } eSnapGizmo; void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index d4ff374dc38..bad080e1609 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -251,6 +251,7 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y); /* ----------- Add Primitive Utilities -------------- */ +void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_lineart(struct bContext *C, struct Object *ob); diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 12d6f1fce54..179c9d5b30d 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -511,6 +511,7 @@ bool ED_autokeyframe_property(struct bContext *C, #define ANIM_KS_ROTATION_ID "Rotation" #define ANIM_KS_SCALING_ID "Scaling" #define ANIM_KS_LOC_ROT_SCALE_ID "LocRotScale" +#define ANIM_KS_LOC_ROT_SCALE_CPROP_ID "LocRotScaleCProp" #define ANIM_KS_AVAILABLE_ID "Available" #define ANIM_KS_WHOLE_CHARACTER_ID "WholeCharacter" #define ANIM_KS_WHOLE_CHARACTER_SELECTED_ID "WholeCharacterSelected" diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 78c4c2a8eba..bdd7ec571dc 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -162,7 +162,7 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area); void ED_area_tag_redraw_regiontype(ScrArea *area, int type); void ED_area_tag_refresh(ScrArea *area); void ED_area_do_refresh(struct bContext *C, ScrArea *area); -struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[]); +struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[2]); void ED_area_status_text(ScrArea *area, const char *str); void ED_area_newspace(struct bContext *C, ScrArea *area, int type, const bool skip_region_exit); void ED_area_prevspace(struct bContext *C, ScrArea *area); @@ -200,8 +200,6 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); /* screens */ void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm); void ED_screen_draw_edges(struct wmWindow *win); -void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2); -void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac); void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win); void ED_screen_ensure_updated(struct wmWindowManager *wm, struct wmWindow *win, @@ -450,10 +448,10 @@ enum { }; /* SCREEN_OT_space_context_cycle direction */ -enum { +typedef enum eScreenCycle { SPACE_CONTEXT_CYCLE_PREV, SPACE_CONTEXT_CYCLE_NEXT, -}; +} eScreenCycle; #ifdef __cplusplus } diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index b7174964ef6..42e73bbf744 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -39,12 +39,19 @@ struct View3D; /* ED_transform_snap_object_*** API */ -typedef enum eSnapSelect { +typedef enum { SNAP_ALL = 0, SNAP_NOT_SELECTED = 1, SNAP_NOT_ACTIVE = 2, + SNAP_ONLY_ACTIVE = 3, } eSnapSelect; +typedef enum { + SNAP_GEOM_FINAL = 0, + SNAP_GEOM_CAGE = 1, + SNAP_GEOM_EDIT = 2, /* Bmesh for mesh-type. */ +} eSnapEditType; + /** used for storing multiple hits */ struct SnapObjectHitDepth { struct SnapObjectHitDepth *next, *prev; @@ -54,7 +61,7 @@ struct SnapObjectHitDepth { float no[3]; int index; - struct Object *ob; + struct Object *ob_eval; float obmat[4][4]; /* needed to tell which ray-cast this was part of, @@ -64,10 +71,10 @@ struct SnapObjectHitDepth { /** parameters that define which objects will be used to snap. */ struct SnapObjectParams { - /* special context sensitive handling for the active or selected object */ + /* Special context sensitive handling for the active or selected object. */ char snap_select; - /* use editmode cage */ - unsigned int use_object_edit_cage : 1; + /* Geometry for snapping in edit mode. */ + char edit_mode_type; /* snap to the closest element, use when using more than one snap type */ unsigned int use_occlusion_test : 1; /* exclude back facing geometry from snapping */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 81872428038..338b12f7985 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -186,17 +186,17 @@ enum { UI_RETURN_POPUP_OK = 1 << 5, }; -/* but->flag - general state flags. */ +/** #uiBut.flag general state flags. */ enum { - /** Warning, the first 6 flags are internal. */ - UI_BUT_ICON_SUBMENU = 1 << 6, - UI_BUT_ICON_PREVIEW = 1 << 7, + /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */ + UI_BUT_ICON_SUBMENU = 1 << 7, + UI_BUT_ICON_PREVIEW = 1 << 8, - UI_BUT_NODE_LINK = 1 << 8, - UI_BUT_NODE_ACTIVE = 1 << 9, - UI_BUT_DRAG_LOCK = 1 << 10, + UI_BUT_NODE_LINK = 1 << 9, + UI_BUT_NODE_ACTIVE = 1 << 10, + UI_BUT_DRAG_LOCK = 1 << 11, /** Grayed out and un-editable. */ - UI_BUT_DISABLED = 1 << 11, + UI_BUT_DISABLED = 1 << 12, UI_BUT_ANIMATED = 1 << 13, UI_BUT_ANIMATED_KEY = 1 << 14, @@ -2137,7 +2137,7 @@ void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name); -void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color); +void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float color[4]); void uiTemplateCacheFile(uiLayout *layout, const struct bContext *C, struct PointerRNA *ptr, @@ -2405,9 +2405,12 @@ void uiItemS_ex(uiLayout *layout, float factor); void uiItemSpacer(uiLayout *layout); void uiItemPopoverPanel_ptr( - uiLayout *layout, struct bContext *C, struct PanelType *pt, const char *name, int icon); -void uiItemPopoverPanel( - uiLayout *layout, struct bContext *C, const char *panel_type, const char *name, int icon); + uiLayout *layout, const struct bContext *C, struct PanelType *pt, const char *name, int icon); +void uiItemPopoverPanel(uiLayout *layout, + const struct bContext *C, + const char *panel_type, + const char *name, + int icon); void uiItemPopoverPanelFromGroup(uiLayout *layout, struct bContext *C, int space_id, diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 1820c2f133c..c99c7f681b3 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -451,7 +451,7 @@ int UI_ThemeMenuShadowWidth(void); /* only for buttons in theme editor! */ const unsigned char *UI_ThemeGetColorPtr(struct bTheme *btheme, int spacetype, int colorid); -void UI_make_axis_color(const unsigned char *src_col, unsigned char *dst_col, const char axis); +void UI_make_axis_color(const unsigned char src_col[3], unsigned char dst_col[3], const char axis); #ifdef __cplusplus } diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 90d604b3190..a31efefd99c 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2327,6 +2327,14 @@ bool ui_but_is_float(const uiBut *but) return false; } +PropertyScaleType ui_but_scale_type(const uiBut *but) +{ + if (but->rnaprop) { + return RNA_property_ui_scale(but->rnaprop); + } + return PROP_SCALE_LINEAR; +} + bool ui_but_is_bool(const uiBut *but) { if (ELEM(but->type, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index a5a5a69728e..282d470c7ea 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -126,6 +126,24 @@ #define UI_MAX_PASSWORD_STR 128 /** + * This is a lower limit on the soft minimum of the range. + * Usually the derived lower limit from the visible precision is higher, + * so this number is the backup minimum. + * + * Logarithmic scale does not work with a minimum value of zero, + * but we want to support it anyway. It is set to 0.5e... for + * correct rounding since when the tweaked value is lower than + * the log minimum (lower limit), it will snap to 0. + */ +#define UI_PROP_SCALE_LOG_MIN 0.5e-8f +/** + * This constant defines an offset for the precision change in + * snap rounding, when going to higher values. It is set to + * `0.5 - log10(3) = 0.03` to make the switch at `0.3` values. + */ +#define UI_PROP_SCALE_LOG_SNAP_OFFSET 0.03f + +/** * When #USER_CONTINUOUS_MOUSE is disabled or tablet input is used, * Use this as a maximum soft range for mapping cursor motion to the value. * Otherwise min/max of #FLT_MAX, #INT_MAX cause small adjustments to jump to large numbers. @@ -477,6 +495,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C, static void ui_do_but_extra_operator_icons_mousemove(uiBut *but, uiHandleButtonData *data, const wmEvent *event); +static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data); #ifdef USE_DRAG_MULTINUM static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block); @@ -1114,6 +1133,13 @@ static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data) static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data) { if (data->str) { + double value; + /* Check if the string value is a number and cancel if it's equal to the startvalue. */ + if (ui_but_string_eval_number(C, but, data->str, &value) && (value == data->startvalue)) { + data->cancel = true; + return; + } + if (ui_but_string_set(C, but, data->str)) { data->value = ui_but_value_get(but); } @@ -3354,6 +3380,8 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) if (is_num_but) { BLI_assert(data->is_str_dynamic == false); ui_but_convert_to_unit_alt_name(but, data->str, data->maxlen); + + ui_numedit_begin_set_values(but, data); } /* won't change from now on */ @@ -3890,6 +3918,14 @@ static void ui_do_but_textedit_select( /** \name Button Number Editing (various types) * \{ */ +static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data) +{ + data->startvalue = ui_but_value_get(but); + data->origvalue = data->startvalue; + data->value = data->origvalue; + but->editval = &data->value; +} + static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) { if (but->type == UI_BTYPE_CURVE) { @@ -3915,19 +3951,21 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) but->editvec = data->vec; } else { - float softrange, softmin, softmax; + ui_numedit_begin_set_values(but, data); - data->startvalue = ui_but_value_get(but); - data->origvalue = data->startvalue; - data->value = data->origvalue; - but->editval = &data->value; + float softmin = but->softmin; + float softmax = but->softmax; + float softrange = softmax - softmin; + const PropertyScaleType scale_type = ui_but_scale_type(but); - softmin = but->softmin; - softmax = but->softmax; - softrange = softmax - softmin; + float log_min = (scale_type == PROP_SCALE_LOG) ? max_ff(softmin, UI_PROP_SCALE_LOG_MIN) : 0.0f; if ((but->type == UI_BTYPE_NUM) && (ui_but_is_cursor_warp(but) == false)) { uiButNumber *number_but = (uiButNumber *)but; + + if (scale_type == PROP_SCALE_LOG) { + log_min = max_ff(log_min, powf(10, -number_but->precision) * 0.5f); + } /* Use a minimum so we have a predictable range, * otherwise some float buttons get a large range. */ const float value_step_float_min = 0.1f; @@ -3976,7 +4014,31 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) } } - data->dragfstart = (softrange == 0.0f) ? 0.0f : ((float)data->value - softmin) / softrange; + if (softrange == 0.0f) { + data->dragfstart = 0.0f; + } + else { + switch (scale_type) { + case PROP_SCALE_LINEAR: { + data->dragfstart = ((float)data->value - softmin) / softrange; + break; + } + case PROP_SCALE_LOG: { + BLI_assert(log_min != 0.0f); + const float base = softmax / log_min; + data->dragfstart = logf((float)data->value / log_min) / logf(base); + break; + } + case PROP_SCALE_CUBIC: { + const float cubic_min = cube_f(softmin); + const float cubic_max = cube_f(softmax); + const float cubic_range = cubic_max - cubic_min; + const float f = ((float)data->value - softmin) * cubic_range / softrange + cubic_min; + data->dragfstart = (cbrtf(f) - softmin) / softrange; + break; + } + } + } data->dragf = data->dragfstart; data->drag_map_soft_min = softmin; @@ -4694,6 +4756,7 @@ static float ui_numedit_apply_snapf( /* pass */ } else { + const PropertyScaleType scale_type = ui_but_scale_type(but); float softrange = softmax - softmin; float fac = 1.0f; @@ -4731,31 +4794,30 @@ static float ui_numedit_apply_snapf( } } - if (snap == SNAP_ON) { - if (softrange < 2.10f) { - tempf = roundf(tempf * 10.0f) * 0.1f; - } - else if (softrange < 21.0f) { - tempf = roundf(tempf); - } - else { - tempf = roundf(tempf * 0.1f) * 10.0f; - } - } - else if (snap == SNAP_ON_SMALL) { - if (softrange < 2.10f) { - tempf = roundf(tempf * 100.0f) * 0.01f; - } - else if (softrange < 21.0f) { - tempf = roundf(tempf * 10.0f) * 0.1f; + BLI_assert(ELEM(snap, SNAP_ON, SNAP_ON_SMALL)); + switch (scale_type) { + case PROP_SCALE_LINEAR: + case PROP_SCALE_CUBIC: { + const float snap_fac = (snap == SNAP_ON_SMALL ? 0.1f : 1.0f); + if (softrange < 2.10f) { + tempf = roundf(tempf * 10.0f / snap_fac) * 0.1f * snap_fac; + } + else if (softrange < 21.0f) { + tempf = roundf(tempf / snap_fac) * snap_fac; + } + else { + tempf = roundf(tempf * 0.1f / snap_fac) * 10.0f * snap_fac; + } + break; } - else { - tempf = roundf(tempf); + case PROP_SCALE_LOG: { + const float snap_fac = powf(10.0f, + roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - + (snap == SNAP_ON_SMALL ? 2.0f : 1.0f)); + tempf = roundf(tempf / snap_fac) * snap_fac; + break; } } - else { - BLI_assert(0); - } if (fac != 1.0f) { tempf *= fac; @@ -4800,6 +4862,7 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, int lvalue, temp; bool changed = false; const bool is_float = ui_but_is_float(but); + const PropertyScaleType scale_type = ui_but_scale_type(but); /* prevent unwanted drag adjustments, test motion so modifier keys refresh. */ if ((is_motion || data->draglock) && (ui_but_dragedit_update_mval(data, mx) == false)) { @@ -4811,21 +4874,74 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, const float softmax = but->softmax; const float softrange = softmax - softmin; + const float log_min = (scale_type == PROP_SCALE_LOG) ? + max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN), + powf(10, -number_but->precision) * 0.5f) : + 0; + /* Mouse location isn't screen clamped to the screen so use a linear mapping * 2px == 1-int, or 1px == 1-ClickStep */ if (is_float) { fac *= 0.01f * number_but->step_size; - tempf = (float)data->startvalue + ((float)(mx - data->dragstartx) * fac); + switch (scale_type) { + case PROP_SCALE_LINEAR: { + tempf = (float)data->startvalue + (float)(mx - data->dragstartx) * fac; + break; + } + case PROP_SCALE_LOG: { + const float startvalue = max_ff((float)data->startvalue, log_min); + tempf = expf((float)(mx - data->dragstartx) * fac) * startvalue; + if (tempf <= log_min) { + tempf = 0.0f; + } + break; + } + case PROP_SCALE_CUBIC: { + tempf = cbrtf((float)data->startvalue) + (float)(mx - data->dragstartx) * fac; + tempf *= tempf * tempf; + break; + } + } + tempf = ui_numedit_apply_snapf(but, tempf, softmin, softmax, snap); #if 1 /* fake moving the click start, nicer for dragging back after passing the limit */ - if (tempf < softmin) { - data->dragstartx -= (softmin - tempf) / fac; - tempf = softmin; - } - else if (tempf > softmax) { - data->dragstartx += (tempf - softmax) / fac; - tempf = softmax; + switch (scale_type) { + case PROP_SCALE_LINEAR: { + if (tempf < softmin) { + data->dragstartx -= (softmin - tempf) / fac; + tempf = softmin; + } + else if (tempf > softmax) { + data->dragstartx -= (softmax - tempf) / fac; + tempf = softmax; + } + break; + } + case PROP_SCALE_LOG: { + if (tempf < log_min) { + data->dragstartx -= logf(log_min / (float)data->startvalue) / fac - + (float)(mx - data->dragstartx); + tempf = softmin; + } + else if (tempf > softmax) { + data->dragstartx -= logf(softmax / (float)data->startvalue) / fac - + (float)(mx - data->dragstartx); + tempf = softmax; + } + break; + } + case PROP_SCALE_CUBIC: { + if (tempf < softmin) { + data->dragstartx = mx - (int)((cbrtf(softmin) - cbrtf((float)data->startvalue)) / fac); + tempf = softmin; + } + else if (tempf > softmax) { + data->dragstartx = mx - (int)((cbrtf(softmax) - cbrtf((float)data->startvalue)) / fac); + tempf = softmax; + } + break; + } } #else CLAMP(tempf, softmin, softmax); @@ -4932,7 +5048,31 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, } data->draglastx = mx; - tempf = (softmin + data->dragf * softrange); + + switch (scale_type) { + case PROP_SCALE_LINEAR: { + tempf = (softmin + data->dragf * softrange); + break; + } + case PROP_SCALE_LOG: { + const float log_min = max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN), + powf(10.0f, -number_but->precision) * 0.5f); + const float base = softmax / log_min; + tempf = powf(base, data->dragf) * log_min; + if (tempf <= log_min) { + tempf = 0.0f; + } + break; + } + case PROP_SCALE_CUBIC: { + tempf = (softmin + data->dragf * softrange); + tempf *= tempf * tempf; + float cubic_min = softmin * softmin * softmin; + float cubic_max = softmax * softmax * softmax; + tempf = (tempf - cubic_min) / (cubic_max - cubic_min) * softrange + softmin; + break; + } + } if (!is_float) { temp = round_fl_to_int(tempf); @@ -5179,9 +5319,19 @@ static int ui_do_but_NUM( else { /* Float Value. */ if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) { + const PropertyScaleType scale_type = ui_but_scale_type(but); + button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - const double value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE; + double value_step; + if (scale_type == PROP_SCALE_LOG) { + value_step = powf(10.0f, + (roundf(log10f(data->value) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f) + + log10f(number_but->step_size)); + } + else { + value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE; + } BLI_assert(value_step > 0.0f); const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ? (double)max_ff(but->softmin, @@ -5229,6 +5379,8 @@ static bool ui_numedit_but_SLI(uiBut *but, return changed; } + const PropertyScaleType scale_type = ui_but_scale_type(but); + softmin = but->softmin; softmax = but->softmax; softrange = softmax - softmin; @@ -5270,7 +5422,24 @@ static bool ui_numedit_but_SLI(uiBut *but, #endif /* done correcting mouse */ - tempf = softmin + f * softrange; + switch (scale_type) { + case PROP_SCALE_LINEAR: { + tempf = softmin + f * softrange; + break; + } + case PROP_SCALE_LOG: { + tempf = powf(softmax / softmin, f) * softmin; + break; + } + case PROP_SCALE_CUBIC: { + const float cubicmin = cube_f(softmin); + const float cubicmax = cube_f(softmax); + const float cubicrange = cubicmax - cubicmin; + tempf = cube_f(softmin + f * softrange); + tempf = (tempf - cubicmin) / cubicrange * softrange + softmin; + break; + } + } temp = round_fl_to_int(tempf); if (snap) { @@ -5464,6 +5633,8 @@ static int ui_do_but_SLI( if (click) { if (click == 2) { + const PropertyScaleType scale_type = ui_but_scale_type(but); + /* nudge slider to the left or right */ float f, tempf, softmin, softmax, softrange; int temp; @@ -5488,14 +5659,20 @@ static int ui_do_but_SLI( f = (float)(mx - but->rect.xmin) / (BLI_rctf_size_x(&but->rect)); } - f = softmin + f * softrange; + if (scale_type == PROP_SCALE_LOG) { + f = powf(softmax / softmin, f) * softmin; + } + else { + f = softmin + f * softrange; + } if (!ui_but_is_float(but)) { + int value_step = 1; if (f < temp) { - temp--; + temp -= value_step; } else { - temp++; + temp += value_step; } if (temp >= softmin && temp <= softmax) { @@ -5506,14 +5683,23 @@ static int ui_do_but_SLI( } } else { - if (f < tempf) { - tempf -= 0.01f; - } - else { - tempf += 0.01f; - } - if (tempf >= softmin && tempf <= softmax) { + float value_step; + if (scale_type == PROP_SCALE_LOG) { + value_step = powf(10.0f, roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f); + } + else { + value_step = 0.01f; + } + + if (f < tempf) { + tempf -= value_step; + } + else { + tempf += value_step; + } + + CLAMP(tempf, softmin, softmax); data->value = tempf; } else { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 4c96512b4f3..40a3f0d330f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -62,7 +62,7 @@ struct wmTimer; #define UI_MENU_PADDING (int)(0.2f * UI_UNIT_Y) #define UI_MENU_WIDTH_MIN (UI_UNIT_Y * 9) -/* some extra padding added to menus containing submenu icons */ +/** Some extra padding added to menus containing sub-menu icons. */ #define UI_MENU_SUBMENU_PADDING (6 * UI_DPI_FAC) /* menu scrolling */ @@ -74,28 +74,31 @@ struct wmTimer; #define UI_PANEL_MINX 100 #define UI_PANEL_MINY 70 -/* popover width (multiplied by 'U.widget_unit') */ +/** Popover width (multiplied by #U.widget_unit) */ #define UI_POPOVER_WIDTH_UNITS 10 -/* uiBut->flag */ +/** #uiBut.flag */ enum { - UI_SELECT = (1 << 0), /* use when the button is pressed */ - UI_SCROLLED = (1 << 1), /* temp hidden, scrolled away */ + /** Use when the button is pressed. */ + UI_SELECT = (1 << 0), + /** Temporarily hidden (scrolled out of the view). */ + UI_SCROLLED = (1 << 1), UI_ACTIVE = (1 << 2), UI_HAS_ICON = (1 << 3), UI_HIDDEN = (1 << 4), - UI_SELECT_DRAW = (1 << 5), /* Display selected, doesn't impact interaction. */ + /** Display selected, doesn't impact interaction. */ + UI_SELECT_DRAW = (1 << 5), /** Property search filter is active and the button does not match. */ - UI_SEARCH_FILTER_NO_MATCH = (1 << 12), - /* warn: rest of uiBut->flag in UI_interface.h */ + UI_SEARCH_FILTER_NO_MATCH = (1 << 6), + /* WARNING: rest of #uiBut.flag in UI_interface.h */ }; -/* uiBut->dragflag */ +/** #uiBut.dragflag */ enum { UI_BUT_DRAGPOIN_FREE = (1 << 0), }; -/* but->pie_dir */ +/** #uiBut.pie_dir */ typedef enum RadialDirection { UI_RADIAL_NONE = -1, UI_RADIAL_N = 0, @@ -126,13 +129,13 @@ extern const short ui_radial_dir_to_angle[8]; #define UI_BITBUT_ROW(min, max) \ (((max) >= 31 ? 0xFFFFFFFF : (1 << ((max) + 1)) - 1) - ((min) ? ((1 << (min)) - 1) : 0)) -/* split numbuts by ':' and align l/r */ +/** Split number-buttons by ':' and align left/right. */ #define USE_NUMBUTS_LR_ALIGN -/* Use new 'align' computation code. */ +/** Use new 'align' computation code. */ #define USE_UIBUT_SPATIAL_ALIGN -/* PieMenuData->flags */ +/** #PieMenuData.flags */ enum { /** pie menu item collision is detected at 90 degrees */ UI_PIE_DEGREES_RANGE_LARGE = (1 << 0), @@ -152,13 +155,13 @@ enum { #define PIE_CLICK_THRESHOLD_SQ 50.0f -/* max amount of items a radial menu (pie menu) can contain */ +/** The maximum number of items a radial menu (pie menu) can contain. */ #define PIE_MAX_ITEMS 8 struct uiBut { struct uiBut *next, *prev; - /* Pointer back to the layout item holding this button. */ + /** Pointer back to the layout item holding this button. */ uiLayout *layout; int flag, drawflag; eButType type; @@ -235,10 +238,10 @@ struct uiBut { short modifier_key; short iconadd; - /* UI_BTYPE_BLOCK data */ + /** #UI_BTYPE_BLOCK data */ uiBlockCreateFunc block_create_func; - /* UI_BTYPE_PULLDOWN/UI_BTYPE_MENU data */ + /** #UI_BTYPE_PULLDOWN / #UI_BTYPE_MENU data */ uiMenuCreateFunc menu_create_func; uiMenuStepFunc menu_step_func; @@ -252,9 +255,11 @@ struct uiBut { struct wmOperatorType *optype; struct PointerRNA *opptr; short opcontext; - uchar menu_key; /* 'a'-'z', always lower case */ - ListBase extra_op_icons; /* uiButExtraOpIcon */ + /** When non-zero, this is the key used to activate a menu items (`a-z` always lower case). */ + uchar menu_key; + + ListBase extra_op_icons; /** #uiButExtraOpIcon */ /* Draggable data, type is WM_DRAG_... */ char dragtype; @@ -263,10 +268,10 @@ struct uiBut { struct ImBuf *imb; float imb_scale; - /* active button data */ + /** Active button data (set when the user is hovering or interacting with a button). */ struct uiHandleButtonData *active; - /* Custom button data. */ + /** Custom button data (borrowed, not owned). */ void *custom_data; char *editstr; @@ -429,7 +434,7 @@ struct PieMenuData { float alphafac; }; -/* uiBlock.content_hints */ +/** #uiBlock.content_hints */ enum eBlockContentHints { /** In a menu block, if there is a single sub-menu button, we add some * padding to the right to put nicely aligned triangle icons there. */ @@ -463,7 +468,8 @@ struct uiBlock { struct Panel *panel; uiBlock *oldblock; - ListBase butstore; /* UI_butstore_* runtime function */ + /** Used for `UI_butstore_*` runtime function. */ + ListBase butstore; ListBase button_groups; /* #uiButtonGroup. */ @@ -479,7 +485,8 @@ struct uiBlock { rctf rect; float aspect; - uint puphash; /* popup menu hash for memory */ + /** Unique hash used to implement popup menu memory. */ + uint puphash; uiButHandleFunc func; void *func_arg1; @@ -494,10 +501,10 @@ struct uiBlock { uiBlockHandleFunc handle_func; void *handle_func_arg; - /* custom extra handling */ + /** Custom extra event handling. */ int (*block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *); - /* extra draw function for custom blocks */ + /** Custom extra draw function for custom blocks. */ void (*drawextra)(const struct bContext *C, void *idv, void *arg1, void *arg2, rcti *rect); void *drawextra_arg1; void *drawextra_arg2; @@ -507,7 +514,7 @@ struct uiBlock { /** Hints about the buttons of this block. Used to avoid iterating over * buttons to find out if some criteria is met by any. Instead, check this * criteria when adding the button and set a flag here if it's met. */ - short content_hints; /* eBlockContentHints */ + short content_hints; /* #eBlockContentHints */ char direction; /** UI_BLOCK_THEME_STYLE_* */ @@ -521,11 +528,11 @@ struct uiBlock { const char *lockstr; bool lock; - /** to keep blocks while drawing and free them afterwards */ + /** To keep blocks while drawing and free them afterwards. */ bool active; - /** to avoid tooltip after click */ + /** To avoid tool-tip after click. */ bool tooltipdisabled; - /** UI_block_end done? */ + /** True when #UI_block_end has been called. */ bool endblock; /** for doing delayed */ @@ -535,12 +542,12 @@ struct uiBlock { /** for doing delayed */ int bounds, minbounds; - /** pull-downs, to detect outside, can differ per case how it is created. */ + /** Pull-downs, to detect outside, can differ per case how it is created. */ rctf safety; - /** uiSafetyRct list */ + /** #uiSafetyRct list */ ListBase saferct; - uiPopupBlockHandle *handle; /* handle */ + uiPopupBlockHandle *handle; /** use so presets can find the operator, * across menus and from nested popups which fail for operator context. */ @@ -555,10 +562,12 @@ struct uiBlock { /** \note only accessed by color picker templates. */ ColorPickerData color_pickers; - bool is_color_gamma_picker; /* Block for color picker with gamma baked in. */ + /** Block for color picker with gamma baked in. */ + bool is_color_gamma_picker; - /** display device name used to display this block, - * used by color widgets to transform colors from/to scene linear + /** + * Display device name used to display this block, + * used by color widgets to transform colors from/to scene linear. */ char display_device[64]; @@ -651,6 +660,7 @@ bool ui_but_context_poll_operator(struct bContext *C, struct wmOperatorType *ot, extern void ui_but_update(uiBut *but); extern void ui_but_update_edited(uiBut *but); +extern PropertyScaleType ui_but_scale_type(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_float(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_bool(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_unit(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -671,9 +681,9 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]); /* interface_regions.c */ struct uiKeyNavLock { - /* Set when we're using key-input. */ + /** Set when we're using keyboard-input. */ bool is_keynav; - /* only used to check if we've moved the cursor */ + /** Only used to check if we've moved the cursor. */ int event_xy[2]; }; @@ -689,7 +699,7 @@ struct uiPopupBlockCreate { int event_xy[2]; - /* when popup is initialized from a button */ + /** Set when popup is initialized from a button. */ struct ARegion *butregion; uiBut *but; }; @@ -698,7 +708,7 @@ struct uiPopupBlockHandle { /* internal */ struct ARegion *region; - /* use only for 'UI_BLOCK_MOVEMOUSE_QUIT' popups */ + /** Use only for #UI_BLOCK_MOVEMOUSE_QUIT popups. */ float towards_xy[2]; double towardstime; bool dotowards; @@ -708,9 +718,9 @@ struct uiPopupBlockHandle { void (*cancel_func)(struct bContext *C, void *arg); void *popup_arg; - /* store data for refreshing popups */ + /** Store data for refreshing popups. */ struct uiPopupBlockCreate popup_create_vars; - /* true if we can re-create the popup using 'popup_create_vars' */ + /** True if we can re-create the popup using #uiPopupBlockHandle.popup_create_vars. */ bool can_refresh; bool refresh; @@ -730,7 +740,7 @@ struct uiPopupBlockHandle { int retvalue; float retvec[4]; - /* menu direction */ + /** Menu direction. */ int direction; /* Previous values so we don't resize or reposition on refresh. */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index cabd98902a6..8f2871ce18b 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -3038,7 +3038,7 @@ void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, i /* popover */ void uiItemPopoverPanel_ptr( - uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon) + uiLayout *layout, const bContext *C, PanelType *pt, const char *name, int icon) { if (!name) { name = CTX_IFACE_(pt->translation_context, pt->label); @@ -3067,7 +3067,7 @@ void uiItemPopoverPanel_ptr( } void uiItemPopoverPanel( - uiLayout *layout, bContext *C, const char *panel_type, const char *name, int icon) + uiLayout *layout, const bContext *C, const char *panel_type, const char *name, int icon) { PanelType *pt = WM_paneltype_find(panel_type, true); if (pt == NULL) { diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 1fb7a931c08..0cf3ad59903 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1495,117 +1495,115 @@ static void edittranslation_find_po_file(const char *root, static int edittranslation_exec(bContext *C, wmOperator *op) { uiBut *but = UI_context_active_but_get(C); - int ret = OPERATOR_CANCELLED; - - if (but) { - wmOperatorType *ot; - PointerRNA ptr; - char popath[FILE_MAX]; - const char *root = U.i18ndir; - const char *uilng = BLT_lang_get(); - - uiStringInfo but_label = {BUT_GET_LABEL, NULL}; - uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL}; - uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; - uiStringInfo but_tip = {BUT_GET_TIP, NULL}; - uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL}; - uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; - uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; - uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; - uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL}; - uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL}; - - if (!BLI_is_dir(root)) { - BKE_report(op->reports, - RPT_ERROR, - "Please set your Preferences' 'Translation Branches " - "Directory' path to a valid directory"); - return OPERATOR_CANCELLED; - } - ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); - if (ot == NULL) { - BKE_reportf(op->reports, - RPT_ERROR, - "Could not find operator '%s'! Please enable ui_translate add-on " - "in the User Preferences", - EDTSRC_I18N_OP_NAME); - return OPERATOR_CANCELLED; - } - /* Try to find a valid po file for current language... */ - edittranslation_find_po_file(root, uilng, popath, FILE_MAX); - /* printf("po path: %s\n", popath); */ - if (popath[0] == '\0') { - BKE_reportf( - op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root); - return OPERATOR_CANCELLED; - } - - UI_but_string_info_get(C, - but, - &but_label, - &rna_label, - &enum_label, - &but_tip, - &rna_tip, - &enum_tip, - &rna_struct, - &rna_prop, - &rna_enum, - &rna_ctxt, - NULL); - - WM_operator_properties_create_ptr(&ptr, ot); - RNA_string_set(&ptr, "lang", uilng); - RNA_string_set(&ptr, "po_file", popath); - RNA_string_set(&ptr, "but_label", but_label.strinfo); - RNA_string_set(&ptr, "rna_label", rna_label.strinfo); - RNA_string_set(&ptr, "enum_label", enum_label.strinfo); - RNA_string_set(&ptr, "but_tip", but_tip.strinfo); - RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo); - RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo); - RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo); - RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); - RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); - RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); - ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); - - /* Clean up */ - if (but_label.strinfo) { - MEM_freeN(but_label.strinfo); - } - if (rna_label.strinfo) { - MEM_freeN(rna_label.strinfo); - } - if (enum_label.strinfo) { - MEM_freeN(enum_label.strinfo); - } - if (but_tip.strinfo) { - MEM_freeN(but_tip.strinfo); - } - if (rna_tip.strinfo) { - MEM_freeN(rna_tip.strinfo); - } - if (enum_tip.strinfo) { - MEM_freeN(enum_tip.strinfo); - } - if (rna_struct.strinfo) { - MEM_freeN(rna_struct.strinfo); - } - if (rna_prop.strinfo) { - MEM_freeN(rna_prop.strinfo); - } - if (rna_enum.strinfo) { - MEM_freeN(rna_enum.strinfo); - } - if (rna_ctxt.strinfo) { - MEM_freeN(rna_ctxt.strinfo); - } + if (but == NULL) { + BKE_report(op->reports, RPT_ERROR, "Active button not found"); + return OPERATOR_CANCELLED; + } - return ret; + wmOperatorType *ot; + PointerRNA ptr; + char popath[FILE_MAX]; + const char *root = U.i18ndir; + const char *uilng = BLT_lang_get(); + + uiStringInfo but_label = {BUT_GET_LABEL, NULL}; + uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL}; + uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; + uiStringInfo but_tip = {BUT_GET_TIP, NULL}; + uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL}; + uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; + uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; + uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; + uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL}; + uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL}; + + if (!BLI_is_dir(root)) { + BKE_report(op->reports, + RPT_ERROR, + "Please set your Preferences' 'Translation Branches " + "Directory' path to a valid directory"); + return OPERATOR_CANCELLED; + } + ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); + if (ot == NULL) { + BKE_reportf(op->reports, + RPT_ERROR, + "Could not find operator '%s'! Please enable ui_translate add-on " + "in the User Preferences", + EDTSRC_I18N_OP_NAME); + return OPERATOR_CANCELLED; + } + /* Try to find a valid po file for current language... */ + edittranslation_find_po_file(root, uilng, popath, FILE_MAX); + /* printf("po path: %s\n", popath); */ + if (popath[0] == '\0') { + BKE_reportf( + op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root); + return OPERATOR_CANCELLED; } - BKE_report(op->reports, RPT_ERROR, "Active button not found"); - return OPERATOR_CANCELLED; + UI_but_string_info_get(C, + but, + &but_label, + &rna_label, + &enum_label, + &but_tip, + &rna_tip, + &enum_tip, + &rna_struct, + &rna_prop, + &rna_enum, + &rna_ctxt, + NULL); + + WM_operator_properties_create_ptr(&ptr, ot); + RNA_string_set(&ptr, "lang", uilng); + RNA_string_set(&ptr, "po_file", popath); + RNA_string_set(&ptr, "but_label", but_label.strinfo); + RNA_string_set(&ptr, "rna_label", rna_label.strinfo); + RNA_string_set(&ptr, "enum_label", enum_label.strinfo); + RNA_string_set(&ptr, "but_tip", but_tip.strinfo); + RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo); + RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo); + RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo); + RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); + RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); + RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); + const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + + /* Clean up */ + if (but_label.strinfo) { + MEM_freeN(but_label.strinfo); + } + if (rna_label.strinfo) { + MEM_freeN(rna_label.strinfo); + } + if (enum_label.strinfo) { + MEM_freeN(enum_label.strinfo); + } + if (but_tip.strinfo) { + MEM_freeN(but_tip.strinfo); + } + if (rna_tip.strinfo) { + MEM_freeN(rna_tip.strinfo); + } + if (enum_tip.strinfo) { + MEM_freeN(enum_tip.strinfo); + } + if (rna_struct.strinfo) { + MEM_freeN(rna_struct.strinfo); + } + if (rna_prop.strinfo) { + MEM_freeN(rna_prop.strinfo); + } + if (rna_enum.strinfo) { + MEM_freeN(rna_enum.strinfo); + } + if (rna_ctxt.strinfo) { + MEM_freeN(rna_ctxt.strinfo); + } + + return ret; } static void UI_OT_edittranslation_init(wmOperatorType *ot) diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index f234f0fbbf5..58a74a3473e 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -86,6 +86,17 @@ int ui_but_menu_step(uiBut *but, int direction) return 0; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Popup Menu Memory + * + * Support menu-memory, a feature that positions the cursor + * over the previously used menu item. + * + * \note This is stored for each unique menu title. + * \{ */ + static uint ui_popup_string_hash(const char *str, const bool use_sep) { /* sometimes button contains hotkey, sometimes not, strip for proper compare */ @@ -392,7 +403,7 @@ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, pup->layout = UI_block_layout( pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); - /* note, this intentionally differs from the menu & submenu default because many operators + /* note, this intentionally differs from the menu & sub-menu default because many operators * use popups like this to select one of their options - * where having invoke doesn't make sense */ uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index b11a727b173..accfb78ab94 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -947,12 +947,13 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* button is disabled, we may be able to tell user why */ if (but->flag & UI_BUT_DISABLED) { const char *disabled_msg = NULL; + bool disabled_msg_free = false; /* if operator poll check failed, it can give pretty precise info why */ if (but->optype) { - CTX_wm_operator_poll_msg_set(C, NULL); + CTX_wm_operator_poll_msg_clear(C); WM_operator_poll_context(C, but->optype, but->opcontext); - disabled_msg = CTX_wm_operator_poll_msg_get(C); + disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free); } /* alternatively, buttons can store some reasoning too */ else if (but->disabled_info) { @@ -967,6 +968,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) }); field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg); } + if (disabled_msg_free) { + MEM_freeN((void *)disabled_msg); + } } if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) { diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 0484565ee2e..e3df9704826 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -7195,7 +7195,7 @@ void uiTemplateComponentMenu(uiLayout *layout, /** \name Node Socket Icon Template * \{ */ -void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color) +void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float color[4]) { uiBlock *block = uiLayoutGetBlock(layout); UI_block_align_begin(block); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index c9c1f56dc98..d2e6165a20f 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -3752,12 +3752,35 @@ static void widget_numslider( float factor, factor_ui; float factor_discard = 1.0f; /* No discard. */ const float value = (float)ui_but_value_get(but); - - if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) { - factor = value / but->softmax; - } - else { - factor = (value - but->softmin) / (but->softmax - but->softmin); + const float softmin = but->softmin; + const float softmax = but->softmax; + const float softrange = softmax - softmin; + const PropertyScaleType scale_type = ui_but_scale_type(but); + + switch (scale_type) { + case PROP_SCALE_LINEAR: { + if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) { + factor = value / softmax; + } + else { + factor = (value - softmin) / softrange; + } + break; + } + case PROP_SCALE_LOG: { + const float logmin = fmaxf(softmin, 0.5e-8f); + const float base = softmax / logmin; + factor = logf(value / logmin) / logf(base); + break; + } + case PROP_SCALE_CUBIC: { + const float cubicmin = cube_f(softmin); + const float cubicmax = cube_f(softmax); + const float cubicrange = cubicmax - cubicmin; + const float f = (value - softmin) * cubicrange / softrange + cubicmin; + factor = (cbrtf(f) - softmin) / softrange; + break; + } } const float width = (float)BLI_rcti_size_x(rect); diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 1f1165a464b..592467c2a85 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -240,22 +240,17 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) static void wm_alembic_export_draw(bContext *C, wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - /* Conveniently set start and end frame to match the scene's frame range. */ Scene *scene = CTX_data_scene(C); - if (scene != NULL && RNA_boolean_get(&ptr, "init_scene_frame_range")) { - RNA_int_set(&ptr, "start", SFRA); - RNA_int_set(&ptr, "end", EFRA); + if (scene != NULL && RNA_boolean_get(op->ptr, "init_scene_frame_range")) { + RNA_int_set(op->ptr, "start", SFRA); + RNA_int_set(op->ptr, "end", EFRA); - RNA_boolean_set(&ptr, "init_scene_frame_range", false); + RNA_boolean_set(op->ptr, "init_scene_frame_range", false); } - ui_alembic_export_settings(op->layout, &ptr); + ui_alembic_export_settings(op->layout, op->ptr); } static bool wm_alembic_export_check(bContext *UNUSED(C), wmOperator *op) @@ -594,13 +589,9 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE); } -static void wm_alembic_import_draw(bContext *C, wmOperator *op) +static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - ui_alembic_import_settings(op->layout, &ptr); + ui_alembic_import_settings(op->layout, op->ptr); } /* op->invoke, opens fileselect if path property not set, otherwise executes */ diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index c8e3e1814fc..859c12d7e52 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -400,13 +400,9 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr) } } -static void wm_collada_export_draw(bContext *C, wmOperator *op) +static void wm_collada_export_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - uiCollada_exportSettings(op->layout, &ptr); + uiCollada_exportSettings(op->layout, op->ptr); } static bool wm_collada_export_check(bContext *UNUSED(C), wmOperator *op) @@ -798,13 +794,9 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr) uiItemR(box, imfptr, "keep_bind_info", 0, NULL, ICON_NONE); } -static void wm_collada_import_draw(bContext *C, wmOperator *op) +static void wm_collada_import_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - uiCollada_importSettings(op->layout, &ptr); + uiCollada_importSettings(op->layout, op->ptr); } void WM_OT_collada_import(wmOperatorType *ot) diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c index 7b4b59902f9..b49be324372 100644 --- a/source/blender/editors/io/io_gpencil_export.c +++ b/source/blender/editors/io/io_gpencil_export.c @@ -215,14 +215,9 @@ static void ui_gpencil_export_svg_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "use_clip_camera", 0, NULL, ICON_NONE); } -static void wm_gpencil_export_svg_draw(bContext *C, wmOperator *op) +static void wm_gpencil_export_svg_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - ui_gpencil_export_svg_settings(op->layout, &ptr); + ui_gpencil_export_svg_settings(op->layout, op->ptr); } static bool wm_gpencil_export_svg_poll(bContext *C) @@ -378,14 +373,9 @@ static void ui_gpencil_export_pdf_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "use_normalized_thickness", 0, NULL, ICON_NONE); } -static void wm_gpencil_export_pdf_draw(bContext *C, wmOperator *op) +static void wm_gpencil_export_pdf_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - ui_gpencil_export_pdf_settings(op->layout, &ptr); + ui_gpencil_export_pdf_settings(op->layout, op->ptr); } static bool wm_gpencil_export_pdf_poll(bContext *C) diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c index e4fabc0c5de..a9911f1cef2 100644 --- a/source/blender/editors/io/io_gpencil_import.c +++ b/source/blender/editors/io/io_gpencil_import.c @@ -136,13 +136,9 @@ static void ui_gpencil_import_svg_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "scale", 0, NULL, ICON_NONE); } -static void wm_gpencil_import_svg_draw(bContext *C, wmOperator *op) +static void wm_gpencil_import_svg_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - ui_gpencil_import_svg_settings(op->layout, &ptr); + ui_gpencil_import_svg_settings(op->layout, op->ptr); } static bool wm_gpencil_import_svg_poll(bContext *C) diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index 1eb6613d8fe..81bf66da72c 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -220,8 +220,8 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot) } /* - * - loop over selected shapekeys. - * - find firstsel/lastsel pairs. + * - loop over selected shape-keys. + * - find first-selected/last-selected pairs. * - move these into a temp list. * - re-key all the original shapes. * - copy unselected values back from the original. diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 51190268c4c..43492cd57af 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -911,76 +911,73 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) static void edbm_bevel_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); uiLayout *col, *row; - PointerRNA ptr, toolsettings_ptr; + PointerRNA toolsettings_ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - int profile_type = RNA_enum_get(&ptr, "profile_type"); - int offset_type = RNA_enum_get(&ptr, "offset_type"); - bool affect_type = RNA_enum_get(&ptr, "affect"); + int profile_type = RNA_enum_get(op->ptr, "profile_type"); + int offset_type = RNA_enum_get(op->ptr, "offset_type"); + bool affect_type = RNA_enum_get(op->ptr, "affect"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); - uiItemR(layout, &ptr, "offset_type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "offset_type", 0, NULL, ICON_NONE); if (offset_type == BEVEL_AMT_PERCENT) { - uiItemR(layout, &ptr, "offset_pct", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "offset_pct", 0, NULL, ICON_NONE); } else { - uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE); } - uiItemR(layout, &ptr, "segments", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "segments", 0, NULL, ICON_NONE); if (ELEM(profile_type, BEVEL_PROFILE_SUPERELLIPSE, BEVEL_PROFILE_CUSTOM)) { uiItemR(layout, - &ptr, + op->ptr, "profile", UI_ITEM_R_SLIDER, (profile_type == BEVEL_PROFILE_SUPERELLIPSE) ? IFACE_("Shape") : IFACE_("Miter Shape"), ICON_NONE); } - uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "material", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, &ptr, "harden_normals", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "clamp_overlap", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "loop_slide", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "harden_normals", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "clamp_overlap", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "loop_slide", 0, NULL, ICON_NONE); col = uiLayoutColumnWithHeading(layout, true, IFACE_("Mark")); uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES); - uiItemR(col, &ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE); - uiItemR(col, &ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE); + uiItemR(col, op->ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE); + uiItemR(col, op->ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE); uiItemS(layout); col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES); - uiItemR(col, &ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE); - uiItemR(col, &ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE); - if (RNA_enum_get(&ptr, "miter_inner") == BEVEL_MITER_ARC) { - uiItemR(col, &ptr, "spread", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE); + uiItemR(col, op->ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE); + if (RNA_enum_get(op->ptr, "miter_inner") == BEVEL_MITER_ARC) { + uiItemR(col, op->ptr, "spread", 0, NULL, ICON_NONE); } uiItemS(layout); col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES); - uiItemR(col, &ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE); + uiItemR(col, op->ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE); - uiItemR(layout, &ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE); + uiItemR(layout, op->ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE); uiItemS(layout); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); if (profile_type == BEVEL_PROFILE_CUSTOM) { /* Get an RNA pointer to ToolSettings to give to the curve profile template code. */ Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 141f120396a..d1f228e951a 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -251,32 +251,28 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void edbm_intersect_ui(bContext *C, wmOperator *op) +static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); uiLayout *row; - PointerRNA ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT; + bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT; uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); if (!use_exact) { - uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE); } } @@ -418,32 +414,28 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void edbm_intersect_boolean_ui(bContext *C, wmOperator *op) +static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; uiLayout *row; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT; + bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT; uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); uiItemS(layout); - uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_swap", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_self", 0, NULL, ICON_NONE); if (!use_exact) { - uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE); } } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 33e4691bfb5..bb332a4094c 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3694,21 +3694,18 @@ static const EnumPropertyItem *shape_itemf(bContext *C, static void edbm_blend_from_shape_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; Object *obedit = CTX_data_edit_object(C); Mesh *me = obedit->data; PointerRNA ptr_key; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); RNA_id_pointer_create((ID *)me->key, &ptr_key); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - uiItemPointerR(layout, &ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA); - uiItemR(layout, &ptr, "blend", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "add", 0, NULL, ICON_NONE); + uiItemPointerR(layout, op->ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA); + uiItemR(layout, op->ptr, "blend", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "add", 0, NULL, ICON_NONE); } void MESH_OT_blend_from_shape(wmOperatorType *ot) @@ -5612,29 +5609,25 @@ static bool edbm_decimate_check(bContext *UNUSED(C), wmOperator *UNUSED(op)) return true; } -static void edbm_decimate_ui(bContext *C, wmOperator *op) +static void edbm_decimate_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout, *row, *col, *sub; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiLayoutSetPropSep(layout, true); - uiItemR(layout, &ptr, "ratio", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "ratio", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "use_vertex_group", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_vertex_group", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, false); - uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_vertex_group")); - uiItemR(col, &ptr, "vertex_group_factor", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "invert_vertex_group", 0, NULL, ICON_NONE); + uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "use_vertex_group")); + uiItemR(col, op->ptr, "vertex_group_factor", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "invert_vertex_group", 0, NULL, ICON_NONE); row = uiLayoutRowWithHeading(layout, true, IFACE_("Symmetry")); - uiItemR(row, &ptr, "use_symmetry", 0, "", ICON_NONE); + uiItemR(row, op->ptr, "use_symmetry", 0, "", ICON_NONE); sub = uiLayoutRow(row, true); - uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_symmetry")); - uiItemR(sub, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "use_symmetry")); + uiItemR(sub, op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE); } void MESH_OT_decimate(wmOperatorType *ot) diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 2e98f0558f3..19c9909039c 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -1721,7 +1721,7 @@ void EDBM_project_snap_verts( SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_NOT_ACTIVE, - .use_object_edit_cage = false, + .edit_mode_type = SNAP_GEOM_FINAL, .use_occlusion_test = true, }, mval, diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 4c28b24b8d9..c722a0c00ee 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -117,6 +117,7 @@ #include "ED_physics.h" #include "ED_render.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "ED_transform.h" #include "ED_view3d.h" @@ -1322,12 +1323,14 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) { return OPERATOR_CANCELLED; } - /* add new object if not currently editing a GP object, - * or if "empty" was chosen (i.e. user wants a blank GP canvas) - */ - if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false) || (type == GP_EMPTY)) { + /* Add new object if not currently editing a GP object. */ + if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) { const char *ob_name = NULL; switch (type) { + case GP_EMPTY: { + ob_name = "GPencil"; + break; + } case GP_MONKEY: { ob_name = "Suzanne"; break; @@ -1358,6 +1361,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) /* create relevant geometry */ switch (type) { + case GP_EMPTY: { + float mat[4][4]; + + ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + ED_gpencil_create_blank(C, ob, mat); + break; + } case GP_STROKE: { float radius = RNA_float_get(op->ptr, "radius"); float mat[4][4]; @@ -1434,10 +1444,6 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) break; } - case GP_EMPTY: - /* do nothing */ - break; - default: BKE_report(op->reports, RPT_WARNING, "Not implemented"); break; @@ -2680,6 +2686,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) int a, mballConverted = 0; bool gpencilConverted = false; + bool gpencilCurveConverted = false; /* don't forget multiple users! */ @@ -2964,6 +2971,16 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* meshes doesn't use displist */ BKE_object_free_curve_cache(newob); } + else if (target == OB_GPENCIL) { + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + Object *ob_gpencil = ED_gpencil_add_object(C, newob->loc, local_view_bits); + copy_v3_v3(ob_gpencil->rot, newob->rot); + copy_v3_v3(ob_gpencil->scale, newob->scale); + BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f); + gpencilConverted = true; + gpencilCurveConverted = true; + basen = NULL; + } } else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { ob->flag |= OB_DONE; @@ -3145,6 +3162,17 @@ static int object_convert_exec(bContext *C, wmOperator *op) FOREACH_SCENE_OBJECT_END; } } + else { + /* Remove Text curves converted to Grease Pencil object to avoid duplicated curves. */ + if (gpencilCurveConverted) { + FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) { + if (ELEM(ob_delete->type, OB_CURVE) && (ob_delete->flag & OB_DONE)) { + ED_object_base_free_and_unlink(bmain, scene, ob_delete); + } + } + FOREACH_SCENE_OBJECT_END; + } + } // XXX ED_object_editmode_enter(C, 0); // XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */ @@ -3168,24 +3196,21 @@ static int object_convert_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void object_convert_ui(bContext *C, wmOperator *op) +static void object_convert_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; uiLayoutSetPropSep(layout, true); - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - uiItemR(layout, &ptr, "target", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "keep_original", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "target", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "keep_original", 0, NULL, ICON_NONE); - if (RNA_enum_get(&ptr, "target") == OB_GPENCIL) { - uiItemR(layout, &ptr, "thickness", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "angle", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "seams", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "faces", 0, NULL, ICON_NONE); + if (RNA_enum_get(op->ptr, "target") == OB_GPENCIL) { + uiItemR(layout, op->ptr, "thickness", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "angle", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "seams", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "faces", 0, NULL, ICON_NONE); } } @@ -3465,7 +3490,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or * BKE_view_layer_base_deselect_all(). */ - ED_object_base_deselect_all(view_layer, NULL, BA_DESELECT); + ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT); ED_object_base_select(basen, BA_SELECT); ED_object_base_activate(C, basen); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index d3b4b91881c..5be572baec5 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1592,25 +1592,8 @@ static const EnumPropertyItem *object_mode_set_itemsf(bContext *C, const Object *ob = CTX_data_active_object(C); if (ob) { - const bool use_mode_particle_edit = ED_object_particle_edit_mode_supported(ob); while (input->identifier) { - if ((input->value == OB_MODE_EDIT && OB_TYPE_SUPPORT_EDITMODE(ob->type)) || - (input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) || - (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) || - (ELEM(input->value, - OB_MODE_SCULPT, - OB_MODE_VERTEX_PAINT, - OB_MODE_WEIGHT_PAINT, - OB_MODE_TEXTURE_PAINT) && - (ob->type == OB_MESH)) || - (ELEM(input->value, - OB_MODE_EDIT_GPENCIL, - OB_MODE_PAINT_GPENCIL, - OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL, - OB_MODE_VERTEX_GPENCIL) && - (ob->type == OB_GPENCIL)) || - (input->value == OB_MODE_OBJECT)) { + if (ED_object_mode_compat_test(ob, input->value)) { RNA_enum_item_add(&item, &totitem, input); } input++; diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index ededcbfdba8..5bf04e195fe 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -157,10 +157,6 @@ bool edit_modifier_poll_generic(struct bContext *C, const bool is_liboverride_allowed); void edit_modifier_properties(struct wmOperatorType *ot); bool edit_modifier_invoke_properties(struct bContext *C, struct wmOperator *op); -bool edit_modifier_invoke_properties_with_hover(struct bContext *C, - struct wmOperator *op, - const struct wmEvent *event, - int *r_retval); struct ModifierData *edit_modifier_property_get(struct wmOperator *op, struct Object *ob, diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index ff1855cafc5..dcb294bcb12 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -115,43 +115,46 @@ static const char *object_mode_op_string(eObjectMode mode) */ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) { - if (ob) { - if (mode == OB_MODE_OBJECT) { - return true; - } + if (mode == OB_MODE_OBJECT) { + return true; + } - switch (ob->type) { - case OB_MESH: - if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | - OB_MODE_TEXTURE_PAINT | OB_MODE_PARTICLE_EDIT)) { - return true; - } - break; - case OB_CURVE: - case OB_SURF: - case OB_FONT: - case OB_MBALL: - if (mode & OB_MODE_EDIT) { - return true; - } - break; - case OB_LATTICE: - if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) { - return true; - } - break; - case OB_ARMATURE: - if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) { - return true; - } - break; - case OB_GPENCIL: - if (mode & (OB_MODE_EDIT | OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | - OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) { + switch (ob->type) { + case OB_MESH: + if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | + OB_MODE_TEXTURE_PAINT)) { + return true; + } + if (mode & OB_MODE_PARTICLE_EDIT) { + if (ED_object_particle_edit_mode_supported(ob)) { return true; } - break; - } + } + break; + case OB_CURVE: + case OB_SURF: + case OB_FONT: + case OB_MBALL: + if (mode & OB_MODE_EDIT) { + return true; + } + break; + case OB_LATTICE: + if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) { + return true; + } + break; + case OB_ARMATURE: + if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) { + return true; + } + break; + case OB_GPENCIL: + if (mode & (OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | OB_MODE_SCULPT_GPENCIL | + OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) { + return true; + } + break; } return false; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 49c07b28f07..e71b66b1a72 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -210,7 +210,7 @@ ModifierData *ED_object_modifier_add( /* special cases */ if (type == eModifierType_Softbody) { if (!ob->soft) { - ob->soft = sbNew(scene); + ob->soft = sbNew(); ob->softflag |= OB_SB_GOAL | OB_SB_EDGES; } } @@ -1120,10 +1120,10 @@ bool edit_modifier_invoke_properties(bContext *C, wmOperator *op) * with a UI panel below the mouse cursor, unless a specific modifier is set with a context * pointer. Used in order to apply modifier operators on hover over their panels. */ -bool edit_modifier_invoke_properties_with_hover(bContext *C, - wmOperator *op, - const wmEvent *event, - int *r_retval) +static bool edit_modifier_invoke_properties_with_hover(bContext *C, + wmOperator *op, + const wmEvent *event, + int *r_retval) { if (RNA_struct_property_is_set(op->ptr, "modifier")) { return true; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index a7308002e76..b685a93f27b 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -801,7 +801,7 @@ bool ED_object_parent_set(ReportList *reports, * so we check this by assuming that the parent is selected too. */ /* XXX currently this should only happen for meshes, curves, surfaces, - * and lattices - this stuff isn't available for metas yet */ + * and lattices - this stuff isn't available for meta-balls yet. */ if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { ModifierData *md; diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 10e0a143d9b..157f2b3ecb4 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -692,8 +692,9 @@ struct ObjectPreviewData { int sizey; }; -static Object *object_preview_camera_create( - Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey) +static Object *object_preview_camera_create(Main *preview_main, + ViewLayer *view_layer, + Object *preview_object) { Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera"); @@ -701,18 +702,17 @@ static Object *object_preview_camera_create( float dummyscale[3]; mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat); - /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */ + /* Camera is Y up, so needs additional rotations to obliquely face the front. */ float drotmat[3][3]; - axis_angle_to_mat3_single(drotmat, 'X', M_PI_2); + const float eul[3] = {M_PI * 0.4f, 0.0f, M_PI * 0.1f}; + eul_to_mat3(drotmat, eul); mul_m3_m3_post(rotmat, drotmat); camera->rotmode = ROT_MODE_QUAT; mat3_to_quat(camera->quat, rotmat); - /* shader_preview_render() does this too. */ - if (sizex > sizey) { - ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex; - } + /* Nice focal length for close portraiture. */ + ((Camera *)camera->data)->lens = 85; return camera; } @@ -730,11 +730,8 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object); - Object *camera_object = object_preview_camera_create(preview_data->pr_main, - view_layer, - preview_data->object, - preview_data->sizex, - preview_data->sizey); + Object *camera_object = object_preview_camera_create( + preview_data->pr_main, view_layer, preview_data->object); scene->camera = camera_object; scene->r.xsch = preview_data->sizex; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index bd2b1c4c553..b83eccdcfdd 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1172,12 +1172,12 @@ static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *reg } /* dir is direction to check, not the splitting edge direction! */ -static int rct_fits(const rcti *rect, char dir, int size) +static int rct_fits(const rcti *rect, const eScreenAxis dir_axis, int size) { - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { return BLI_rcti_size_x(rect) + 1 - size; } - /* 'v' */ + /* Vertical. */ return BLI_rcti_size_y(rect) + 1 - size; } @@ -1398,7 +1398,8 @@ static void region_rect_recursive( region->flag |= RGN_FLAG_TOO_SMALL; } } - else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) { + else if (rct_fits(remainder, SCREEN_AXIS_V, 1) < 0 || + rct_fits(remainder, SCREEN_AXIS_H, 1) < 0) { /* remainder is too small for any usage */ region->flag |= RGN_FLAG_TOO_SMALL; } @@ -1410,11 +1411,11 @@ static void region_rect_recursive( else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { rcti *winrct = (region->overlap) ? overlap_remainder : remainder; - if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) { + if ((prefsizey == 0) || (rct_fits(winrct, SCREEN_AXIS_V, prefsizey) < 0)) { region->flag |= RGN_FLAG_TOO_SMALL; } else { - int fac = rct_fits(winrct, 'v', prefsizey); + int fac = rct_fits(winrct, SCREEN_AXIS_V, prefsizey); if (fac < 0) { prefsizey += fac; @@ -1436,11 +1437,11 @@ static void region_rect_recursive( else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { rcti *winrct = (region->overlap) ? overlap_remainder : remainder; - if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) { + if ((prefsizex == 0) || (rct_fits(winrct, SCREEN_AXIS_H, prefsizex) < 0)) { region->flag |= RGN_FLAG_TOO_SMALL; } else { - int fac = rct_fits(winrct, 'h', prefsizex); + int fac = rct_fits(winrct, SCREEN_AXIS_H, prefsizex); if (fac < 0) { prefsizex += fac; @@ -1464,7 +1465,7 @@ static void region_rect_recursive( region->winrct = *remainder; if (alignment == RGN_ALIGN_HSPLIT) { - if (rct_fits(remainder, 'h', prefsizex) > 4) { + if (rct_fits(remainder, SCREEN_AXIS_H, prefsizex) > 4) { region->winrct.xmax = BLI_rcti_cent_x(remainder); remainder->xmin = region->winrct.xmax + 1; } @@ -1473,7 +1474,7 @@ static void region_rect_recursive( } } else { - if (rct_fits(remainder, 'v', prefsizey) > 4) { + if (rct_fits(remainder, SCREEN_AXIS_V, prefsizey) > 4) { region->winrct.ymax = BLI_rcti_cent_y(remainder); remainder->ymin = region->winrct.ymax + 1; } diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 2ba7ef8f972..2c45524ef94 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -33,184 +33,11 @@ #include "WM_api.h" +#include "UI_interface.h" #include "UI_resources.h" #include "screen_intern.h" -/** - * Draw horizontal shape visualizing future joining - * (left as well right direction of future joining). - */ -static void draw_horizontal_join_shape(ScrArea *area, char dir, uint pos) -{ - const float width = screen_geom_area_width(area) - 1; - const float height = screen_geom_area_height(area) - 1; - - float w, h; - if (height < width) { - h = height / 8; - w = height / 4; - } - else { - h = width / 8; - w = width / 4; - } - - vec2f points[10]; - points[0].x = area->v1->vec.x; - points[0].y = area->v1->vec.y + height / 2; - - points[1].x = area->v1->vec.x; - points[1].y = area->v1->vec.y; - - points[2].x = area->v4->vec.x - w; - points[2].y = area->v4->vec.y; - - points[3].x = area->v4->vec.x - w; - points[3].y = area->v4->vec.y + height / 2 - 2 * h; - - points[4].x = area->v4->vec.x - 2 * w; - points[4].y = area->v4->vec.y + height / 2; - - points[5].x = area->v4->vec.x - w; - points[5].y = area->v4->vec.y + height / 2 + 2 * h; - - points[6].x = area->v3->vec.x - w; - points[6].y = area->v3->vec.y; - - points[7].x = area->v2->vec.x; - points[7].y = area->v2->vec.y; - - points[8].x = area->v4->vec.x; - points[8].y = area->v4->vec.y + height / 2 - h; - - points[9].x = area->v4->vec.x; - points[9].y = area->v4->vec.y + height / 2 + h; - - if (dir == 'l') { - /* when direction is left, then we flip direction of arrow */ - float cx = area->v1->vec.x + width; - for (int i = 0; i < 10; i++) { - points[i].x -= cx; - points[i].x = -points[i].x; - points[i].x += area->v1->vec.x; - } - } - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 0; i < 5; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immEnd(); - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 4; i < 8; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immVertex2f(pos, points[0].x, points[0].y); - immEnd(); - - immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y); - immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y); -} - -/** - * Draw vertical shape visualizing future joining (up/down direction). - */ -static void draw_vertical_join_shape(ScrArea *area, char dir, uint pos) -{ - const float width = screen_geom_area_width(area) - 1; - const float height = screen_geom_area_height(area) - 1; - - float w, h; - if (height < width) { - h = height / 4; - w = height / 8; - } - else { - h = width / 4; - w = width / 8; - } - - vec2f points[10]; - points[0].x = area->v1->vec.x + width / 2; - points[0].y = area->v3->vec.y; - - points[1].x = area->v2->vec.x; - points[1].y = area->v2->vec.y; - - points[2].x = area->v1->vec.x; - points[2].y = area->v1->vec.y + h; - - points[3].x = area->v1->vec.x + width / 2 - 2 * w; - points[3].y = area->v1->vec.y + h; - - points[4].x = area->v1->vec.x + width / 2; - points[4].y = area->v1->vec.y + 2 * h; - - points[5].x = area->v1->vec.x + width / 2 + 2 * w; - points[5].y = area->v1->vec.y + h; - - points[6].x = area->v4->vec.x; - points[6].y = area->v4->vec.y + h; - - points[7].x = area->v3->vec.x; - points[7].y = area->v3->vec.y; - - points[8].x = area->v1->vec.x + width / 2 - w; - points[8].y = area->v1->vec.y; - - points[9].x = area->v1->vec.x + width / 2 + w; - points[9].y = area->v1->vec.y; - - if (dir == 'u') { - /* when direction is up, then we flip direction of arrow */ - float cy = area->v1->vec.y + height; - for (int i = 0; i < 10; i++) { - points[i].y -= cy; - points[i].y = -points[i].y; - points[i].y += area->v1->vec.y; - } - } - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 0; i < 5; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immEnd(); - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 4; i < 8; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immVertex2f(pos, points[0].x, points[0].y); - immEnd(); - - immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y); - immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y); -} - -/** - * Draw join shape due to direction of joining. - */ -static void draw_join_shape(ScrArea *area, char dir, uint pos) -{ - if (ELEM(dir, 'u', 'd')) { - draw_vertical_join_shape(area, dir, pos); - } - else { - draw_horizontal_join_shape(area, dir, pos); - } -} - #define CORNER_RESOLUTION 3 static void do_vert_pair(GPUVertBuf *vbo, uint pos, uint *vidx, int corner, int i) @@ -290,28 +117,6 @@ static GPUBatch *batch_screen_edges_get(int *corner_len) #undef CORNER_RESOLUTION -/** - * Draw screen area darker with arrow (visualization of future joining). - */ -static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos) -{ - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ub(0, 0, 0, 50); - - draw_join_shape(area, dir, pos); -} - -/** - * Draw screen area lighter with arrow shape ("eraser" of previous dark shape). - */ -static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos) -{ - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ub(255, 255, 255, 25); - - immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y); -} - static void drawscredge_area_draw( int sizex, int sizey, short x1, short y1, short x2, short y2, float edge_thickness) { @@ -427,53 +232,97 @@ void ED_screen_draw_edges(wmWindow *win) } /** - * The blended join arrows. + * Visual indication of the two areas involved in a proposed join. * * \param sa1: Area from which the resultant originates. * \param sa2: Target area that will be replaced. */ -void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2) +void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const eScreenDir dir = area_getorientation(sa1, sa2); + if (dir == SCREEN_DIR_NONE) { + return; + } + + /* Rect of the combined areas.*/ + const bool vertical = SCREEN_DIR_IS_VERTICAL(dir); + const rctf combined = { + .xmin = vertical ? MAX2(sa1->totrct.xmin, sa2->totrct.xmin) : + MIN2(sa1->totrct.xmin, sa2->totrct.xmin), + .xmax = vertical ? MIN2(sa1->totrct.xmax, sa2->totrct.xmax) : + MAX2(sa1->totrct.xmax, sa2->totrct.xmax), + .ymin = vertical ? MIN2(sa1->totrct.ymin, sa2->totrct.ymin) : + MAX2(sa1->totrct.ymin, sa2->totrct.ymin), + .ymax = vertical ? MAX2(sa1->totrct.ymax, sa2->totrct.ymax) : + MIN2(sa1->totrct.ymax, sa2->totrct.ymax), + }; + + uint pos_id = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); - GPU_line_width(1); - - /* blended join arrow */ - int dir = area_getorientation(sa1, sa2); - int dira = -1; - if (dir != -1) { - switch (dir) { - case 0: /* W */ - dir = 'r'; - dira = 'l'; - break; - case 1: /* N */ - dir = 'd'; - dira = 'u'; - break; - case 2: /* E */ - dir = 'l'; - dira = 'r'; - break; - case 3: /* S */ - dir = 'u'; - dira = 'd'; - break; + /* Highlight source (sa1) within combined area. */ + immUniformColor4fv((const float[4]){1.0f, 1.0f, 1.0f, 0.10f}); + immRectf(pos_id, + MAX2(sa1->totrct.xmin, combined.xmin), + MAX2(sa1->totrct.ymin, combined.ymin), + MIN2(sa1->totrct.xmax, combined.xmax), + MIN2(sa1->totrct.ymax, combined.ymax)); + + /* Highlight destination (sa2) within combined area. */ + immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.25f}); + immRectf(pos_id, + MAX2(sa2->totrct.xmin, combined.xmin), + MAX2(sa2->totrct.ymin, combined.ymin), + MIN2(sa2->totrct.xmax, combined.xmax), + MIN2(sa2->totrct.ymax, combined.ymax)); + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + if (offset1 < 0 || offset2 > 0) { + /* Show partial areas that will be closed. */ + immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.8f}); + if (vertical) { + if (sa1->totrct.xmin < combined.xmin) { + immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax); + } + if (sa2->totrct.xmin < combined.xmin) { + immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax); + } + if (sa1->totrct.xmax > combined.xmax) { + immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax); + } + if (sa2->totrct.xmax > combined.xmax) { + immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax); + } + } + else { + if (sa1->totrct.ymin < combined.ymin) { + immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin); + } + if (sa2->totrct.ymin < combined.ymin) { + immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin); + } + if (sa1->totrct.ymax > combined.ymax) { + immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax); + } + if (sa2->totrct.ymax > combined.ymax) { + immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax); + } } - - GPU_blend(GPU_BLEND_ALPHA); - - scrarea_draw_shape_dark(sa2, dir, pos); - scrarea_draw_shape_light(sa1, dira, pos); - - GPU_blend(GPU_BLEND_NONE); } immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); + + /* Outline the combined area. */ + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, (float[4]){1.0f, 1.0f, 1.0f, 0.8f}); } -void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) +void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const float fac) { uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -485,7 +334,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) immBegin(GPU_PRIM_LINES, 2); - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { const float y = (1 - fac) * area->totrct.ymin + fac * area->totrct.ymax; immVertex2f(pos, area->totrct.xmin, y); @@ -503,7 +352,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) immEnd(); } else { - BLI_assert(dir == 'v'); + BLI_assert(dir_axis == SCREEN_AXIS_V); const float x = (1 - fac) * area->totrct.xmin + fac * area->totrct.xmax; immVertex2f(pos, x, area->totrct.ymin); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 7ad8eada3b9..6fb5f33d836 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -104,8 +104,12 @@ static void screen_delarea(bContext *C, bScreen *screen, ScrArea *area) MEM_freeN(area); } -ScrArea *area_split( - const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge) +ScrArea *area_split(const wmWindow *win, + bScreen *screen, + ScrArea *area, + const eScreenAxis dir_axis, + const float fac, + const bool merge) { ScrArea *newa = NULL; @@ -116,7 +120,7 @@ ScrArea *area_split( rcti window_rect; WM_window_rect_calc(win, &window_rect); - short split = screen_geom_find_area_split_point(area, &window_rect, dir, fac); + short split = screen_geom_find_area_split_point(area, &window_rect, dir_axis, fac); if (split == 0) { return NULL; } @@ -125,7 +129,7 @@ ScrArea *area_split( * normally it shouldn't matter which is used since the copy should match the original * however with viewport rendering and python console this isn't the case. - campbell */ - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { /* new vertices */ ScrVert *sv1 = screen_geom_vertex_add(screen, area->v1->vec.x, split); ScrVert *sv2 = screen_geom_vertex_add(screen, area->v4->vec.x, split); @@ -279,47 +283,44 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new) screen_new->do_draw = true; } -/* with area as center, sb is located at: 0=W, 1=N, 2=E, 3=S */ -/* -1 = not valid check */ -/* used with join operator */ -int area_getorientation(ScrArea *area, ScrArea *sb) +/** + * with `sa_a` as center, `sa_b` is located at: 0=W, 1=N, 2=E, 3=S + * -1 = not valid check. + * used with join operator. + */ +eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b) { - if (area == NULL || sb == NULL) { - return -1; + if (sa_a == NULL || sa_b == NULL || sa_a == sa_b) { + return SCREEN_DIR_NONE; } - ScrVert *saBL = area->v1; - ScrVert *saTL = area->v2; - ScrVert *saTR = area->v3; - ScrVert *saBR = area->v4; + const vec2s *sa_bl = &sa_a->v1->vec; + const vec2s *sa_tl = &sa_a->v2->vec; + const vec2s *sa_tr = &sa_a->v3->vec; + const vec2s *sa_br = &sa_a->v4->vec; - ScrVert *sbBL = sb->v1; - ScrVert *sbTL = sb->v2; - ScrVert *sbTR = sb->v3; - ScrVert *sbBR = sb->v4; + const vec2s *sb_bl = &sa_b->v1->vec; + const vec2s *sb_tl = &sa_b->v2->vec; + const vec2s *sb_tr = &sa_b->v3->vec; + const vec2s *sb_br = &sa_b->v4->vec; - if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */ - if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) && - (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) { + if (sa_bl->x == sb_br->x && sa_tl->x == sb_tr->x) { /* sa_a to right of sa_b = W */ + if ((MIN2(sa_tl->y, sb_tr->y) - MAX2(sa_bl->y, sb_br->y)) > AREAJOINTOLERANCEY) { return 0; } } - else if (saTL->vec.y == sbBL->vec.y && - saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */ - if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) && - (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) { + else if (sa_tl->y == sb_bl->y && sa_tr->y == sb_br->y) { /* sa_a to bottom of sa_b = N */ + if ((MIN2(sa_tr->x, sb_br->x) - MAX2(sa_tl->x, sb_bl->x)) > AREAJOINTOLERANCEX) { return 1; } } - else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */ - if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) && - (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) { + else if (sa_tr->x == sb_tl->x && sa_br->x == sb_bl->x) { /* sa_a to left of sa_b = E */ + if ((MIN2(sa_tr->y, sb_tl->y) - MAX2(sa_br->y, sb_bl->y)) > AREAJOINTOLERANCEY) { return 2; } } - else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/ - if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) && - (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) { + else if (sa_bl->y == sb_tl->y && sa_br->y == sb_tr->y) { /* sa_a on top of sa_b = S */ + if ((MIN2(sa_br->x, sb_tr->x) - MAX2(sa_bl->x, sb_tl->x)) > AREAJOINTOLERANCEX) { return 3; } } @@ -327,6 +328,39 @@ int area_getorientation(ScrArea *area, ScrArea *sb) return -1; } +/** + * Get alignment offset of adjacent areas. 'dir' value is like #area_getorientation(). + */ +void area_getoffsets( + ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2) +{ + if (sa_a == NULL || sa_b == NULL) { + *r_offset1 = INT_MAX; + *r_offset2 = INT_MAX; + } + else if (dir == SCREEN_DIR_W) { /* West: sa on right and sa_b to the left. */ + *r_offset1 = sa_b->v3->vec.y - sa_a->v2->vec.y; + *r_offset2 = sa_b->v4->vec.y - sa_a->v1->vec.y; + } + else if (dir == SCREEN_DIR_N) { /* North: sa below and sa_b above. */ + *r_offset1 = sa_a->v2->vec.x - sa_b->v1->vec.x; + *r_offset2 = sa_a->v3->vec.x - sa_b->v4->vec.x; + } + else if (dir == SCREEN_DIR_E) { /* East: sa on left and sa_b to the right. */ + *r_offset1 = sa_b->v2->vec.y - sa_a->v3->vec.y; + *r_offset2 = sa_b->v1->vec.y - sa_a->v4->vec.y; + } + else if (dir == SCREEN_DIR_S) { /* South: sa above and sa_b below. */ + *r_offset1 = sa_a->v1->vec.x - sa_b->v2->vec.x; + *r_offset2 = sa_a->v4->vec.x - sa_b->v3->vec.x; + } + else { + BLI_assert(dir == SCREEN_DIR_NONE); + *r_offset1 = INT_MAX; + *r_offset2 = INT_MAX; + } +} + /* Screen verts with horizontal position equal to from_x are moved to to_x. */ static void screen_verts_halign(const wmWindow *win, const bScreen *screen, @@ -358,11 +392,11 @@ static void screen_verts_valign(const wmWindow *win, /* Adjust all screen edges to allow joining two areas. 'dir' value is like area_getorientation(). */ static void screen_areas_align( - bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const int dir) + bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const eScreenDir dir) { wmWindow *win = CTX_wm_window(C); - if (ELEM(dir, 0, 2)) { + if (SCREEN_DIR_IS_HORIZONTAL(dir)) { /* horizontal join, use average for new top and bottom. */ int top = (sa1->v2->vec.y + sa2->v2->vec.y) / 2; int bottom = (sa1->v4->vec.y + sa2->v4->vec.y) / 2; @@ -390,41 +424,47 @@ static void screen_areas_align( } } -/* Helper function to join 2 areas, it has a return value, 0=failed 1=success - * used by the split, join operators - */ -int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) +/* Simple join of two areas without any splitting. Will return false if not possible. */ +static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) { - int dir = area_getorientation(sa1, sa2); + const eScreenDir dir = area_getorientation(sa1, sa2); + if (dir == SCREEN_DIR_NONE) { + return false; + } - if (dir == -1) { - return 0; + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + + int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX; + if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) { + return false; } - /* Align areas if they are not. Do sanity checking before getting here. */ + /* Align areas if they are not. */ screen_areas_align(C, screen, sa1, sa2, dir); - if (dir == 0) { /* sa1 to right of sa2 = W */ - sa1->v1 = sa2->v1; /* BL */ - sa1->v2 = sa2->v2; /* TL */ + if (dir == SCREEN_DIR_W) { /* sa1 to right of sa2 = West. */ + sa1->v1 = sa2->v1; /* BL */ + sa1->v2 = sa2->v2; /* TL */ screen_geom_edge_add(screen, sa1->v2, sa1->v3); screen_geom_edge_add(screen, sa1->v1, sa1->v4); } - else if (dir == 1) { /* sa1 to bottom of sa2 = N */ - sa1->v2 = sa2->v2; /* TL */ - sa1->v3 = sa2->v3; /* TR */ + else if (dir == SCREEN_DIR_N) { /* sa1 to bottom of sa2 = North. */ + sa1->v2 = sa2->v2; /* TL */ + sa1->v3 = sa2->v3; /* TR */ screen_geom_edge_add(screen, sa1->v1, sa1->v2); screen_geom_edge_add(screen, sa1->v3, sa1->v4); } - else if (dir == 2) { /* sa1 to left of sa2 = E */ - sa1->v3 = sa2->v3; /* TR */ - sa1->v4 = sa2->v4; /* BR */ + else if (dir == SCREEN_DIR_E) { /* sa1 to left of sa2 = East. */ + sa1->v3 = sa2->v3; /* TR */ + sa1->v4 = sa2->v4; /* BR */ screen_geom_edge_add(screen, sa1->v2, sa1->v3); screen_geom_edge_add(screen, sa1->v1, sa1->v4); } - else if (dir == 3) { /* sa1 on top of sa2 = S */ - sa1->v1 = sa2->v1; /* BL */ - sa1->v4 = sa2->v4; /* BR */ + else if (dir == SCREEN_DIR_S) { /* sa1 on top of sa2 = South. */ + sa1->v1 = sa2->v1; /* BL */ + sa1->v4 = sa2->v4; /* BR */ screen_geom_edge_add(screen, sa1->v1, sa1->v2); screen_geom_edge_add(screen, sa1->v3, sa1->v4); } @@ -434,7 +474,104 @@ int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) /* Update preview thumbnail */ BKE_icon_changed(screen->id.icon_id); - return 1; + return true; +} + +/* Slice off and return new area. "Reverse" gives right/bottom, rather than left/top. */ +static ScrArea *screen_area_trim( + bContext *C, bScreen *screen, ScrArea **area, int size, eScreenDir dir, bool reverse) +{ + const bool vertical = SCREEN_DIR_IS_VERTICAL(dir); + if (abs(size) < (vertical ? AREAJOINTOLERANCEX : AREAJOINTOLERANCEY)) { + return NULL; + } + + /* Measurement with ScrVerts because winx and winy might not be correct at this time. */ + float fac = abs(size) / (float)(vertical ? ((*area)->v3->vec.x - (*area)->v1->vec.x) : + ((*area)->v3->vec.y - (*area)->v1->vec.y)); + fac = (reverse == vertical) ? 1.0f - fac : fac; + ScrArea *newsa = area_split( + CTX_wm_window(C), screen, *area, vertical ? SCREEN_AXIS_V : SCREEN_AXIS_H, fac, true); + + /* area_split always returns smallest of the two areas, so might have to swap. */ + if (((fac > 0.5f) == vertical) != reverse) { + ScrArea *temp = *area; + *area = newsa; + newsa = temp; + } + + return newsa; +} + +/* Join any two neighboring areas. Might create new areas, kept if over min_remainder. */ +static bool screen_area_join_ex( + bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, bool close_all_remainders) +{ + const eScreenDir dir = area_getorientation(sa1, sa2); + if (dir == SCREEN_DIR_NONE) { + return false; + } + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + + /* Split Left/Top into new area if overhanging. */ + ScrArea *side1 = screen_area_trim(C, screen, (offset1 > 0) ? &sa2 : &sa1, offset1, dir, false); + + /* Split Right/Bottom into new area if overhanging. */ + ScrArea *side2 = screen_area_trim(C, screen, (offset2 > 0) ? &sa1 : &sa2, offset2, dir, true); + + /* The two areas now line up, so join them. */ + screen_area_join_aligned(C, screen, sa1, sa2); + + if (close_all_remainders || offset1 < 0 || offset2 > 0) { + /* Close both if trimming `sa1`. */ + screen_area_close(C, screen, side1); + screen_area_close(C, screen, side2); + } + + BKE_icon_changed(screen->id.icon_id); + return true; +} + +/* Join any two neighboring areas. Might involve complex changes. */ +int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) +{ + return screen_area_join_ex(C, screen, sa1, sa2, false); +} + +/* Close a screen area, allowing most-aligned neighbor to take its place. */ +bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area) +{ + if (area == NULL) { + return false; + } + + ScrArea *sa2 = NULL; + float best_alignment = 0.0f; + + LISTBASE_FOREACH (ScrArea *, neighbor, &screen->areabase) { + const eScreenDir dir = area_getorientation(area, neighbor); + /* Must at least partially share an edge and not be a global area. */ + if ((dir != SCREEN_DIR_NONE) && (neighbor->global == NULL)) { + /* Winx/Winy might not be updated yet, so get lengths from verts. */ + const bool vertical = SCREEN_DIR_IS_VERTICAL(dir); + const int area_length = vertical ? (area->v3->vec.x - area->v1->vec.x) : + (area->v3->vec.y - area->v1->vec.y); + const int ar_length = vertical ? (neighbor->v3->vec.x - neighbor->v1->vec.x) : + (neighbor->v3->vec.y - neighbor->v1->vec.y); + /* Calculate the ratio of the lengths of the shared edges. */ + float alignment = MIN2(area_length, ar_length) / (float)MAX2(area_length, ar_length); + if (alignment > best_alignment) { + best_alignment = alignment; + sa2 = neighbor; + } + } + } + + /* Join from neighbor into this area to close it. */ + return screen_area_join_ex(C, screen, sa2, area, true); } /* ****************** EXPORTED API TO OTHER MODULES *************************** */ diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c index ac159f4d633..51edad0332b 100644 --- a/source/blender/editors/screen/screen_geometry.c +++ b/source/blender/editors/screen/screen_geometry.c @@ -304,7 +304,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) */ short screen_geom_find_area_split_point(const ScrArea *area, const rcti *window_rect, - char dir, + const eScreenAxis dir_axis, float fac) { const int cur_area_width = screen_geom_area_width(area); @@ -313,17 +313,21 @@ short screen_geom_find_area_split_point(const ScrArea *area, const short area_min_y = ED_area_headersize(); /* area big enough? */ - if ((dir == 'v') && (cur_area_width <= 2 * area_min_x)) { - return 0; + if (dir_axis == SCREEN_AXIS_V) { + if (cur_area_width <= 2 * area_min_x) { + return 0; + } } - if ((dir == 'h') && (cur_area_height <= 2 * area_min_y)) { - return 0; + else if (dir_axis == SCREEN_AXIS_H) { + if (cur_area_height <= 2 * area_min_y) { + return 0; + } } /* to be sure */ CLAMP(fac, 0.0f, 1.0f); - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { short y = area->v1->vec.y + round_fl_to_short(fac * cur_area_height); int area_min = area_min_y; @@ -373,13 +377,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge) { bScreen *screen = WM_window_get_active_screen(win); - /* 'dir' is the direction of EDGE */ - char dir; + /* 'dir_axis' is the direction of EDGE */ + eScreenAxis dir_axis; if (edge->v1->vec.x == edge->v2->vec.x) { - dir = 'v'; + dir_axis = SCREEN_AXIS_V; } else { - dir = 'h'; + dir_axis = SCREEN_AXIS_H; } ED_screen_verts_iter(win, screen, sv) @@ -396,13 +400,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge) oneselected = false; LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) { if (se->v1->flag + se->v2->flag == 1) { - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { if (se->v1->vec.y == se->v2->vec.y) { se->v1->flag = se->v2->flag = 1; oneselected = true; } } - if (dir == 'v') { + else if (dir_axis == SCREEN_AXIS_V) { if (se->v1->vec.x == se->v2->vec.x) { se->v1->flag = se->v2->flag = 1; oneselected = true; diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index c51ff559786..683f2844371 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -29,12 +29,37 @@ struct bContextDataResult; /* internal exports only */ +typedef enum eScreenDir { + /** This can mean unset, unknown or invalid. */ + SCREEN_DIR_NONE = -1, + /** West/Left. */ + SCREEN_DIR_W = 0, + /** North/Up. */ + SCREEN_DIR_N = 1, + /** East/Right. */ + SCREEN_DIR_E = 2, + /** South/Down. */ + SCREEN_DIR_S = 3, +} eScreenDir; + +#define SCREEN_DIR_IS_VERTICAL(dir) (ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S)) +#define SCREEN_DIR_IS_HORIZONTAL(dir) (ELEM(dir, SCREEN_DIR_W, SCREEN_DIR_E)) + +typedef enum eScreenAxis { + /** Horizontal. */ + SCREEN_AXIS_H = 'h', + /** Vertical. */ + SCREEN_AXIS_V = 'v', +} eScreenAxis; + #define AZONESPOTW UI_HEADER_OFFSET /* width of corner #AZone - max */ #define AZONESPOTH (0.6f * U.widget_unit) /* height of corner #AZone */ #define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */ #define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */ -#define AREAJOINTOLERANCE (1.0f * U.widget_unit) /* Edges must be close to allow joining. */ +/* Edges must be within these to allow joining. */ +#define AREAJOINTOLERANCEX (AREAMINX * U.dpi_fac) +#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac) /* Expanded interaction influence of area borders. */ #define BORDERPADDING (U.dpi_fac + U.pixelsize) @@ -44,6 +69,10 @@ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free) void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src); void region_toggle_hidden(struct bContext *C, ARegion *region, const bool do_fade); +/* screen_draw.c */ +void screen_draw_join_highlight(struct ScrArea *sa1, struct ScrArea *sa2); +void screen_draw_split_preview(struct ScrArea *area, const eScreenAxis dir_axis, const float fac); + /* screen_edit.c */ bScreen *screen_add(struct Main *bmain, const char *name, const rcti *rect); void screen_data_copy(bScreen *to, bScreen *from); @@ -54,11 +83,17 @@ void screen_change_prepare(bScreen *screen_old, struct Main *bmain, struct bContext *C, wmWindow *win); -ScrArea *area_split( - const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge); +ScrArea *area_split(const wmWindow *win, + bScreen *screen, + ScrArea *area, + const eScreenAxis dir_axis, + const float fac, + const bool merge); int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2); -int area_getorientation(ScrArea *area, ScrArea *sb); - +eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b); +void area_getoffsets( + ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2); +bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area); struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]); /* screen_geometry.c */ @@ -80,7 +115,7 @@ ScrEdge *screen_geom_find_active_scredge(const wmWindow *win, void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen); short screen_geom_find_area_split_point(const ScrArea *area, const rcti *window_rect, - char dir, + const eScreenAxis dir_axis, float fac); void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 765877d6a8e..6b8d4e73f12 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -694,7 +694,9 @@ static bool screen_active_editable(bContext *C) typedef struct sActionzoneData { ScrArea *sa1, *sa2; AZone *az; - int x, y, gesture_dir, modifier; + int x, y; + eScreenDir gesture_dir; + int modifier; } sActionzoneData; /* quick poll to save operators to be created and handled */ @@ -1045,16 +1047,16 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Calculate gesture cardinal direction. */ if (delta_y > abs(delta_x)) { - sad->gesture_dir = 'n'; + sad->gesture_dir = SCREEN_DIR_N; } else if (delta_x >= abs(delta_y)) { - sad->gesture_dir = 'e'; + sad->gesture_dir = SCREEN_DIR_E; } else if (delta_y < -abs(delta_x)) { - sad->gesture_dir = 's'; + sad->gesture_dir = SCREEN_DIR_S; } else { - sad->gesture_dir = 'w'; + sad->gesture_dir = SCREEN_DIR_W; } bool is_gesture; @@ -1071,22 +1073,24 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Are we still in same area? */ if (BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y) == sad->sa1) { /* Same area, so possible split. */ - WM_cursor_set( - win, (ELEM(sad->gesture_dir, 'n', 's')) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); + WM_cursor_set(win, + SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT : + WM_CURSOR_V_SPLIT); is_gesture = (delta_max > split_threshold); } else { /* Different area, so possible join. */ - if (sad->gesture_dir == 'n') { + if (sad->gesture_dir == SCREEN_DIR_N) { WM_cursor_set(win, WM_CURSOR_N_ARROW); } - else if (sad->gesture_dir == 's') { + else if (sad->gesture_dir == SCREEN_DIR_S) { WM_cursor_set(win, WM_CURSOR_S_ARROW); } - else if (sad->gesture_dir == 'e') { + else if (sad->gesture_dir == SCREEN_DIR_E) { WM_cursor_set(win, WM_CURSOR_E_ARROW); } else { + BLI_assert(sad->gesture_dir == SCREEN_DIR_W); WM_cursor_set(win, WM_CURSOR_W_ARROW); } is_gesture = (delta_max > join_threshold); @@ -1395,6 +1399,58 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Area Close Operator + * + * Close selected area, replace by expanding a neighbor + * \{ */ + +/* operator callback */ +static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +{ + ScrArea *area = CTX_wm_area(C); + if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) { + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static bool area_close_poll(bContext *C) +{ + if (!ED_operator_areaactive(C)) { + return false; + } + + ScrArea *area = CTX_wm_area(C); + + if (ED_area_is_global(area)) { + return false; + } + + bScreen *screen = CTX_wm_screen(C); + + /* Can this area join with ANY other area? */ + LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) { + if (area_getorientation(ar, area) != -1) { + return true; + } + } + + return false; +} + +static void SCREEN_OT_area_close(wmOperatorType *ot) +{ + ot->name = "Close Area"; + ot->description = "Close selected area"; + ot->idname = "SCREEN_OT_area_close"; + ot->invoke = area_close_invoke; + ot->poll = area_close_poll; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Move Area Edge Operator * \{ */ @@ -1428,7 +1484,7 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) typedef struct sAreaMoveData { int bigger, smaller, origval, step; - char dir; + eScreenAxis dir_axis; enum AreaMoveSnapType { /* Snapping disabled */ SNAP_NONE = 0, @@ -1447,7 +1503,7 @@ typedef struct sAreaMoveData { * need window bounds in order to get correct limits */ static void area_move_set_limits(wmWindow *win, bScreen *screen, - int dir, + const eScreenAxis dir_axis, int *bigger, int *smaller, bool *use_bigger_smaller_snap) @@ -1500,7 +1556,7 @@ static void area_move_set_limits(wmWindow *win, WM_window_rect_calc(win, &window_rect); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { int areamin = ED_area_headersize(); if (area->v1->vec.y > window_rect.ymin) { @@ -1563,8 +1619,8 @@ static bool area_move_init(bContext *C, wmOperator *op) sAreaMoveData *md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData"); op->customdata = md; - md->dir = screen_geom_edge_is_horizontal(actedge) ? 'h' : 'v'; - if (md->dir == 'h') { + md->dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_H : SCREEN_AXIS_V; + if (md->dir_axis == SCREEN_AXIS_H) { md->origval = actedge->v1->vec.y; } else { @@ -1579,7 +1635,8 @@ static bool area_move_init(bContext *C, wmOperator *op) } bool use_bigger_smaller_snap = false; - area_move_set_limits(win, screen, md->dir, &md->bigger, &md->smaller, &use_bigger_smaller_snap); + area_move_set_limits( + win, screen, md->dir_axis, &md->bigger, &md->smaller, &use_bigger_smaller_snap); md->snap_type = use_bigger_smaller_snap ? SNAP_BIGGER_SMALLER_ONLY : SNAP_AREAGRID; @@ -1590,7 +1647,7 @@ static int area_snap_calc_location(const bScreen *screen, const enum AreaMoveSnapType snap_type, const int delta, const int origval, - const int dir, + const eScreenAxis dir_axis, const int bigger, const int smaller) { @@ -1615,7 +1672,7 @@ static int area_snap_calc_location(const bScreen *screen, break; case SNAP_FRACTION_AND_ADJACENT: { - const int axis = (dir == 'v') ? 0 : 1; + const int axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1; int snap_dist_best = INT_MAX; { const float div_array[] = { @@ -1683,7 +1740,7 @@ static int area_snap_calc_location(const bScreen *screen, static void area_move_apply_do(const bContext *C, int delta, const int origval, - const int dir, + const eScreenAxis dir_axis, const int bigger, const int smaller, const enum AreaMoveSnapType snap_type) @@ -1701,11 +1758,12 @@ static void area_move_apply_do(const bContext *C, final_loc = origval + delta; } else { - final_loc = area_snap_calc_location(screen, snap_type, delta, origval, dir, bigger, smaller); + final_loc = area_snap_calc_location( + screen, snap_type, delta, origval, dir_axis, bigger, smaller); } BLI_assert(final_loc != -1); - short axis = (dir == 'v') ? 0 : 1; + short axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1; ED_screen_verts_iter(win, screen, v1) { @@ -1761,7 +1819,7 @@ static void area_move_apply(bContext *C, wmOperator *op) sAreaMoveData *md = op->customdata; int delta = RNA_int_get(op->ptr, "delta"); - area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->snap_type); + area_move_apply_do(C, delta, md->origval, md->dir_axis, md->bigger, md->smaller, md->snap_type); } static void area_move_exit(bContext *C, wmOperator *op) @@ -1826,7 +1884,7 @@ static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event) int x = RNA_int_get(op->ptr, "x"); int y = RNA_int_get(op->ptr, "y"); - int delta = (md->dir == 'v') ? event->x - x : event->y - y; + const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->x - x : event->y - y; RNA_int_set(op->ptr, "delta", delta); area_move_apply(C, op); @@ -1892,7 +1950,7 @@ static void SCREEN_OT_area_move(wmOperatorType *ot) /* * operator state vars: * fac spit point - * dir direction 'v' or 'h' + * dir direction #SCREEN_AXIS_V or #SCREEN_AXIS_H * * operator customdata: * area pointer to (active) area @@ -1929,7 +1987,7 @@ typedef struct sAreaSplitData { int delta; /* delta move edge */ int origmin, origsize; /* to calculate fac, for property storage */ int previewmode; /* draw previewline, then split */ - void *draw_callback; /* call `ED_screen_draw_split_preview` */ + void *draw_callback; /* call `screen_draw_split_preview` */ bool do_snap; ScrEdge *nedge; /* new edge */ @@ -1944,10 +2002,10 @@ static void area_split_draw_cb(const struct wmWindow *UNUSED(win), void *userdat sAreaSplitData *sd = op->customdata; if (sd->sarea) { - int dir = RNA_enum_get(op->ptr, "direction"); + const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction"); float fac = RNA_float_get(op->ptr, "factor"); - ED_screen_draw_split_preview(sd->sarea, dir, fac); + screen_draw_split_preview(sd->sarea, dir_axis, fac); } } @@ -1974,14 +2032,18 @@ static bool area_split_init(bContext *C, wmOperator *op) } /* required properties */ - int dir = RNA_enum_get(op->ptr, "direction"); + const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction"); /* minimal size */ - if (dir == 'v' && area->winx < 2 * AREAMINX) { - return false; + if (dir_axis == SCREEN_AXIS_V) { + if (area->winx < 2 * AREAMINX) { + return false; + } } - if (dir == 'h' && area->winy < 2 * ED_area_headersize()) { - return false; + else { + if (area->winy < 2 * ED_area_headersize()) { + return false; + } } /* custom data */ @@ -1989,7 +2051,7 @@ static bool area_split_init(bContext *C, wmOperator *op) op->customdata = sd; sd->sarea = area; - if (dir == 'v') { + if (dir_axis == SCREEN_AXIS_V) { sd->origmin = area->v1->vec.x; sd->origsize = area->v4->vec.x - sd->origmin; } @@ -2038,9 +2100,9 @@ static bool area_split_apply(bContext *C, wmOperator *op) sAreaSplitData *sd = (sAreaSplitData *)op->customdata; float fac = RNA_float_get(op->ptr, "factor"); - int dir = RNA_enum_get(op->ptr, "direction"); + const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction"); - sd->narea = area_split(win, screen, sd->sarea, dir, fac, 0); /* 0 = no merge */ + sd->narea = area_split(win, screen, sd->sarea, dir_axis, fac, false); /* false = no merge */ if (sd->narea == NULL) { return false; @@ -2057,7 +2119,7 @@ static bool area_split_apply(bContext *C, wmOperator *op) sd->nedge->v1->editflag = 1; sd->nedge->v2->editflag = 1; - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { sd->origval = sd->nedge->v1->vec.y; } else { @@ -2106,8 +2168,8 @@ static void area_split_exit(bContext *C, wmOperator *op) static void area_split_preview_update_cursor(bContext *C, wmOperator *op) { wmWindow *win = CTX_wm_window(C); - int dir = RNA_enum_get(op->ptr, "direction"); - WM_cursor_set(win, dir == 'h' ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); + const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction"); + WM_cursor_set(win, (dir_axis == SCREEN_AXIS_H) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); } /* UI callback, adds new handler */ @@ -2123,7 +2185,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) PropertyRNA *prop_factor = RNA_struct_find_property(op->ptr, "factor"); PropertyRNA *prop_cursor = RNA_struct_find_property(op->ptr, "cursor"); - int dir; + eScreenAxis dir_axis; if (event->type == EVT_ACTIONZONE_AREA) { sActionzoneData *sad = event->customdata; @@ -2151,12 +2213,12 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) float factor; /* Prepare operator state vars. */ - if (ELEM(sad->gesture_dir, 'n', 's')) { - dir = 'h'; + if (SCREEN_DIR_IS_VERTICAL(sad->gesture_dir)) { + dir_axis = SCREEN_AXIS_H; factor = factor_h; } else { - dir = 'v'; + dir_axis = SCREEN_AXIS_V; factor = factor_v; } @@ -2166,7 +2228,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_property_float_set(op->ptr, prop_factor, factor); - RNA_property_enum_set(op->ptr, prop_dir, dir); + RNA_property_enum_set(op->ptr, prop_dir, dir_axis); /* general init, also non-UI case, adds customdata, sets area and defaults */ if (!area_split_init(C, op)) { @@ -2178,8 +2240,8 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (area == NULL) { return OPERATOR_CANCELLED; } - dir = RNA_property_enum_get(op->ptr, prop_dir); - if (dir == 'h') { + dir_axis = RNA_property_enum_get(op->ptr, prop_dir); + if (dir_axis == SCREEN_AXIS_H) { RNA_property_float_set( op->ptr, prop_factor, ((float)(event->x - area->v1->vec.x)) / (float)area->winx); } @@ -2212,9 +2274,9 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - dir = screen_geom_edge_is_horizontal(actedge) ? 'v' : 'h'; + dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_V : SCREEN_AXIS_H; - RNA_property_enum_set(op->ptr, prop_dir, dir); + RNA_property_enum_set(op->ptr, prop_dir, dir_axis); /* special case, adds customdata, sets defaults */ if (!area_split_menu_init(C, op)) { @@ -2227,7 +2289,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == EVT_ACTIONZONE_AREA) { /* do the split */ if (area_split_apply(C, op)) { - area_move_set_limits(win, screen, dir, &sd->bigger, &sd->smaller, NULL); + area_move_set_limits(win, screen, dir_axis, &sd->bigger, &sd->smaller, NULL); /* add temp handler for edge move or cancel */ G.moving |= G_TRANSFORM_WM; @@ -2315,8 +2377,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) else { if (event->val == KM_PRESS) { if (sd->sarea) { - int dir = RNA_property_enum_get(op->ptr, prop_dir); - RNA_property_enum_set(op->ptr, prop_dir, (dir == 'v') ? 'h' : 'v'); + const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir); + RNA_property_enum_set( + op->ptr, prop_dir, (dir_axis == SCREEN_AXIS_V) ? SCREEN_AXIS_H : SCREEN_AXIS_V); area_split_preview_update_cursor(C, op); update_factor = true; } @@ -2337,9 +2400,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (update_factor) { - const int dir = RNA_property_enum_get(op->ptr, prop_dir); + const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir); - sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval; + sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->x - sd->origval : event->y - sd->origval; if (sd->previewmode == 0) { if (sd->do_snap) { @@ -2347,12 +2410,12 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) SNAP_FRACTION_AND_ADJACENT, sd->delta, sd->origval, - dir, + dir_axis, sd->bigger, sd->smaller); sd->delta = snap_loc - sd->origval; } - area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, SNAP_NONE); + area_move_apply_do(C, sd->delta, sd->origval, dir_axis, sd->bigger, sd->smaller, SNAP_NONE); } else { if (sd->sarea) { @@ -2363,7 +2426,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) if (sd->sarea) { ScrArea *area = sd->sarea; - if (dir == 'v') { + if (dir_axis == SCREEN_AXIS_V) { sd->origmin = area->v1->vec.x; sd->origsize = area->v4->vec.x - sd->origmin; } @@ -2379,7 +2442,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) SNAP_FRACTION_AND_ADJACENT, sd->delta, sd->origval, - dir, + dir_axis, sd->origmin + sd->origsize, -sd->origmin); @@ -2401,8 +2464,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) } static const EnumPropertyItem prop_direction_items[] = { - {'h', "HORIZONTAL", 0, "Horizontal", ""}, - {'v', "VERTICAL", 0, "Vertical", ""}, + {SCREEN_AXIS_H, "HORIZONTAL", 0, "Horizontal", ""}, + {SCREEN_AXIS_V, "VERTICAL", 0, "Vertical", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2423,7 +2486,7 @@ static void SCREEN_OT_area_split(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL; /* rna */ - RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", ""); + RNA_def_enum(ot->srna, "direction", prop_direction_items, SCREEN_AXIS_H, "Direction", ""); RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0); RNA_def_int_vector( ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX); @@ -3218,9 +3281,10 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot) */ typedef struct sAreaJoinData { - ScrArea *sa1; /* first area to be considered */ - ScrArea *sa2; /* second area to be considered */ - void *draw_callback; /* call `ED_screen_draw_join_shape` */ + ScrArea *sa1; /* Potential source area (kept). */ + ScrArea *sa2; /* Potential target area (removed or reduced). */ + eScreenDir dir; /* Direction of potential join. */ + void *draw_callback; /* call #screen_draw_join_highlight */ } sAreaJoinData; @@ -3229,8 +3293,8 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata const wmOperator *op = userdata; sAreaJoinData *sd = op->customdata; - if (sd->sa1 && sd->sa2) { - ED_screen_draw_join_shape(sd->sa1, sd->sa2); + if (sd->sa1 && sd->sa2 && (sd->dir != SCREEN_DIR_NONE)) { + screen_draw_join_highlight(sd->sa1, sd->sa2); } } @@ -3252,6 +3316,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s jd->sa1 = sa1; jd->sa2 = sa2; + jd->dir = SCREEN_DIR_NONE; op->customdata = jd; @@ -3264,7 +3329,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s static bool area_join_apply(bContext *C, wmOperator *op) { sAreaJoinData *jd = (sAreaJoinData *)op->customdata; - if (!jd) { + if (!jd || (jd->dir == SCREEN_DIR_NONE)) { return false; } @@ -3366,61 +3431,30 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) case MOUSEMOVE: { ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); - int dir = -1; + jd->dir = area_getorientation(jd->sa1, jd->sa2); - if (area) { - if (jd->sa1 != area) { - dir = area_getorientation(jd->sa1, area); - if (dir != -1) { - jd->sa2 = area; - } - else { - /* we are not bordering on the previously selected area - * we check if area has common border with the one marked for removal - * in this case we can swap areas. - */ - dir = area_getorientation(area, jd->sa2); - if (dir != -1) { - jd->sa1 = jd->sa2; - jd->sa2 = area; - } - else { - jd->sa2 = NULL; - } - } - WM_event_add_notifier(C, NC_WINDOW, NULL); - } - else { - /* we are back in the area previously selected for keeping - * we swap the areas if possible to allow user to choose */ - if (jd->sa2 != NULL) { - jd->sa1 = jd->sa2; - jd->sa2 = area; - dir = area_getorientation(jd->sa1, jd->sa2); - if (dir == -1) { - printf("oops, didn't expect that!\n"); - } - } - else { - dir = area_getorientation(jd->sa1, area); - if (dir != -1) { - jd->sa2 = area; - } - } - WM_event_add_notifier(C, NC_WINDOW, NULL); - } + if (area == jd->sa1) { + /* Hovering current source, so change direction. */ + jd->sa1 = jd->sa2; + jd->sa2 = area; + jd->dir = area_getorientation(jd->sa1, jd->sa2); + } + else if (area != jd->sa2) { + jd->dir = SCREEN_DIR_NONE; } - if (dir == 1) { + WM_event_add_notifier(C, NC_WINDOW, NULL); + + if (jd->dir == SCREEN_DIR_N) { WM_cursor_set(win, WM_CURSOR_N_ARROW); } - else if (dir == 3) { + else if (jd->dir == SCREEN_DIR_S) { WM_cursor_set(win, WM_CURSOR_S_ARROW); } - else if (dir == 2) { + else if (jd->dir == SCREEN_DIR_E) { WM_cursor_set(win, WM_CURSOR_E_ARROW); } - else if (dir == 0) { + else if (jd->dir == SCREEN_DIR_W) { WM_cursor_set(win, WM_CURSOR_W_ARROW); } else { @@ -3431,6 +3465,10 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) } case LEFTMOUSE: if (event->val == KM_RELEASE) { + if (jd->dir == SCREEN_DIR_NONE) { + area_join_cancel(C, op); + return OPERATOR_CANCELLED; + } ED_area_tag_redraw(jd->sa1); ED_area_tag_redraw(jd->sa2); @@ -3501,7 +3539,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent &ptr); /* store initial mouse cursor position. */ RNA_int_set_array(&ptr, "cursor", &event->x); - RNA_enum_set(&ptr, "direction", 'v'); + RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V); /* Horizontal Split */ uiItemFullO(layout, @@ -3514,7 +3552,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent &ptr); /* store initial mouse cursor position. */ RNA_int_set_array(&ptr, "cursor", &event->x); - RNA_enum_set(&ptr, "direction", 'h'); + RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H); if (sa1 && sa2) { uiItemS(layout); @@ -4077,6 +4115,69 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot) /** \name Region Context Menu Operator (Header/Footer/Navbar) * \{ */ +static void screen_area_menu_items(ScrArea *area, uiLayout *layout) +{ + if (ED_area_is_global(area)) { + return; + } + + PointerRNA ptr; + + /* Mouse position as if in middle of area. */ + const int loc[2] = {BLI_rcti_cent_x(&area->totrct), BLI_rcti_cent_y(&area->totrct)}; + + /* Vertical Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Vertical Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + + RNA_int_set_array(&ptr, "cursor", loc); + RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V); + + /* Horizontal Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Horizontal Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + + RNA_int_set_array(&ptr, "cursor", &loc[0]); + RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H); + + uiItemS(layout); + + if (area->spacetype != SPACE_FILE) { + uiItemO(layout, + area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"), + ICON_NONE, + "SCREEN_OT_screen_full_area"); + + if (!area->full) { + uiItemFullO(layout, + "SCREEN_OT_screen_full_area", + IFACE_("Full Screen Area"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + RNA_boolean_set(&ptr, "use_hide_panels", true); + } + } + + uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_dupli"); + uiItemS(layout); + uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_close"); +} + void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) { ScrArea *area = CTX_wm_area(C); @@ -4110,17 +4211,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN if (!ELEM(area->spacetype, SPACE_TOPBAR)) { uiItemS(layout); - uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); - } - - /* File browser should be fullscreen all the time, top-bar should - * never be. But other regions can be maximized/restored. */ - if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) { uiItemS(layout); - - const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); - uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); + screen_area_menu_items(area, layout); } } @@ -4142,14 +4235,8 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); - /* File browser should be fullscreen all the time, top-bar should - * never be. But other regions can be maximized/restored... */ - if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) { - uiItemS(layout); - - const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); - uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); - } + uiItemS(layout); + screen_area_menu_items(area, layout); } void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) @@ -5344,7 +5431,7 @@ static void context_cycle_prop_get(bScreen *screen, static int space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - const int direction = RNA_enum_get(op->ptr, "direction"); + const eScreenCycle direction = RNA_enum_get(op->ptr, "direction"); PointerRNA ptr; PropertyRNA *prop; @@ -5393,7 +5480,7 @@ static int space_workspace_cycle_invoke(bContext *C, wmOperator *op, const wmEve } Main *bmain = CTX_data_main(C); - const int direction = RNA_enum_get(op->ptr, "direction"); + const eScreenCycle direction = RNA_enum_get(op->ptr, "direction"); WorkSpace *workspace_src = WM_window_get_active_workspace(win); WorkSpace *workspace_dst = NULL; @@ -5469,6 +5556,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_area_move); WM_operatortype_append(SCREEN_OT_area_split); WM_operatortype_append(SCREEN_OT_area_join); + WM_operatortype_append(SCREEN_OT_area_close); WM_operatortype_append(SCREEN_OT_area_options); WM_operatortype_append(SCREEN_OT_area_dupli); WM_operatortype_append(SCREEN_OT_area_swap); diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 3df63d423e2..6df96b1e30f 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -199,10 +199,9 @@ static bool screenshot_draw_check_prop(PointerRNA *UNUSED(ptr), return !(STREQ(prop_id, "filepath")); } -static void screenshot_draw(bContext *C, wmOperator *op) +static void screenshot_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); ScreenshotData *scd = op->customdata; uiLayoutSetPropSep(layout, true); @@ -214,9 +213,8 @@ static void screenshot_draw(bContext *C, wmOperator *op) uiTemplateImageSettings(layout, &ptr, false); /* main draw call */ - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA( - layout, &ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + layout, op->ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); } static bool screenshot_poll(bContext *C) diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 7740fb42c37..3829aeebbeb 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1324,6 +1324,13 @@ static bool paint_cursor_context_init(bContext *C, copy_v3_fl(pcontext->outline_col, 0.8f); } + const bool is_brush_tool = PAINT_brush_tool_poll(C); + if (!is_brush_tool) { + /* Use a default color for tools that are not brushes. */ + pcontext->outline_alpha = 0.8f; + copy_v3_fl(pcontext->outline_col, 0.8f); + } + pcontext->is_stroke_active = pcontext->ups->stroke_active; return true; @@ -1610,9 +1617,11 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * pcontext->radius); } + const bool is_brush_tool = PAINT_brush_tool_poll(pcontext->C); + /* Pose brush updates and rotation origins. */ - if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) { /* Just after switching to the Pose Brush, the active vertex can be the same and the * cursor won't be tagged to update, so always initialize the preview chain if it is * null before drawing it. */ @@ -1645,7 +1654,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * 2); } - if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { paint_cursor_preview_boundary_data_update(pcontext, update_previews); paint_cursor_preview_boundary_data_pivot_draw(pcontext); } @@ -1666,17 +1675,18 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * GPU_matrix_mul(pcontext->vc.obact->obmat); /* Drawing Cursor overlays in 3D object space. */ - if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_GRAB && + (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) { SCULPT_geometry_preview_lines_update(pcontext->C, pcontext->ss, pcontext->radius); sculpt_geometry_preview_lines_draw( pcontext->pos, pcontext->brush, pcontext->is_multires, pcontext->ss); } - if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) { paint_cursor_pose_brush_segments_draw(pcontext); } - if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { SCULPT_boundary_edges_preview_draw( pcontext->pos, pcontext->ss, pcontext->outline_col, pcontext->outline_alpha); SCULPT_boundary_pivot_line_preview_draw(pcontext->pos, pcontext->ss); @@ -1692,7 +1702,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * paint_cursor_draw_main_inactive_cursor(pcontext); /* Cloth brush local simulation areas. */ - if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_CLOTH && brush->cloth_simulation_area_type != BRUSH_CLOTH_SIMULATION_AREA_GLOBAL) { const float white[3] = {1.0f, 1.0f, 1.0f}; const float zero_v[3] = {0.0f}; @@ -1704,7 +1714,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * } /* Layer brush height. */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_LAYER) { SCULPT_layer_brush_height_preview_draw(pcontext->pos, brush, pcontext->radius, diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 3ca0d853d6a..3f5093222e9 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -87,7 +87,7 @@ struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke); void *paint_stroke_mode_data(struct PaintStroke *stroke); float paint_stroke_distance_get(struct PaintStroke *stroke); void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data); -bool paint_poll(struct bContext *C); +bool PAINT_brush_tool_poll(struct bContext *C); void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C)); void paint_cursor_delete_textures(void); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index e1dc8fa30b9..fed89e02e8f 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -139,13 +139,14 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + const bool is_gpencil = (brush && brush->gpencil_settings != NULL); // Object *ob = CTX_data_active_object(C); float scalar = RNA_float_get(op->ptr, "scalar"); if (brush) { /* pixel radius */ { - const int old_size = BKE_brush_size_get(scene, brush); + const int old_size = (!is_gpencil) ? BKE_brush_size_get(scene, brush) : brush->size; int size = (int)(scalar * old_size); if (abs(old_size - size) < U.pixelsize) { @@ -156,6 +157,12 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op) size -= U.pixelsize; } } + /* Grease Pencil does not use unified size. */ + if (is_gpencil) { + brush->size = max_ii(size, 1); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + return OPERATOR_FINISHED; + } BKE_brush_size_set(scene, brush, size); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 49ddf2f82d8..b093f07226e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -1462,7 +1462,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (paint_supports_smooth_stroke(br, mode)) { stroke->stroke_cursor = WM_paint_cursor_activate( - SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_smooth_cursor, stroke); + SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_smooth_cursor, stroke); } stroke->stroke_init = true; @@ -1489,7 +1489,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (br->flag & BRUSH_LINE) { stroke->stroke_cursor = WM_paint_cursor_activate( - SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_line_cursor, stroke); + SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_line_cursor, stroke); } first_dab = true; @@ -1659,7 +1659,7 @@ void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data) stroke->mode_data = mode_data; } -bool paint_poll(bContext *C) +bool PAINT_brush_tool_poll(bContext *C) { Paint *p = BKE_paint_get_active_from_context(C); Object *ob = CTX_data_active_object(C); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 964e5bdaa90..2e1dd928f96 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -780,6 +780,10 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, iter->neighbors = iter->neighbors_fixed; for (int i = 0; i < ss->pmap[index].count; i++) { + if (ss->face_sets[vert_map->indices[i]] < 0) { + /* Skip connectivity from hidden faces. */ + continue; + } const MPoly *p = &ss->mpoly[vert_map->indices[i]]; uint f_adj_v[2]; if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) { @@ -6602,7 +6606,7 @@ bool SCULPT_poll_view3d(bContext *C) bool SCULPT_poll(bContext *C) { - return SCULPT_mode_poll(C) && paint_poll(C); + return SCULPT_mode_poll(C) && PAINT_brush_tool_poll(C); } static const char *sculpt_tool_name(Sculpt *sd) diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 85616f6356d..fe0a53ae964 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -52,6 +52,7 @@ #include "RNA_enum_types.h" #include "SEQ_iterator.h" +#include "SEQ_utils.h" #include "UI_interface.h" @@ -258,7 +259,7 @@ static void sound_update_animation_flags(Scene *scene) scene->id.tag |= LIB_TAG_DOIT; SEQ_ALL_BEGIN (scene->ed, seq) { - SEQ_iterator_recursive_apply(seq, sound_update_animation_flags_fn, scene); + SEQ_recursive_apply(seq, sound_update_animation_flags_fn, scene); } SEQ_ALL_END; diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h index 74e7bc11c26..7564fa4b930 100644 --- a/source/blender/editors/space_buttons/buttons_intern.h +++ b/source/blender/editors/space_buttons/buttons_intern.h @@ -35,6 +35,7 @@ struct bContext; struct bContextDataResult; struct bNode; struct bNodeTree; +struct bNodeSocket; struct wmOperatorType; struct SpaceProperties_Runtime { @@ -66,6 +67,7 @@ typedef struct ButsTextureUser { struct bNodeTree *ntree; struct bNode *node; + struct bNodeSocket *socket; const char *category; int icon; diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index 43128ed00fa..97e3cb750c1 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -75,15 +75,16 @@ static SpaceProperties *find_space_properties(const bContext *C); /************************* Texture User **************************/ -static void buttons_texture_user_node_property_add(ListBase *users, - ID *id, - PointerRNA ptr, - PropertyRNA *prop, - bNodeTree *ntree, - bNode *node, - const char *category, - int icon, - const char *name) +static void buttons_texture_user_socket_property_add(ListBase *users, + ID *id, + PointerRNA ptr, + PropertyRNA *prop, + bNodeTree *ntree, + bNode *node, + bNodeSocket *socket, + const char *category, + int icon, + const char *name) { ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser"); @@ -92,6 +93,7 @@ static void buttons_texture_user_node_property_add(ListBase *users, user->prop = prop; user->ntree = ntree; user->node = node; + user->socket = socket; user->category = category; user->icon = icon; user->name = name; @@ -181,25 +183,29 @@ static void buttons_texture_modifier_geonodes_users_add(Object *ob, /* Recurse into the node group */ buttons_texture_modifier_geonodes_users_add(ob, nmd, (bNodeTree *)node->id, users); } - else if (node->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) { - RNA_pointer_create(&node_tree->id, &RNA_Node, node, &ptr); - prop = RNA_struct_find_property(&ptr, "texture"); - if (prop == NULL) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->flag & SOCK_UNAVAIL) { + continue; + } + if (socket->type != SOCK_TEXTURE) { continue; } + RNA_pointer_create(&node_tree->id, &RNA_NodeSocket, socket, &ptr); + prop = RNA_struct_find_property(&ptr, "default_value"); PointerRNA texptr = RNA_property_pointer_get(&ptr, prop); Tex *tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? (Tex *)texptr.data : NULL; if (tex != NULL) { - buttons_texture_user_node_property_add(users, - &ob->id, - ptr, - prop, - node_tree, - node, - N_("Geometry Nodes"), - RNA_struct_ui_icon(ptr.type), - nmd->modifier.name); + buttons_texture_user_socket_property_add(users, + &ob->id, + ptr, + prop, + node_tree, + node, + socket, + N_("Geometry Nodes"), + RNA_struct_ui_icon(ptr.type), + nmd->modifier.name); } } } diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 309b280177c..f1d0197b9ae 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -63,6 +63,7 @@ void FILE_OT_bookmark_move(struct wmOperatorType *ot); void FILE_OT_reset_recent(wmOperatorType *ot); void FILE_OT_hidedot(struct wmOperatorType *ot); void FILE_OT_execute(struct wmOperatorType *ot); +void FILE_OT_mouse_execute(struct wmOperatorType *ot); void FILE_OT_cancel(struct wmOperatorType *ot); void FILE_OT_parent(struct wmOperatorType *ot); void FILE_OT_directory_new(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 856bd5b1bc3..61f3c046550 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -536,6 +536,14 @@ void FILE_OT_select_box(wmOperatorType *ot) /** \name Select Pick Operator * \{ */ +static rcti file_select_mval_to_select_rect(const int mval[2]) +{ + rcti rect; + rect.xmin = rect.xmax = mval[0]; + rect.ymin = rect.ymax = mval[1]; + return rect; +} + static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); @@ -551,8 +559,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - rect.xmin = rect.xmax = event->mval[0]; - rect.ymin = rect.ymax = event->mval[1]; + rect = file_select_mval_to_select_rect(event->mval); if (!ED_fileselect_layout_is_inside_pt(sfile->layout, ®ion->v2d, rect.xmin, rect.ymin)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; @@ -1711,14 +1718,14 @@ bool file_draw_check_exists(SpaceFile *sfile) /** \name Execute File Window Operator * \{ */ -static int file_exec(bContext *C, wmOperator *exec_op) +/** + * Execute the active file, as set in the file select params. + */ +static bool file_execute(bContext *C, SpaceFile *sfile) { Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_active_params(sfile); - struct FileDirEntry *file = filelist_file(sfile->files, params->active_file); - char filepath[FILE_MAX]; + FileDirEntry *file = filelist_file(sfile->files, params->active_file); if (file && file->redirection_path) { /* redirection_path is an absolute path that takes precedence @@ -1753,22 +1760,7 @@ static int file_exec(bContext *C, wmOperator *exec_op) /* opening file - sends events now, so things get handled on windowqueue level */ else if (sfile->op) { wmOperator *op = sfile->op; - - /* When used as a macro, for double-click, to prevent closing when double-clicking on item. */ - if (RNA_boolean_get(exec_op->ptr, "need_active")) { - const int numfiles = filelist_files_ensure(sfile->files); - int i, active = 0; - - for (i = 0; i < numfiles; i++) { - if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) { - active = 1; - break; - } - } - if (active == 0) { - return OPERATOR_CANCELLED; - } - } + char filepath[FILE_MAX]; sfile->op = NULL; @@ -1788,50 +1780,94 @@ static int file_exec(bContext *C, wmOperator *exec_op) BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); fsmenu_write_file(ED_fsmenu_get(), filepath); - WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC); + WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC); } return OPERATOR_FINISHED; } -static int file_exec_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int file_exec(bContext *C, wmOperator *UNUSED(op)) { - ARegion *region = CTX_wm_region(C); SpaceFile *sfile = CTX_wm_space_file(C); - if (!ED_fileselect_layout_is_inside_pt( - sfile->layout, ®ion->v2d, event->mval[0], event->mval[1])) { - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + if (!file_execute(C, sfile)) { + return OPERATOR_CANCELLED; } - return file_exec(C, op); + return OPERATOR_FINISHED; } void FILE_OT_execute(struct wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Execute File Window"; ot->description = "Execute selected file"; ot->idname = "FILE_OT_execute"; /* api callbacks */ - ot->invoke = file_exec_invoke; ot->exec = file_exec; /* Important since handler is on window level. * * Avoid using #file_operator_poll since this is also used for entering directories * which is used even when the file manager doesn't have an operator. */ ot->poll = ED_operator_file_active; +} - /* properties */ - prop = RNA_def_boolean(ot->srna, - "need_active", - 0, - "Need Active", - "Only execute if there's an active selected file in the file list"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); +/** + * \returns false if the mouse doesn't hover a selectable item. + */ +static bool file_ensure_hovered_is_active(bContext *C, const wmEvent *event) +{ + rcti rect = file_select_mval_to_select_rect(event->mval); + if (file_select(C, &rect, FILE_SEL_ADD, false, false) == FILE_SELECT_NOTHING) { + return false; + } + + return true; +} + +static int file_execute_mouse_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + if (!ED_fileselect_layout_is_inside_pt( + sfile->layout, ®ion->v2d, event->mval[0], event->mval[1])) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + /* Note that this isn't needed practically, because the keymap already activates the hovered item + * on mouse-press. This execute operator is called afterwards on the double-click event then. + * However relying on this would be fragile and could break with keymap changes, so better to + * have this mouse-execute operator that makes sure once more that the hovered file is active. */ + if (!file_ensure_hovered_is_active(C, event)) { + return OPERATOR_CANCELLED; + } + + if (!file_execute(C, sfile)) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +/** + * Variation of #FILE_OT_execute that accounts for some mouse specific handling. Otherwise calls + * the same logic. + */ +void FILE_OT_mouse_execute(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Execute File"; + ot->description = + "Perform the current execute action for the file under the cursor (e.g. open the file)"; + ot->idname = "FILE_OT_mouse_execute"; + + /* api callbacks */ + ot->invoke = file_execute_mouse_invoke; + ot->poll = ED_operator_file_active; + + ot->flag = OPTYPE_INTERNAL; } /** \} */ diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index c2b3a7ac0ee..ca16563a7e2 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1601,37 +1601,46 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE); - if (!entry->preview_icon_id && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) && - (entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | - FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) { - FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__); - FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index]; + if (entry->preview_icon_id) { + return; + } - if (entry->redirection_path) { - BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); - } - else { - BLI_join_dirfile( - preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); - } + if (entry->flags & FILE_ENTRY_INVALID_PREVIEW) { + return; + } - preview->index = index; - preview->flags = entry->typeflag; - preview->in_memory_preview = intern_entry->local_data.preview_image; - preview->icon_id = 0; - // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + if (!(entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | + FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) { + return; + } - filelist_cache_preview_ensure_running(cache); + FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__); + FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index]; - FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), - __func__); - preview_taskdata->preview = preview; - BLI_task_pool_push(cache->previews_pool, - filelist_cache_preview_runf, - preview_taskdata, - true, - filelist_cache_preview_freef); + if (entry->redirection_path) { + BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); } + else { + BLI_join_dirfile( + preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); + } + + preview->index = index; + preview->flags = entry->typeflag; + preview->in_memory_preview = intern_entry->local_data.preview_image; + preview->icon_id = 0; + // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + + filelist_cache_preview_ensure_running(cache); + + FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), + __func__); + preview_taskdata->preview = preview; + BLI_task_pool_push(cache->previews_pool, + filelist_cache_preview_runf, + preview_taskdata, + true, + filelist_cache_preview_freef); } static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size) @@ -2360,17 +2369,19 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index) // printf("Re-queueing previews...\n"); - /* Note we try to preview first images around given index - i.e. assumed visible ones. */ if (cache->flags & FLC_PREVIEWS_ACTIVE) { - for (i = 0; ((index + i) < end_index) || ((index - i) >= start_index); i++) { - if ((index - i) >= start_index) { - const int idx = (cache->block_cursor + (index - start_index) - i) % cache_size; - filelist_cache_previews_push(filelist, cache->block_entries[idx], index - i); - } - if ((index + i) < end_index) { - const int idx = (cache->block_cursor + (index - start_index) + i) % cache_size; - filelist_cache_previews_push(filelist, cache->block_entries[idx], index + i); - } + /* Note we try to preview first images around given index - i.e. assumed visible ones. */ + int block_index = cache->block_cursor + (index - start_index); + int offs_max = max_ii(end_index - index, index - start_index); + for (i = 0; i <= offs_max; i++) { + int offs = i; + do { + int offs_idx = index + offs; + if (start_index <= offs_idx && offs_idx < end_index) { + int offs_block_idx = (block_index + offs) % (int)cache_size; + filelist_cache_previews_push(filelist, cache->block_entries[offs_block_idx], offs_idx); + } + } while ((offs = -offs) < 0); /* Switch between negative and positive offset. */ } } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 993b1d9b69c..12bc0a68ca6 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -467,6 +467,15 @@ static void file_listener(const wmSpaceTypeListenerParams *params) break; } break; + case NC_ID: { + switch (wmn->action) { + case NA_RENAME: + /* Force list to update sorting (with a full reset for now). */ + file_reset_filelist_showing_main_data(area, sfile); + break; + } + break; + } case NC_ASSET: { switch (wmn->action) { case NA_SELECTED: @@ -654,6 +663,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_highlight); WM_operatortype_append(FILE_OT_sort_column_ui_context); WM_operatortype_append(FILE_OT_execute); + WM_operatortype_append(FILE_OT_mouse_execute); WM_operatortype_append(FILE_OT_cancel); WM_operatortype_append(FILE_OT_parent); WM_operatortype_append(FILE_OT_previous); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 83dd5098a30..6053253790a 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1479,18 +1479,16 @@ static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr), return !(STR_ELEM(prop_id, "filepath", "directory", "filename")); } -static void image_open_draw(bContext *C, wmOperator *op) +static void image_open_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); ImageOpenData *iod = op->customdata; ImageFormatData *imf = &iod->im_format; - PointerRNA imf_ptr, ptr; + PointerRNA imf_ptr; /* main draw call */ - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA( - layout, &ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + layout, op->ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* image template */ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); @@ -2001,12 +1999,11 @@ static bool image_save_as_draw_check_prop(PointerRNA *ptr, ((STREQ(prop_id, "relative_path")) && RNA_boolean_get(ptr, "copy"))); } -static void image_save_as_draw(bContext *C, wmOperator *op) +static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); ImageSaveData *isd = op->customdata; - PointerRNA imf_ptr, ptr; + PointerRNA imf_ptr; const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview"); /* image template */ @@ -2014,9 +2011,8 @@ static void image_save_as_draw(bContext *C, wmOperator *op) uiTemplateImageSettings(layout, &imf_ptr, false); /* main draw call */ - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA( - layout, &ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + layout, op->ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* multiview template */ if (is_multiview) { @@ -2614,38 +2610,34 @@ static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e return WM_operator_props_dialog_popup(C, op, 300); } -static void image_new_draw(bContext *C, wmOperator *op) +static void image_new_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *col; uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; #if 0 Scene *scene = CTX_data_scene(C); const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; #endif - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - /* copy of WM_operator_props_dialog_popup() layout */ uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); col = uiLayoutColumn(layout, false); - uiItemR(col, &ptr, "name", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "width", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "height", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "color", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "alpha", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "generated_type", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "float", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "tiled", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "name", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "color", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "alpha", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "generated_type", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "float", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "tiled", 0, NULL, ICON_NONE); #if 0 if (is_multiview) { uiItemL(col[0], "", ICON_NONE); - uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE); + uiItemR(col[1], op->ptr, "use_stereo_3d", 0, NULL, ICON_NONE); } #endif } @@ -3991,26 +3983,22 @@ static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev return WM_operator_props_dialog_popup(C, op, 300); } -static void tile_add_draw(bContext *C, wmOperator *op) +static void tile_add_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *col; uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); col = uiLayoutColumn(layout, false); - uiItemR(col, &ptr, "number", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "count", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "label", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "fill", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "number", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "count", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "label", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "fill", 0, NULL, ICON_NONE); - if (RNA_boolean_get(&ptr, "fill")) { - draw_fill_tile(&ptr, layout); + if (RNA_boolean_get(op->ptr, "fill")) { + draw_fill_tile(op->ptr, layout); } } @@ -4128,13 +4116,9 @@ static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e return WM_operator_props_dialog_popup(C, op, 300); } -static void tile_fill_draw(bContext *C, wmOperator *op) +static void tile_fill_draw(bContext *UNUSED(C), wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - draw_fill_tile(&ptr, op->layout); + draw_fill_tile(op->ptr, op->layout); } void IMAGE_OT_tile_fill(wmOperatorType *ot) diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 0583628be43..aaf9852e212 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -56,7 +56,9 @@ #include "info_intern.h" -/********************* pack blend file libraries operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Pack Blend File Libraries Operator + * \{ */ static int pack_libraries_exec(bContext *C, wmOperator *op) { @@ -70,9 +72,11 @@ static int pack_libraries_exec(bContext *C, wmOperator *op) void FILE_OT_pack_libraries(wmOperatorType *ot) { /* identifiers */ - ot->name = "Pack Blender Libraries"; + ot->name = "Pack Linked Libraries"; ot->idname = "FILE_OT_pack_libraries"; - ot->description = "Pack all used Blender library files into the current .blend"; + ot->description = + "Store all data-blocks linked from other .blend files in the current .blend file. " + "Library references are preserved so the linked data-blocks can be unpacked again"; /* api callbacks */ ot->exec = pack_libraries_exec; @@ -90,18 +94,24 @@ static int unpack_libraries_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack Blend File Libraries Operator + * \{ */ + static int unpack_libraries_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { return WM_operator_confirm_message( - C, op, "Unpack Blender Libraries - creates directories, all new paths should work"); + C, op, "Unpack Linked Libraries - creates directories, all new paths should work"); } void FILE_OT_unpack_libraries(wmOperatorType *ot) { /* identifiers */ - ot->name = "Unpack Blender Libraries"; + ot->name = "Unpack Linked Libraries"; ot->idname = "FILE_OT_unpack_libraries"; - ot->description = "Unpack all used Blender library files from this .blend file"; + ot->description = "Restore all packed linked data-blocks to their original locations"; /* api callbacks */ ot->invoke = unpack_libraries_invoke; @@ -111,7 +121,11 @@ void FILE_OT_unpack_libraries(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* toggle auto-pack operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Auto-Pack Operator + * \{ */ static int autopack_toggle_exec(bContext *C, wmOperator *op) { @@ -131,7 +145,7 @@ static int autopack_toggle_exec(bContext *C, wmOperator *op) void FILE_OT_autopack_toggle(wmOperatorType *ot) { /* identifiers */ - ot->name = "Automatically Pack Into .blend"; + ot->name = "Automatically Pack Resources"; ot->idname = "FILE_OT_autopack_toggle"; ot->description = "Automatically pack all external files into the .blend file"; @@ -142,7 +156,11 @@ void FILE_OT_autopack_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* pack all operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pack All Operator + * \{ */ static int pack_all_exec(bContext *C, wmOperator *op) { @@ -176,9 +194,9 @@ static int pack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev void FILE_OT_pack_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Pack All Into .blend"; + ot->name = "Pack Resources"; ot->idname = "FILE_OT_pack_all"; - ot->description = "Pack all used external files into the .blend"; + ot->description = "Pack all used external files into this .blend"; /* api callbacks */ ot->exec = pack_all_exec; @@ -188,7 +206,11 @@ void FILE_OT_pack_all(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* unpack all operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack All Operator + * \{ */ static const EnumPropertyItem unpack_all_method_items[] = { {PF_USE_LOCAL, "USE_LOCAL", 0, "Use files in current directory (create when necessary)", ""}, @@ -263,7 +285,7 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( void FILE_OT_unpack_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Unpack All Into Files"; + ot->name = "Unpack Resources"; ot->idname = "FILE_OT_unpack_all"; ot->description = "Unpack all files packed into this .blend to external ones"; @@ -279,7 +301,11 @@ void FILE_OT_unpack_all(wmOperatorType *ot) ot->srna, "method", unpack_all_method_items, PF_USE_LOCAL, "Method", "How to unpack"); } -/********************* unpack single item operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack Single Item Operator + * \{ */ static const EnumPropertyItem unpack_item_method_items[] = { {PF_USE_LOCAL, "USE_LOCAL", 0, "Use file from current directory (create when necessary)", ""}, @@ -373,7 +399,11 @@ void FILE_OT_unpack_item(wmOperatorType *ot) INT_MAX); } -/********************* make paths relative operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Paths Relative Operator + * \{ */ static int make_paths_relative_exec(bContext *C, wmOperator *op) { @@ -395,7 +425,7 @@ static int make_paths_relative_exec(bContext *C, wmOperator *op) void FILE_OT_make_paths_relative(wmOperatorType *ot) { /* identifiers */ - ot->name = "Make All Paths Relative"; + ot->name = "Make Paths Relative"; ot->idname = "FILE_OT_make_paths_relative"; ot->description = "Make all paths to external files relative to current .blend"; @@ -406,7 +436,11 @@ void FILE_OT_make_paths_relative(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* make paths absolute operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Paths Absolute Operator + * \{ */ static int make_paths_absolute_exec(bContext *C, wmOperator *op) { @@ -428,7 +462,7 @@ static int make_paths_absolute_exec(bContext *C, wmOperator *op) void FILE_OT_make_paths_absolute(wmOperatorType *ot) { /* identifiers */ - ot->name = "Make All Paths Absolute"; + ot->name = "Make Paths Absolute"; ot->idname = "FILE_OT_make_paths_absolute"; ot->description = "Make all paths to external files absolute"; @@ -439,7 +473,11 @@ void FILE_OT_make_paths_absolute(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* report missing files operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Report Missing Files Operator + * \{ */ static int report_missing_files_exec(bContext *C, wmOperator *op) { @@ -465,7 +503,11 @@ void FILE_OT_report_missing_files(wmOperatorType *ot) ot->flag = 0; /* only reports so no need to undo/register */ } -/********************* find missing files operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Find Missing Files Operator + * \{ */ static int find_missing_files_exec(bContext *C, wmOperator *op) { @@ -516,7 +558,11 @@ void FILE_OT_find_missing_files(wmOperatorType *ot) FILE_SORT_DEFAULT); } -/********************* report box operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Report Box Operator + * \{ */ /* Hard to decide whether to keep this as an operator, * or turn it into a hardcoded ui control feature, @@ -621,3 +667,5 @@ void INFO_OT_reports_display_update(wmOperatorType *ot) } /* report operators */ + +/** \} */ diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index ffac5c982d6..0bdfceb36b6 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -697,7 +697,8 @@ void ED_info_draw_stats( UI_FontThemeColor(font_id, TH_TEXT_HI); BLF_enable(font_id, BLF_SHADOW); - BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f}); + const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + BLF_shadow(font_id, 5, shadow_color); BLF_shadow_offset(font_id, 1, -1); /* Translated labels for each stat row. */ diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index dfc0abee704..e56bb44b1e6 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -255,11 +255,10 @@ static void info_header_region_message_subscribe(const wmRegionMessageSubscribeP struct wmMsgBus *mbus = params->message_bus; ARegion *region = params->region; - wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { - .owner = region, - .user_data = region, - .notify = ED_region_do_msg_notify_tag_redraw, - }; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {NULL}; + msg_sub_value_region_tag_redraw.owner = region; + msg_sub_value_region_tag_redraw.user_data = region; + msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw; WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw); WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw); diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 8bf6f2698e0..7d4011e0812 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -266,7 +266,7 @@ static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float col } else if (strip->type == NLASTRIP_TYPE_META) { /* Meta Clip */ - /* TODO: should temporary metas get different colors too? */ + /* TODO: should temporary meta-strips get different colors too? */ if (strip->flag & NLASTRIP_FLAG_SELECT) { /* selected - use a bold purple color */ UI_GetThemeColor3fv(TH_NLA_META_SEL, color); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 5110c14ef4d..6b4366b2966 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -456,7 +456,9 @@ static void node_draw_frame(const bContext *C, } /* label */ - node_draw_frame_label(ntree, node, snode->runtime->aspect); + if (node->label[0] != '\0') { + node_draw_frame_label(ntree, node, snode->runtime->aspect); + } UI_block_end(C, node->block); UI_block_draw(C, node->block); @@ -3332,12 +3334,14 @@ static const float std_node_socket_colors[][4] = { {0.39, 0.78, 0.39, 1.0}, /* SOCK_SHADER */ {0.80, 0.65, 0.84, 1.0}, /* SOCK_BOOLEAN */ {0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */ - {0.25, 0.75, 0.26, 1.0}, /* SOCK_INT */ + {0.35, 0.55, 0.36, 1.0}, /* SOCK_INT */ {0.44, 0.70, 1.00, 1.0}, /* SOCK_STRING */ {0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */ - {0.89, 0.76, 0.43, 1.0}, /* SOCK_IMAGE */ + {0.39, 0.22, 0.39, 1.0}, /* SOCK_IMAGE */ {0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */ {0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */ + {0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */ + {0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */ }; /* common color callbacks for standard types */ @@ -3478,6 +3482,14 @@ static void std_node_socket_draw( uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); break; } + case SOCK_TEXTURE: { + uiTemplateID(layout, C, ptr, "default_value", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); + break; + } + case SOCK_MATERIAL: { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + break; + } default: node_socket_button_label(C, layout, ptr, node_ptr, text); break; diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index d4780534a83..50fa8b28468 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -1713,8 +1713,6 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) } } - do_tag_update |= ED_node_is_geometry(snode); - snode_notify(C, snode); if (do_tag_update) { snode_dag_update(C, snode); @@ -1755,8 +1753,6 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) } } - do_tag_update |= ED_node_is_geometry(snode); - ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 91fe8f5ec89..28c660b0632 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -852,8 +852,6 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) } ntree->is_updating = false; - do_tag_update |= ED_node_is_geometry(snode); - ntreeUpdateTree(bmain, ntree); snode_notify(C, snode); if (do_tag_update) { @@ -1291,8 +1289,6 @@ static int cut_links_exec(bContext *C, wmOperator *op) } } - do_tag_update |= ED_node_is_geometry(snode); - if (found) { ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); @@ -1399,8 +1395,6 @@ static int mute_links_exec(bContext *C, wmOperator *op) link->flag &= ~NODE_LINK_TEST; } - do_tag_update |= ED_node_is_geometry(snode); - ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); if (do_tag_update) { @@ -1882,28 +1876,63 @@ void ED_node_link_intersect_test(ScrArea *area, int test) } } -/* assumes sockets in list */ -static bNodeSocket *socket_best_match(ListBase *sockets) -{ - /* find type range */ - int maxtype = 0; +static int get_main_socket_priority(const bNodeSocket *socket) +{ + switch ((eNodeSocketDatatype)socket->type) { + case __SOCK_MESH: + case SOCK_CUSTOM: + return -1; + case SOCK_BOOLEAN: + return 0; + case SOCK_INT: + return 1; + case SOCK_FLOAT: + return 2; + case SOCK_VECTOR: + return 3; + case SOCK_RGBA: + return 4; + case SOCK_STRING: + case SOCK_SHADER: + case SOCK_OBJECT: + case SOCK_IMAGE: + case SOCK_GEOMETRY: + case SOCK_COLLECTION: + case SOCK_TEXTURE: + case SOCK_MATERIAL: + return 5; + } + return -1; +} + +/** Get the "main" socket of a socket list using a heuristic based on socket types. */ +static bNodeSocket *get_main_socket(ListBase *sockets) +{ + /* find priority range */ + int maxpriority = -1; LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - maxtype = max_ii(sock->type, maxtype); + if (sock->flag & SOCK_UNAVAIL) { + continue; + } + maxpriority = max_ii(get_main_socket_priority(sock), maxpriority); } - /* try all types, starting from 'highest' (i.e. colors, vectors, values) */ - for (int type = maxtype; type >= 0; type--) { + /* try all priorities, starting from 'highest' */ + for (int priority = maxpriority; priority >= 0; priority--) { LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - if (!nodeSocketIsHidden(sock) && type == sock->type) { + if (!nodeSocketIsHidden(sock) && priority == get_main_socket_priority(sock)) { return sock; } } } - /* no visible sockets, unhide first of highest type */ - for (int type = maxtype; type >= 0; type--) { + /* no visible sockets, unhide first of highest priority */ + for (int priority = maxpriority; priority >= 0; priority--) { LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - if (type == sock->type) { + if (sock->flag & SOCK_UNAVAIL) { + continue; + } + if (priority == get_main_socket_priority(sock)) { sock->flag &= ~SOCK_HIDDEN; return sock; } @@ -2248,8 +2277,8 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) } if (link) { - bNodeSocket *best_input = socket_best_match(&select->inputs); - bNodeSocket *best_output = socket_best_match(&select->outputs); + bNodeSocket *best_input = get_main_socket(&select->inputs); + bNodeSocket *best_output = get_main_socket(&select->outputs); if (best_input && best_output) { bNode *node = link->tonode; diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 456c2079f27..c532dd8accd 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -902,7 +902,7 @@ static void id_override_library_reset_fn(bContext *C, } static void id_override_library_resync_fn(bContext *C, - ReportList *UNUSED(reports), + ReportList *reports, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), @@ -931,7 +931,7 @@ static void id_override_library_resync_fn(bContext *C, } BKE_lib_override_library_resync( - bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true); + bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true, reports); WM_event_add_notifier(C, NC_WINDOW, NULL); } diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 5d774049e3e..728be1ccaaf 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -155,6 +155,8 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params) case NC_OBJECT: switch (wmn->data) { case ND_TRANSFORM: + ED_region_tag_redraw_no_rebuild(region); + break; case ND_BONE_ACTIVE: case ND_BONE_SELECT: case ND_DRAW: diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index a61260acfb2..cd75bc98b15 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -754,18 +754,16 @@ static int sequencer_add_movie_strip_invoke(bContext *C, return OPERATOR_RUNNING_MODAL; } -static void sequencer_add_draw(bContext *C, wmOperator *op) +static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); SequencerAddData *sad = op->customdata; ImageFormatData *imf = &sad->im_format; - PointerRNA imf_ptr, ptr; + PointerRNA imf_ptr; /* Main draw call. */ - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA( - layout, &ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); + layout, op->ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* Image template. */ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 5d616969a4f..1a1ba5a0754 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -290,7 +290,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) snap_frame = RNA_int_get(op->ptr, "frame"); - /* Check metas. */ + /* Check meta-strips. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK) && SEQ_transform_sequence_can_be_translated(seq)) { @@ -1485,26 +1485,22 @@ static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *ev return sequencer_split_exec(C, op); } -static void sequencer_split_ui(bContext *C, wmOperator *op) +static void sequencer_split_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - PointerRNA ptr; - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - uiLayout *row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(layout, &ptr, "frame", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "side", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "frame", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "side", 0, NULL, ICON_NONE); uiItemS(layout); - uiItemR(layout, &ptr, "use_cursor_position", 0, NULL, ICON_NONE); - if (RNA_boolean_get(&ptr, "use_cursor_position")) { - uiItemR(layout, &ptr, "channel", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_cursor_position", 0, NULL, ICON_NONE); + if (RNA_boolean_get(op->ptr, "use_cursor_position")) { + uiItemR(layout, op->ptr, "channel", 0, NULL, ICON_NONE); } } @@ -1612,7 +1608,7 @@ static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) BLI_movelisttolist(ed->seqbasep, &nseqbase); for (; seq; seq = seq->next) { - SEQ_iterator_recursive_apply(seq, apply_unique_name_fn, scene); + SEQ_recursive_apply(seq, apply_unique_name_fn, scene); } WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1871,13 +1867,13 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op)) Sequence *active_seq = SEQ_select_active_get(scene); if (active_seq && active_seq->type == SEQ_TYPE_META && active_seq->flag & SELECT) { - /* Enter metastrip. */ + /* Enter meta-strip. */ SEQ_meta_stack_alloc(ed, active_seq); SEQ_seqbase_active_set(ed, &active_seq->seqbase); SEQ_select_active_set(scene, NULL); } else { - /* Exit metastrip if possible. */ + /* Exit meta-strip if possible. */ if (BLI_listbase_is_empty(&ed->metastack)) { return OPERATOR_CANCELLED; } @@ -1899,7 +1895,7 @@ void SEQUENCER_OT_meta_toggle(wmOperatorType *ot) /* Identifiers. */ ot->name = "Toggle Meta Strip"; ot->idname = "SEQUENCER_OT_meta_toggle"; - ot->description = "Toggle a metastrip (to edit enclosed strips)"; + ot->description = "Toggle a meta-strip (to edit enclosed strips)"; /* Api callbacks. */ ot->exec = sequencer_meta_toggle_exec; @@ -1967,7 +1963,7 @@ void SEQUENCER_OT_meta_make(wmOperatorType *ot) /* Identifiers. */ ot->name = "Make Meta Strip"; ot->idname = "SEQUENCER_OT_meta_make"; - ot->description = "Group selected strips into a metastrip"; + ot->description = "Group selected strips into a meta-strip"; /* Api callbacks. */ ot->exec = sequencer_meta_make_exec; @@ -2030,7 +2026,7 @@ void SEQUENCER_OT_meta_separate(wmOperatorType *ot) /* Identifiers. */ ot->name = "UnMeta Strip"; ot->idname = "SEQUENCER_OT_meta_separate"; - ot->description = "Put the contents of a metastrip back in the sequencer"; + ot->description = "Put the contents of a meta-strip back in the sequencer"; /* Api callbacks. */ ot->exec = sequencer_meta_separate_exec; @@ -2278,43 +2274,45 @@ void SEQUENCER_OT_swap(wmOperatorType *ot) static int sequencer_rendersize_exec(bContext *C, wmOperator *UNUSED(op)) { - int retval = OPERATOR_CANCELLED; Scene *scene = CTX_data_scene(C); Sequence *active_seq = SEQ_select_active_get(scene); StripElem *se = NULL; - if (active_seq == NULL) { + if (active_seq == NULL || active_seq->strip == NULL) { return OPERATOR_CANCELLED; } - if (active_seq->strip) { - switch (active_seq->type) { - case SEQ_TYPE_IMAGE: - se = SEQ_render_give_stripelem(active_seq, scene->r.cfra); - break; - case SEQ_TYPE_MOVIE: - se = active_seq->strip->stripdata; - break; - case SEQ_TYPE_SCENE: - case SEQ_TYPE_META: - case SEQ_TYPE_SOUND_RAM: - case SEQ_TYPE_SOUND_HD: - default: - break; - } + switch (active_seq->type) { + case SEQ_TYPE_IMAGE: + se = SEQ_render_give_stripelem(active_seq, scene->r.cfra); + break; + case SEQ_TYPE_MOVIE: + se = active_seq->strip->stripdata; + break; + default: + return OPERATOR_CANCELLED; } - if (se) { - /* Prevent setting the render size if sequence values aren't initialized. */ - if ((se->orig_width > 0) && (se->orig_height > 0)) { - scene->r.xsch = se->orig_width; - scene->r.ysch = se->orig_height; - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); - retval = OPERATOR_FINISHED; - } + if (se == NULL) { + return OPERATOR_CANCELLED; + } + + /* Prevent setting the render size if sequence values aren't initialized. */ + if (se->orig_width <= 0 || se->orig_height <= 0) { + return OPERATOR_CANCELLED; } - return retval; + scene->r.xsch = se->orig_width; + scene->r.ysch = se->orig_height; + + active_seq->strip->transform->scale_x = active_seq->strip->transform->scale_y = 1.0f; + active_seq->strip->transform->xofs = active_seq->strip->transform->yofs = 0.0f; + + SEQ_relations_invalidate_cache_preprocessed(scene, active_seq); + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + return OPERATOR_FINISHED; } void SEQUENCER_OT_rendersize(wmOperatorType *ot) @@ -2467,7 +2465,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) for (iseq = iseq_first; iseq; iseq = iseq->next) { /* Make sure, that pasted strips have unique names. */ - SEQ_iterator_recursive_apply(iseq, apply_unique_name_fn, scene); + SEQ_recursive_apply(iseq, apply_unique_name_fn, scene); /* Translate after name has been changed, otherwise this will affect animdata of original * strip. */ SEQ_transform_translate_sequence(scene, iseq, ofs); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index a9f8a70d61e..e08af8f0977 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -26,7 +26,6 @@ #include <string.h> #include "BLI_blenlib.h" -#include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -1622,64 +1621,78 @@ static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq) return changed; } -static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int channel) +/* Query all effect strips that are directly or indirectly connected to seq_reference. */ +static void query_strip_effect_chain(Sequence *seq_reference, + ListBase *seqbase, + SeqCollection *collection) { - bool changed = false; - const bool is_audio = ((actseq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(actseq)); - int startdisp = actseq->startdisp; - int enddisp = actseq->enddisp; - int machine = actseq->machine; - SeqIterator iter; - - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - seq->tmp = NULL; + if (!SEQ_collection_append_strip(seq_reference, collection)) { + return; /* Strip is already in set, so all effects connected to it are as well. */ } - actseq->tmp = POINTER_FROM_INT(true); - - Sequence *seq = NULL; - for (SEQ_iterator_begin(ed, &iter, true); iter.valid; SEQ_iterator_next(&iter)) { - seq = iter.seq; + /* Find all strips that seq_reference is connected to. */ + if (seq_reference->type & SEQ_TYPE_EFFECT) { + if (seq_reference->seq1) { + query_strip_effect_chain(seq_reference->seq1, seqbase, collection); + } + if (seq_reference->seq2) { + query_strip_effect_chain(seq_reference->seq2, seqbase, collection); + } + if (seq_reference->seq3) { + query_strip_effect_chain(seq_reference->seq3, seqbase, collection); + } + } - /* Ignore all seqs already selected. */ - /* Ignore all seqs not sharing some time with active one. */ - /* Ignore all seqs of incompatible types (audio vs video). */ - if (!SEQ_CHANNEL_CHECK(seq, channel) || (seq->flag & SELECT) || (seq->startdisp >= enddisp) || - (seq->enddisp < startdisp) || (!is_audio && SEQ_IS_SOUND(seq)) || - (is_audio && !((seq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(seq)))) { - continue; + /* Find all strips connected to seq_reference. */ + LISTBASE_FOREACH (Sequence *, seq_test, seqbase) { + if (seq_test->seq1 == seq_reference || seq_test->seq2 == seq_reference || + seq_test->seq3 == seq_reference) { + query_strip_effect_chain(seq_test, seqbase, collection); } + } +} - /* If the seq is an effect one, we need extra checking. */ - if (SEQ_IS_EFFECT(seq) && ((seq->seq1 && seq->seq1->tmp) || (seq->seq2 && seq->seq2->tmp) || - (seq->seq3 && seq->seq3->tmp))) { - if (startdisp > seq->startdisp) { - startdisp = seq->startdisp; - } - if (enddisp < seq->enddisp) { - enddisp = seq->enddisp; - } - if (machine < seq->machine) { - machine = seq->machine; - } +/* Query strips that are in lower channel and intersect in time with seq_reference. */ +static void query_lower_channel_strips(Sequence *seq_reference, + ListBase *seqbase, + SeqCollection *collection) +{ + LISTBASE_FOREACH (Sequence *, seq_test, seqbase) { + if (seq_test->machine > seq_reference->machine) { + continue; /* Not lower channel. */ + } + if (seq_test->enddisp <= seq_reference->startdisp || + seq_test->startdisp >= seq_reference->enddisp) { + continue; /* Not intersecting in time. */ + } + SEQ_collection_append_strip(seq_test, collection); + } +} - seq->tmp = POINTER_FROM_INT(true); +/* Select all strips within time range and with lower channel of initial selection. Then select + * effect chains of these strips. */ +static bool select_grouped_effect_link(Editing *ed, + Sequence *UNUSED(actseq), + const int UNUSED(channel)) +{ + ListBase *seqbase = SEQ_active_seqbase_get(ed); - seq->flag |= SELECT; - changed = true; + /* Get collection of strips. */ + SeqCollection *collection = SEQ_query_selected_strips(seqbase); + const int selected_strip_count = BLI_gset_len(collection->set); + SEQ_collection_expand(seqbase, collection, query_lower_channel_strips); + SEQ_collection_expand(seqbase, collection, query_strip_effect_chain); - /* Unfortunately, we must restart checks from the beginning. */ - SEQ_iterator_end(&iter); - SEQ_iterator_begin(ed, &iter, true); - } + /* Check if other strips will be affected. */ + const bool changed = BLI_gset_len(collection->set) > selected_strip_count; - /* Video strips below active one, or any strip for audio (order doesn't matter here). */ - else if (seq->machine < machine || is_audio) { - seq->flag |= SELECT; - changed = true; - } + /* Actual logic. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + seq->flag |= SELECT; } - SEQ_iterator_end(&iter); + + SEQ_collection_free(collection); return changed; } diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index acdf30d0ea7..1f0b5d5d13e 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -256,7 +256,7 @@ static std::unique_ptr<DataSource> get_data_source(const bContext *C) return {}; } Object *object_orig = (Object *)used_id; - if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) { + if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) { return {}; } Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 018431225e5..02ffa1259fc 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -57,44 +57,45 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( { std::lock_guard lock{mutex_}; - bke::ReadAttributePtr attribute_ptr = component_->attribute_try_get_for_read(column_id.name); - if (!attribute_ptr) { + bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name); + if (!attribute) { return {}; } - const bke::ReadAttribute *attribute = scope_.add(std::move(attribute_ptr), __func__); - if (attribute->domain() != domain_) { + const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__); + if (attribute.domain != domain_) { return {}; } - int domain_size = attribute->size(); - switch (attribute->custom_data_type()) { + int domain_size = varray->size(); + const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type()); + switch (type) { case CD_PROP_FLOAT: return column_values_from_function( - column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) { + column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { float value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_float = value; }); case CD_PROP_INT32: return column_values_from_function( - column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) { + column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { int value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_int = value; }); case CD_PROP_BOOL: return column_values_from_function( - column_id.name, domain_size, [attribute](int index, CellValue &r_cell_value) { + column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { bool value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_bool = value; }); case CD_PROP_FLOAT2: { return column_values_from_function( column_id.name, domain_size, - [attribute](int index, CellValue &r_cell_value) { + [varray](int index, CellValue &r_cell_value) { float2 value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_float2 = value; }, default_float2_column_width); @@ -103,9 +104,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( return column_values_from_function( column_id.name, domain_size, - [attribute](int index, CellValue &r_cell_value) { + [varray](int index, CellValue &r_cell_value) { float3 value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_float3 = value; }, default_float3_column_width); @@ -114,9 +115,9 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( return column_values_from_function( column_id.name, domain_size, - [attribute](int index, CellValue &r_cell_value) { + [varray](int index, CellValue &r_cell_value) { Color4f value; - attribute->get(index, &value); + varray->get(index, &value); r_cell_value.value_color = value; }, default_color_column_width); @@ -260,7 +261,7 @@ void InstancesDataSource::foreach_default_column_ids( SpreadsheetColumnID column_id; column_id.name = (char *)"Name"; fn(column_id); - for (const char *name : {"Position", "Rotation", "Scale"}) { + for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { column_id.name = (char *)name; fn(column_id); } @@ -275,25 +276,31 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( const int size = this->tot_rows(); if (STREQ(column_id.name, "Name")) { - Span<InstancedData> instance_data = component_->instanced_data(); + Span<int> reference_handles = component_->instance_reference_handles(); + Span<InstanceReference> references = component_->references(); std::unique_ptr<ColumnValues> values = column_values_from_function( - "Name", size, [instance_data](int index, CellValue &r_cell_value) { - const InstancedData &data = instance_data[index]; - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - if (data.data.object != nullptr) { - r_cell_value.value_object = ObjectCellValue{data.data.object}; + "Name", size, [reference_handles, references](int index, CellValue &r_cell_value) { + const InstanceReference &reference = references[reference_handles[index]]; + switch (reference.type()) { + case InstanceReference::Type::Object: { + Object &object = reference.object(); + r_cell_value.value_object = ObjectCellValue{&object}; + break; } - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - if (data.data.collection != nullptr) { - r_cell_value.value_collection = CollectionCellValue{data.data.collection}; + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); + r_cell_value.value_collection = CollectionCellValue{&collection}; + break; + } + case InstanceReference::Type::None: { + break; } } }); values->default_width = 8.0f; return values; } - Span<float4x4> transforms = component_->transforms(); + Span<float4x4> transforms = component_->instance_transforms(); if (STREQ(column_id.name, "Position")) { return column_values_from_function( column_id.name, @@ -321,6 +328,15 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( }, default_float3_column_width); } + Span<int> ids = component_->instance_ids(); + if (STREQ(column_id.name, "ID")) { + /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ + return column_values_from_function( + column_id.name, + size, + [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, + 5.5f); + } return {}; } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index e6916c34a88..b5274c2357e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1595,7 +1595,6 @@ static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(area)) } const char *view3d_context_dir[] = { - "active_base", "active_object", NULL, }; @@ -1608,20 +1607,6 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (CTX_data_dir(member)) { CTX_data_dir_set(result, view3d_context_dir); } - else if (CTX_data_equals(member, "active_base")) { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - if (view_layer->basact) { - Object *ob = view_layer->basact->object; - /* if hidden but in edit mode, we still display, can happen with animation */ - if ((view_layer->basact->flag & BASE_VISIBLE_DEPSGRAPH) != 0 || - (ob->mode != OB_MODE_OBJECT)) { - CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, view_layer->basact); - } - } - - return 1; - } else if (CTX_data_equals(member, "active_object")) { /* In most cases the active object is the `view_layer->basact->object`. * For the 3D view however it can be NULL when hidden. diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index f3a279ee12b..1ef37ba4f9b 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1288,6 +1288,11 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y name_array[name_array_len++] = IFACE_(" (Local)"); } + /* Indicate that clipping region is enabled. */ + if (rv3d->rflag & RV3D_CLIPPING) { + name_array[name_array_len++] = IFACE_(" (Clipped)"); + } + if (name_array_len > 1) { BLI_string_join_array(tmpstr, sizeof(tmpstr), name_array, name_array_len); name = tmpstr; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 5f3d71cc190..967ad966320 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -2507,7 +2507,7 @@ static void view_dolly_to_vector_3d(ARegion *region, madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac)); } -static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const short zoom_invert) +static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert) { float zfac = 1.0; @@ -5065,7 +5065,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = false, + .edit_mode_type = SNAP_GEOM_FINAL, .use_occlusion_test = true, }, mval_fl, diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index e202276831c..0d568363b00 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -338,7 +338,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_CAGE, }, mval_fl, NULL, @@ -352,7 +352,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, depsgraph, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_CAGE, }, ray_start, ray_normal, @@ -446,7 +446,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) gpl = view3d_ruler_layer_get(gpd); if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false); + gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false, false); copy_v4_v4(gpl->color, U.gpencil_new_layer_col); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER; @@ -1124,12 +1124,13 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup) const wmGizmoType *gzt_snap; gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true); gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL); + RNA_enum_set(gizmo->ptr, "snap_elements_force", (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | /* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */ SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT)); - + ED_gizmotypes_snap_3d_flag_set(gizmo, ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE); WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}); wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_ruler_add", true); diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 7347e528edc..00e30f8afae 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -323,7 +323,7 @@ static bool idp_poject_surface_normal(SnapObjectContext *snap_context, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_EDIT, }, mval_fl, NULL, @@ -941,7 +941,7 @@ static void view3d_interactive_add_calc_plane(bContext *C, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_EDIT, }, mval_fl, NULL, @@ -2061,7 +2061,7 @@ static void cursor_plane_draw(bContext *C, int x, int y, void *customdata) GPU_matrix_projection_set(rv3d->winmat); GPU_matrix_set(rv3d->viewmat); - const float scale_mod = U.gizmo_size * 2 * U.dpi_fac; + const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize; float final_scale = (scale_mod * pixel_size); diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index cbd65e3175d..ab4cf0c2135 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -407,7 +407,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, /* Avoid having to convert the edit-mesh to a regular mesh. */ - .use_object_edit_cage = true, + .edit_mode_type = SNAP_GEOM_EDIT, }, ray_start, ray_normal, diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 2fbcbe22349..ba8e5c1e2c6 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -74,7 +74,7 @@ static void drawTransformApply(const struct bContext *C, ARegion *region, void *arg); -static void initSnapSpatial(TransInfo *t, float r_snap[3]); +static void initSnapSpatial(TransInfo *t, float r_snap[2]); bool transdata_check_local_islands(TransInfo *t, short around) { @@ -782,7 +782,7 @@ static bool transform_event_modal_constraint(TransInfo *t, short modal_type) } else { short orient_index = 1; - if (t->orient_curr == 0 || ELEM(constraint_curr, -1, constraint_new)) { + if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) { /* Successive presses on existing axis, cycle orientation modes. */ orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); } @@ -1412,25 +1412,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) ToolSettings *ts = CTX_data_tool_settings(C); PropertyRNA *prop; - if (!(t->con.mode & CON_APPLY) && (t->flag & T_MODAL) && - ELEM(t->mode, TFM_TRANSLATION, TFM_RESIZE)) { - /* When redoing these modes the first time, it's more convenient to save - * in the Global orientation. */ - if (t->mode == TFM_TRANSLATION) { - mul_m3_v3(t->spacemtx, t->values_final); - } - else { - float tmat[3][3], sizemat[3][3]; - size_to_mat3(sizemat, t->values_final); - mul_m3_m3m3(tmat, t->spacemtx, sizemat); - mat3_to_size(t->values_final, tmat); - } - - BLI_assert(t->orient_curr == 0); - unit_m3(t->spacemtx); - t->orient[0].type = V3D_ORIENT_GLOBAL; - } - /* Save back mode in case we're in the generic operator */ if ((prop = RNA_struct_find_property(op->ptr, "mode"))) { RNA_property_enum_set(op->ptr, prop, t->mode); @@ -1638,7 +1619,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]) /** * \note caller needs to free 't' on a 0 return - * \warning \a event might be NULL (when tweaking from redo panel) + * \warning \a event might be NULL (when tweaking from redo panel) * \see #saveTransform which writes these values back. */ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode) diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index f2d5800abb7..a10a53b2983 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -88,12 +88,11 @@ typedef enum { CTX_TEXTURE_SPACE = (1 << 9), CTX_NO_PET = (1 << 10), - CTX_NO_MIRROR = (1 << 11), - CTX_AUTOCONFIRM = (1 << 12), + CTX_AUTOCONFIRM = (1 << 11), /** When transforming object's, adjust the object data so it stays in the same place. */ - CTX_OBMODE_XFORM_OBDATA = (1 << 13), + CTX_OBMODE_XFORM_OBDATA = (1 << 12), /** Transform object parents without moving their children. */ - CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14), + CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13), } eTContext; /** #TransInfo.flag */ @@ -105,51 +104,50 @@ typedef enum { /** restrictions flags */ T_NO_CONSTRAINT = 1 << 2, T_NULL_ONE = 1 << 3, - T_NO_ZERO = 1 << 4, - T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE | T_NO_ZERO, + T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE, - T_PROP_EDIT = 1 << 5, - T_PROP_CONNECTED = 1 << 6, - T_PROP_PROJECTED = 1 << 7, + T_PROP_EDIT = 1 << 4, + T_PROP_CONNECTED = 1 << 5, + T_PROP_PROJECTED = 1 << 6, T_PROP_EDIT_ALL = T_PROP_EDIT | T_PROP_CONNECTED | T_PROP_PROJECTED, - T_V3D_ALIGN = 1 << 8, + T_V3D_ALIGN = 1 << 7, /** For 2D views such as UV or f-curve. */ - T_2D_EDIT = 1 << 9, - T_CLIP_UV = 1 << 10, + T_2D_EDIT = 1 << 8, + T_CLIP_UV = 1 << 9, /** Auto-IK is on. */ - T_AUTOIK = 1 << 11, + T_AUTOIK = 1 << 10, /** Don't use mirror even if the data-block option is set. */ - T_NO_MIRROR = 1 << 12, + T_NO_MIRROR = 1 << 11, /** To indicate that the value set in the `value` parameter is the final * value of the transformation, modified only by the constrain. */ - T_INPUT_IS_VALUES_FINAL = 1 << 13, + T_INPUT_IS_VALUES_FINAL = 1 << 12, /** To specify if we save back settings at the end. */ - T_MODAL = 1 << 14, + T_MODAL = 1 << 13, /** No re-topology (projection). */ - T_NO_PROJECT = 1 << 15, + T_NO_PROJECT = 1 << 14, - T_RELEASE_CONFIRM = 1 << 16, + T_RELEASE_CONFIRM = 1 << 15, /** Alternative transformation. used to add offset to tracking markers. */ - T_ALT_TRANSFORM = 1 << 17, + T_ALT_TRANSFORM = 1 << 16, /** #TransInfo.center has been set, don't change it. */ - T_OVERRIDE_CENTER = 1 << 18, + T_OVERRIDE_CENTER = 1 << 17, - T_MODAL_CURSOR_SET = 1 << 19, + T_MODAL_CURSOR_SET = 1 << 18, - T_CLNOR_REBUILD = 1 << 20, + T_CLNOR_REBUILD = 1 << 19, /** Merges unselected into selected after transforming (runs after transforming). */ - T_AUTOMERGE = 1 << 21, + T_AUTOMERGE = 1 << 20, /** Runs auto-merge & splits. */ - T_AUTOSPLIT = 1 << 22, + T_AUTOSPLIT = 1 << 21, } eTFlag; /** #TransInfo.modifiers */ @@ -598,11 +596,18 @@ typedef struct TransInfo { * mouse button then.) */ bool is_launch_event_tweak; + bool is_orient_set; + struct { short type; float matrix[3][3]; } orient[3]; - short orient_curr; + + enum { + O_DEFAULT = 0, + O_SCENE, + O_SET, + } orient_curr; /** backup from view3d, to restore on end. */ short gizmo_flag; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 2037981e655..615467932a7 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -953,9 +953,8 @@ void startConstraint(TransInfo *t) void stopConstraint(TransInfo *t) { - if (t->orient_curr != 0) { - t->orient_curr = 0; - transform_orientations_current_set(t, t->orient_curr); + if (t->orient_curr != O_DEFAULT) { + transform_orientations_current_set(t, O_DEFAULT); } t->con.mode &= ~(CON_APPLY | CON_SELECT); @@ -971,8 +970,8 @@ void stopConstraint(TransInfo *t) void initSelectConstraint(TransInfo *t) { - if (t->orient_curr == 0) { - transform_orientations_current_set(t, 1); + if (t->orient_curr == O_DEFAULT) { + transform_orientations_current_set(t, O_SCENE); } setUserConstraint(t, CON_APPLY | CON_SELECT, "%s"); diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 7239bed1eeb..b4175faacf4 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1147,8 +1147,7 @@ static void init_TransDataContainers(TransInfo *t, for (int i = 0; i < objects_len; i++) { TransDataContainer *tc = &t->data_container[i]; - if (((t->flag & T_NO_MIRROR) == 0) && ((t->options & CTX_NO_MIRROR) == 0) && - (objects[i]->type == OB_MESH)) { + if (!(t->flag & T_NO_MIRROR) && (objects[i]->type == OB_MESH)) { tc->use_mirror_axis_x = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_X) != 0; tc->use_mirror_axis_y = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Y) != 0; tc->use_mirror_axis_z = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Z) != 0; @@ -1497,91 +1496,89 @@ void createTransData(bContext *C, TransInfo *t) /** \name Transform Data Recalc/Flush * \{ */ -void clipMirrorModifier(TransInfo *t) +void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc) { - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - Object *ob = tc->obedit; - ModifierData *md = ob->modifiers.first; - float tolerance[3] = {0.0f, 0.0f, 0.0f}; - int axis = 0; - - for (; md; md = md->next) { - if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { - MirrorModifierData *mmd = (MirrorModifierData *)md; - - if (mmd->flag & MOD_MIR_CLIPPING) { - axis = 0; - if (mmd->flag & MOD_MIR_AXIS_X) { - axis |= 1; - tolerance[0] = mmd->tolerance; - } - if (mmd->flag & MOD_MIR_AXIS_Y) { - axis |= 2; - tolerance[1] = mmd->tolerance; - } - if (mmd->flag & MOD_MIR_AXIS_Z) { - axis |= 4; - tolerance[2] = mmd->tolerance; - } - if (axis) { - float mtx[4][4], imtx[4][4]; - int i; + Object *ob = tc->obedit; + ModifierData *md = ob->modifiers.first; + float tolerance[3] = {0.0f, 0.0f, 0.0f}; + int axis = 0; + + for (; md; md = md->next) { + if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + + if (mmd->flag & MOD_MIR_CLIPPING) { + axis = 0; + if (mmd->flag & MOD_MIR_AXIS_X) { + axis |= 1; + tolerance[0] = mmd->tolerance; + } + if (mmd->flag & MOD_MIR_AXIS_Y) { + axis |= 2; + tolerance[1] = mmd->tolerance; + } + if (mmd->flag & MOD_MIR_AXIS_Z) { + axis |= 4; + tolerance[2] = mmd->tolerance; + } + if (axis) { + float mtx[4][4], imtx[4][4]; + int i; - if (mmd->mirror_ob) { - float obinv[4][4]; + if (mmd->mirror_ob) { + float obinv[4][4]; - invert_m4_m4(obinv, mmd->mirror_ob->obmat); - mul_m4_m4m4(mtx, obinv, ob->obmat); - invert_m4_m4(imtx, mtx); - } + invert_m4_m4(obinv, mmd->mirror_ob->obmat); + mul_m4_m4m4(mtx, obinv, ob->obmat); + invert_m4_m4(imtx, mtx); + } - TransData *td = tc->data; - for (i = 0; i < tc->data_len; i++, td++) { - int clip; - float loc[3], iloc[3]; + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + int clip; + float loc[3], iloc[3]; - if (td->loc == NULL) { - break; - } + if (td->loc == NULL) { + break; + } - if (td->flag & TD_SKIP) { - continue; - } + if (td->flag & TD_SKIP) { + continue; + } - copy_v3_v3(loc, td->loc); - copy_v3_v3(iloc, td->iloc); + copy_v3_v3(loc, td->loc); + copy_v3_v3(iloc, td->iloc); - if (mmd->mirror_ob) { - mul_m4_v3(mtx, loc); - mul_m4_v3(mtx, iloc); - } + if (mmd->mirror_ob) { + mul_m4_v3(mtx, loc); + mul_m4_v3(mtx, iloc); + } - clip = 0; - if (axis & 1) { - if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) { - loc[0] = 0.0f; - clip = 1; - } + clip = 0; + if (axis & 1) { + if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) { + loc[0] = 0.0f; + clip = 1; } + } - if (axis & 2) { - if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) { - loc[1] = 0.0f; - clip = 1; - } + if (axis & 2) { + if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) { + loc[1] = 0.0f; + clip = 1; } - if (axis & 4) { - if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) { - loc[2] = 0.0f; - clip = 1; - } + } + if (axis & 4) { + if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) { + loc[2] = 0.0f; + clip = 1; } - if (clip) { - if (mmd->mirror_ob) { - mul_m4_v3(imtx, loc); - } - copy_v3_v3(td->loc, loc); + } + if (clip) { + if (mmd->mirror_ob) { + mul_m4_v3(imtx, loc); } + copy_v3_v3(td->loc, loc); } } } @@ -1669,38 +1666,6 @@ void animrecord_check_state(TransInfo *t, struct Object *ob) } } -static void recalcData_cursor_image(TransInfo *t) -{ - TransDataContainer *tc = t->data_container; - TransData *td = tc->data; - float aspect_inv[2]; - - aspect_inv[0] = 1.0f / t->aspect[0]; - aspect_inv[1] = 1.0f / t->aspect[1]; - - td->loc[0] = td->loc[0] * aspect_inv[0]; - td->loc[1] = td->loc[1] * aspect_inv[1]; - - DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); -} - -static void recalcData_cursor(TransInfo *t) -{ - DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); -} - -static void recalcData_obedit(TransInfo *t) -{ - if (t->state != TRANS_CANCEL) { - applyProject(t); - } - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (tc->data_len) { - DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ - } - } -} - /* called for updating while transform acts, once per redraw */ void recalcData(TransInfo *t) { @@ -1767,7 +1732,7 @@ void recalcData(TransInfo *t) recalcData_tracking(t); break; case TC_MBALL_VERTS: - recalcData_obedit(t); + recalcData_mball(t); break; case TC_LATTICE_VERTS: recalcData_lattice(t); diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 36a51d57f64..11550ec8803 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -45,7 +45,7 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); /* transform_convert_mesh.c */ -void mesh_customdatacorrect_init(TransInfo *t); +void transform_convert_mesh_customdatacorrect_init(TransInfo *t); /* transform_convert_sequencer.c */ int transform_convert_sequencer_get_snap_bound(TransInfo *t); @@ -62,7 +62,7 @@ void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic); struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt); char transform_convert_frame_side_dir_get(TransInfo *t, float cframe); bool FrameOnMouseSide(char side, float frame, float cframe); -void clipMirrorModifier(TransInfo *t); +void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc); void animrecord_check_state(TransInfo *t, struct Object *ob); /* transform_convert_action.c */ @@ -84,6 +84,8 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t); /* transform_convert_cursor.c */ void createTransCursor_image(TransInfo *t); void createTransCursor_view3d(TransInfo *t); +void recalcData_cursor_image(TransInfo *t); +void recalcData_cursor(TransInfo *t); /* transform_convert_curve.c */ void createTransCurveVerts(TransInfo *t); @@ -109,6 +111,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t); /* transform_convert_mball.c */ void createTransMBallVerts(TransInfo *t); +void recalcData_mball(TransInfo *t); /* transform_convert_mesh.c */ struct TransIslandData { diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c index 67d85f9610b..1f3eff31205 100644 --- a/source/blender/editors/transform/transform_convert_cursor.c +++ b/source/blender/editors/transform/transform_convert_cursor.c @@ -134,3 +134,29 @@ void createTransCursor_view3d(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Cursor + * \{ */ + +void recalcData_cursor_image(TransInfo *t) +{ + TransDataContainer *tc = t->data_container; + TransData *td = tc->data; + float aspect_inv[2]; + + aspect_inv[0] = 1.0f / t->aspect[0]; + aspect_inv[1] = 1.0f / t->aspect[1]; + + td->loc[0] = td->loc[0] * aspect_inv[0]; + td->loc[1] = td->loc[1] * aspect_inv[1]; + + DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); +} + +void recalcData_cursor(TransInfo *t) +{ + DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c index 3ff83e00f43..f294bbbf0aa 100644 --- a/source/blender/editors/transform/transform_convert_curve.c +++ b/source/blender/editors/transform/transform_convert_curve.c @@ -441,7 +441,6 @@ void createTransCurveVerts(TransInfo *t) void recalcData_curve(TransInfo *t) { if (t->state != TRANS_CANCEL) { - clipMirrorModifier(t); applyProject(t); } @@ -460,7 +459,10 @@ void recalcData_curve(TransInfo *t) } } else { - /* Normal updating */ + /* Apply clipping after so we never project past the clip plane T25423. */ + transform_convert_clip_mirror_modifier_apply(tc); + + /* Normal updating. */ BKE_curve_dimension_update(cu); } } diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c index 6f5c0318054..e47cf4edc4a 100644 --- a/source/blender/editors/transform/transform_convert_mball.c +++ b/source/blender/editors/transform/transform_convert_mball.c @@ -30,6 +30,8 @@ #include "BKE_context.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" /* -------------------------------------------------------------------- */ @@ -128,3 +130,21 @@ void createTransMBallVerts(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Meta Ball + * \{ */ + +void recalcData_mball(TransInfo *t) +{ + if (t->state != TRANS_CANCEL) { + applyProject(t); + } + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->data_len) { + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + } + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 5c05e35feb4..8d942b1094e 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -48,7 +48,575 @@ #include "transform_convert.h" +/* -------------------------------------------------------------------- */ +/** \name Container TransCustomData Creation + * \{ */ + +struct TransCustomDataMergeGroup { + /** map {BMVert: TransCustomDataLayerVert} */ + struct LinkNode **cd_loop_groups; +}; + +struct TransCustomDataLayer { + BMesh *bm; + struct MemArena *arena; + + struct GHash *origfaces; + struct BMesh *bm_origfaces; + + /* Special handle for multi-resolution. */ + int cd_loop_mdisp_offset; + + /* Optionally merge custom-data groups (this keeps UVs connected for example). */ + struct { + /** map {BMVert: TransDataBasic} */ + struct GHash *origverts; + struct TransCustomDataMergeGroup *data; + int data_len; + /** Array size of 'layer_math_map_len' + * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */ + int *customdatalayer_map; + /** Number of math BMLoop layers. */ + int customdatalayer_map_len; + } merge_group; + + bool use_merge_group; +}; + +static void tc_mesh_customdatacorrect_free_fn(struct TransInfo *UNUSED(t), + struct TransDataContainer *UNUSED(tc), + struct TransCustomData *custom_data) +{ + struct TransCustomDataLayer *tcld = custom_data->data; + bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + + if (tcld->bm_origfaces) { + BM_mesh_free(tcld->bm_origfaces); + } + if (tcld->origfaces) { + BLI_ghash_free(tcld->origfaces, NULL, NULL); + } + if (tcld->merge_group.origverts) { + BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); + } + if (tcld->arena) { + BLI_memarena_free(tcld->arena); + } + if (tcld->merge_group.customdatalayer_map) { + MEM_freeN(tcld->merge_group.customdatalayer_map); + } + + MEM_freeN(tcld); + custom_data->data = NULL; +} + #define USE_FACE_SUBSTITUTE +#ifdef USE_FACE_SUBSTITUTE +# define FACE_SUBSTITUTE_INDEX INT_MIN + +/** + * Search for a neighboring face with area and preferably without selected vertex. + * Used to replace area-less faces in custom-data correction. + */ +static BMFace *tc_mesh_customdatacorrect_find_best_face_substitute(BMFace *f) +{ + BMFace *best_face = NULL; + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BMLoop *l_radial_next = l->radial_next; + BMFace *f_test = l_radial_next->f; + if (f_test == f) { + continue; + } + if (is_zero_v3(f_test->no)) { + continue; + } + + /* Check the loops edge isn't selected. */ + if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) && + !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) { + /* Prefer edges with unselected vertices. + * Useful for extrude. */ + best_face = f_test; + break; + } + if (best_face == NULL) { + best_face = f_test; + } + } + return best_face; +} + +static void tc_mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld, + BMFace *f, + BMFace *f_copy) +{ + BLI_assert(is_zero_v3(f->no)); + BMesh *bm = tcld->bm; + /* It is impossible to calculate the loops weights of a face without area. + * Find a substitute. */ + BMFace *f_substitute = tc_mesh_customdatacorrect_find_best_face_substitute(f); + if (f_substitute) { + /* Copy the custom-data from the substitute face. */ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false); + } while ((l_iter = l_iter->next) != l_first); + + /* Use the substitute face as the reference during the transformation. */ + BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true); + + /* Hack: reference substitute face in `f_copy->no`. + * `tcld->origfaces` is already used to restore the initial value. */ + BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX); + *((BMFace **)&f_copy->no[0]) = f_substitute_copy; + } +} + +static BMFace *tc_mesh_customdatacorrect_face_substitute_get(BMFace *f_copy) +{ + BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX); + return *((BMFace **)&f_copy->no[0]); +} + +#endif /* USE_FACE_SUBSTITUTE */ + +static void tc_mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld, + struct TransDataBasic *td, + const int index) +{ + BMesh *bm = tcld->bm; + BMVert *v = td->extra; + BMIter liter; + int j, l_num; + float *loop_weights; + + // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { + BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); + l_num = liter.count; + loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL; + for (j = 0; j < l_num; j++) { + BMLoop *l = BM_iter_step(&liter); + BMLoop *l_prev, *l_next; + + /* Generic custom-data correction. Copy face data. */ + void **val_p; + if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) { + BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true); + *val_p = f_copy; +#ifdef USE_FACE_SUBSTITUTE + if (is_zero_v3(l->f->no)) { + tc_mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy); + } +#endif + } + + if (tcld->use_merge_group) { + if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && + (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) { + loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); + } + else { + loop_weights[j] = 0.0f; + } + } + } + + if (tcld->use_merge_group) { + /* Store cd_loop_groups. */ + struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index]; + if (l_num != 0) { + merge_data->cd_loop_groups = BLI_memarena_alloc( + tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *)); + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + const int layer_nr = tcld->merge_group.customdatalayer_map[j]; + merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( + bm, v, layer_nr, loop_weights, tcld->arena); + } + } + else { + merge_data->cd_loop_groups = NULL; + } + + BLI_ghash_insert(tcld->merge_group.origverts, v, td); + } +} + +static void tc_mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc), + struct TransCustomDataLayer *tcld) +{ + BMesh *bm = tcld->bm; + + struct GHash *origfaces = BLI_ghash_ptr_new(__func__); + struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); + + /* We need to have matching loop custom-data. */ + BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL); + + tcld->origfaces = origfaces; + tcld->bm_origfaces = bm_origfaces; + + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); +} + +static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, + struct TransCustomDataLayer *tcld) +{ + BMesh *bm = tcld->bm; + BLI_assert(CustomData_has_math(&bm->ldata)); + + /* TODO: We don't need `layer_math_map` when there are no loops linked + * to one of the sliding vertices. */ + + /* Over allocate, only 'math' layers are indexed. */ + int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__); + int layer_math_map_len = 0; + for (int i = 0; i < bm->ldata.totlayer; i++) { + if (CustomData_layer_has_math(&bm->ldata, i)) { + customdatalayer_map[layer_math_map_len++] = i; + } + } + BLI_assert(layer_math_map_len != 0); + + tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len; + tcld->merge_group.customdatalayer_map = customdatalayer_map; + tcld->merge_group.customdatalayer_map_len = layer_math_map_len; + tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len); + tcld->merge_group.data = BLI_memarena_alloc( + tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); +} + +static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataContainer *tc, + const bool use_merge_group) +{ + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BMesh *bm = em->bm; + + if (bm->shapenr > 1) { + /* Don't do this at all for non-basis shape keys, too easy to + * accidentally break uv maps or vertex colors then */ + /* create copies of faces for custom-data projection. */ + return NULL; + } + if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + /* There is no custom-data to correct. */ + return NULL; + } + + struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__); + tcld->bm = bm; + tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + + /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */ + tcld->cd_loop_mdisp_offset = -1; + tcld->use_merge_group = use_merge_group; + + tc_mesh_customdatacorrect_init_container_generic(tc, tcld); + + if (tcld->use_merge_group) { + tc_mesh_customdatacorrect_init_container_merge_group(tc, tcld); + } + + { + /* Setup Verts. */ + int i = 0; + + TransData *tob = tc->data; + for (int j = tc->data_len; j--; tob++, i++) { + tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i); + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (int j = tc->data_mirror_len; j--; td_mirror++, i++) { + tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i); + } + } + + return tcld; +} + +static void tc_mesh_customdata_create(TransDataContainer *tc, const bool use_merge_group) +{ + struct TransCustomDataLayer *customdatacorrect; + customdatacorrect = tc_mesh_customdatacorrect_create(tc, use_merge_group); + + if (!customdatacorrect) { + return; + } + + BLI_assert(tc->custom.type.data == NULL); + tc->custom.type.data = customdatacorrect; + tc->custom.type.free_cb = tc_mesh_customdatacorrect_free_fn; +} + +void transform_convert_mesh_customdatacorrect_init(TransInfo *t) +{ + bool use_merge_group = false; + if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { + if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) { + /* No custom-data correction. */ + return; + } + use_merge_group = true; + } + else if (ELEM(t->mode, + TFM_TRANSLATION, + TFM_ROTATION, + TFM_RESIZE, + TFM_TOSPHERE, + TFM_SHEAR, + TFM_BEND, + TFM_SHRINKFATTEN, + TFM_TRACKBALL, + TFM_PUSHPULL, + TFM_ALIGN)) { + { + if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { + /* No custom-data correction. */ + return; + } + use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0; + } + } + else { + return; + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->custom.type.data != NULL) { + tc_mesh_customdatacorrect_free_fn(t, tc, &tc->custom.type); + } + + tc_mesh_customdata_create(tc, use_merge_group); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData Layer Correction Apply + * \{ */ + +/** + * If we're sliding the vert, return its original location, if not, the current location is good. + */ +static const float *tc_mesh_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v) +{ + TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v); + return td ? td->iloc : v->co; +} + +static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld, + struct TransDataBasic *td, + struct TransCustomDataMergeGroup *merge_data, + bool do_loop_mdisps) +{ + BMesh *bm = tcld->bm; + BMVert *v = td->extra; + const float *co_orig_3d = td->iloc; + + BMIter liter; + int j, l_num; + float *loop_weights; + const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON); + const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len; + const float *v_proj_axis = v->no; + /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ + float v_proj[3][3]; + + if (do_loop_weight) { + project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis); + } + + // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) + BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); + l_num = liter.count; + loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL; + for (j = 0; j < l_num; j++) { + BMFace *f_copy; /* the copy of 'f' */ + BMLoop *l = BM_iter_step(&liter); + + f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); + +#ifdef USE_FACE_SUBSTITUTE + /* In some faces it is not possible to calculate interpolation, + * so we use a substitute. */ + if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) { + f_copy = tc_mesh_customdatacorrect_face_substitute_get(f_copy); + } +#endif + + /* only loop data, no vertex data since that contains shape keys, + * and we do not want to mess up other shape keys */ + BM_loop_interp_from_face(bm, l, f_copy, false, false); + + /* weight the loop */ + if (do_loop_weight) { + const float eps = 1.0e-8f; + const BMLoop *l_prev = l->prev; + const BMLoop *l_next = l->next; + const float *co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v); + const float *co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v); + bool co_prev_ok; + bool co_next_ok; + + /* In the unlikely case that we're next to a zero length edge - + * walk around the to the next. + * + * Since we only need to check if the vertex is in this corner, + * its not important _which_ loop - as long as its not overlapping + * 'sv->co_orig_3d', see: T45096. */ + project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && + ((l_prev = l_prev->prev) != l->next))) { + co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v); + project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + } + project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); + while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && + ((l_next = l_next->next) != l->prev))) { + co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v); + project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); + } + + if (co_prev_ok && co_next_ok) { + const float dist = dist_signed_squared_to_corner_v3v3v3( + v->co, UNPACK3(v_proj), v_proj_axis); + + loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); + if (UNLIKELY(!isfinite(loop_weights[j]))) { + loop_weights[j] = 0.0f; + } + } + else { + loop_weights[j] = 0.0f; + } + } + } + + if (tcld->use_merge_group) { + struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups; + if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) { + if (do_loop_weight) { + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + BM_vert_loop_groups_data_layer_merge_weights( + bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights); + } + } + else { + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + BM_vert_loop_groups_data_layer_merge( + bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]); + } + } + } + } + + /* Special handling for multires + * + * Interpolate from every other loop (not ideal) + * However values will only be taken from loops which overlap other mdisps. + */ + const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); + if (update_loop_mdisps) { + float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); + BMLoop *l; + + BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { + BM_face_calc_center_median(l->f, faces_center[j]); + } + + BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { + BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); + float f_copy_center[3]; + BMIter liter_other; + BMLoop *l_other; + int j_other; + + BM_face_calc_center_median(f_copy, f_copy_center); + + BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) { + BM_face_interp_multires_ex(bm, + l_other->f, + f_copy, + faces_center[j_other], + f_copy_center, + tcld->cd_loop_mdisp_offset); + } + } + } +} + +static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final) +{ + if (!tc->custom.type.data) { + return; + } + struct TransCustomDataLayer *tcld = tc->custom.type.data; + const bool use_merge_group = tcld->use_merge_group; + + struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; + TransData *tob = tc->data; + for (int i = tc->data_len; i--; tob++) { + tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final); + + if (use_merge_group) { + merge_data++; + } + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (int i = tc->data_mirror_len; i--; td_mirror++) { + tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final); + + if (use_merge_group) { + merge_data++; + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData Layer Correction Restore + * \{ */ + +static void tc_mesh_customdatacorrect_restore(struct TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + struct TransCustomDataLayer *tcld = tc->custom.type.data; + if (!tcld) { + continue; + } + + BMesh *bm = tcld->bm; + BMesh *bm_copy = tcld->bm_origfaces; + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, tcld->origfaces) { + BMFace *f = BLI_ghashIterator_getKey(&gh_iter); + BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter); + BLI_assert(f->len == f_copy->len); + + BMLoop *l_iter, *l_first, *l_copy; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + l_copy = BM_FACE_FIRST_LOOP(f_copy); + do { + /* TODO: Restore only the elements that transform. */ + BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter); + l_copy = l_copy->next; + } while ((l_iter = l_iter->next) != l_first); + } + } +} + +/** \} */ /* -------------------------------------------------------------------- */ /** \name Island Creation @@ -741,10 +1309,10 @@ void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_cr /** \name Edit Mesh Verts Transform Creation * \{ */ -static void transdata_center_get(const struct TransIslandData *island_data, - const int island_index, - const float iloc[3], - float r_center[3]) +static void tc_mesh_transdata_center_copy(const struct TransIslandData *island_data, + const int island_index, + const float iloc[3], + float r_center[3]) { if (island_data->center && island_index != -1) { copy_v3_v3(r_center, island_data->center[island_index]); @@ -783,7 +1351,7 @@ static void VertsToTransData(TransInfo *t, no = eve->no; } - transdata_center_get(island_data, island_index, td->iloc, td->center); + tc_mesh_transdata_center_copy(island_data, island_index, td->iloc, td->center); if ((island_index != -1) && island_data->axismtx) { copy_m3_m3(td->axismtx, island_data->axismtx[island_index]); @@ -973,7 +1541,8 @@ void createTransEditVerts(TransInfo *t) copy_v3_v3(td_mirror->iloc, eve->co); td_mirror->flag = mirror_data.vert_map[a].flag; td_mirror->loc_src = v_src->co; - transdata_center_get(&island_data, island_index, td_mirror->iloc, td_mirror->center); + tc_mesh_transdata_center_copy( + &island_data, island_index, td_mirror->iloc, td_mirror->center); td_mirror++; } @@ -1045,590 +1614,39 @@ void createTransEditVerts(TransInfo *t) /** \} */ /* -------------------------------------------------------------------- */ -/** \name CustomData Layer Correction +/** \name Recalc Mesh Data * \{ */ -struct TransCustomDataMergeGroup { - /** map {BMVert: TransCustomDataLayerVert} */ - struct LinkNode **cd_loop_groups; -}; - -struct TransCustomDataLayer { - BMesh *bm; - struct MemArena *arena; - - struct GHash *origfaces; - struct BMesh *bm_origfaces; - - /* Special handle for multi-resolution. */ - int cd_loop_mdisp_offset; - - /* Optionally merge custom-data groups (this keeps UVs connected for example). */ - struct { - /** map {BMVert: TransDataBasic} */ - struct GHash *origverts; - struct TransCustomDataMergeGroup *data; - int data_len; - /** Array size of 'layer_math_map_len' - * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */ - int *customdatalayer_map; - /** Number of math BMLoop layers. */ - int customdatalayer_map_len; - } merge_group; - - bool use_merge_group; -}; - -static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t), - struct TransDataContainer *UNUSED(tc), - struct TransCustomData *custom_data) -{ - struct TransCustomDataLayer *tcld = custom_data->data; - bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - - if (tcld->bm_origfaces) { - BM_mesh_free(tcld->bm_origfaces); - } - if (tcld->origfaces) { - BLI_ghash_free(tcld->origfaces, NULL, NULL); - } - if (tcld->merge_group.origverts) { - BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); - } - if (tcld->arena) { - BLI_memarena_free(tcld->arena); - } - if (tcld->merge_group.customdatalayer_map) { - MEM_freeN(tcld->merge_group.customdatalayer_map); - } - - MEM_freeN(tcld); - custom_data->data = NULL; -} - -#ifdef USE_FACE_SUBSTITUTE - -# define FACE_SUBSTITUTE_INDEX INT_MIN - -/** - * Search for a neighboring face with area and preferably without selected vertex. - * Used to replace area-less faces in custom-data correction. - */ -static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f) -{ - BMFace *best_face = NULL; - BMLoop *l; - BMIter liter; - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMLoop *l_radial_next = l->radial_next; - BMFace *f_test = l_radial_next->f; - if (f_test == f) { - continue; - } - if (is_zero_v3(f_test->no)) { - continue; - } - - /* Check the loops edge isn't selected. */ - if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) && - !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) { - /* Prefer edges with unselected vertices. - * Useful for extrude. */ - best_face = f_test; - break; - } - if (best_face == NULL) { - best_face = f_test; - } - } - return best_face; -} - -static void mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld, - BMFace *f, - BMFace *f_copy) -{ - BLI_assert(is_zero_v3(f->no)); - BMesh *bm = tcld->bm; - /* It is impossible to calculate the loops weights of a face without area. - * Find a substitute. */ - BMFace *f_substitute = mesh_customdatacorrect_find_best_face_substitute(f); - if (f_substitute) { - /* Copy the custom-data from the substitute face. */ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false); - } while ((l_iter = l_iter->next) != l_first); - - /* Use the substitute face as the reference during the transformation. */ - BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true); - - /* Hack: reference substitute face in `f_copy->no`. - * `tcld->origfaces` is already used to restore the initial value. */ - BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX); - *((BMFace **)&f_copy->no[0]) = f_substitute_copy; - } -} - -static BMFace *mesh_customdatacorrect_face_substitute_get(BMFace *f_copy) -{ - BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX); - return *((BMFace **)&f_copy->no[0]); -} - -#endif /* USE_FACE_SUBSTITUTE */ - -static void mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld, - struct TransDataBasic *td, - const int index) -{ - BMesh *bm = tcld->bm; - BMVert *v = td->extra; - BMIter liter; - int j, l_num; - float *loop_weights; - - // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { - BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); - l_num = liter.count; - loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL; - for (j = 0; j < l_num; j++) { - BMLoop *l = BM_iter_step(&liter); - BMLoop *l_prev, *l_next; - - /* Generic custom-data correction. Copy face data. */ - void **val_p; - if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) { - BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true); - *val_p = f_copy; -#ifdef USE_FACE_SUBSTITUTE - if (is_zero_v3(l->f->no)) { - mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy); - } -#endif - } - - if (tcld->use_merge_group) { - if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && - (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) { - loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); - } - else { - loop_weights[j] = 0.0f; - } - } - } - - if (tcld->use_merge_group) { - /* Store cd_loop_groups. */ - struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index]; - if (l_num != 0) { - merge_data->cd_loop_groups = BLI_memarena_alloc( - tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *)); - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - const int layer_nr = tcld->merge_group.customdatalayer_map[j]; - merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( - bm, v, layer_nr, loop_weights, tcld->arena); - } - } - else { - merge_data->cd_loop_groups = NULL; - } - - BLI_ghash_insert(tcld->merge_group.origverts, v, td); - } -} - -static void mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc), - struct TransCustomDataLayer *tcld) -{ - BMesh *bm = tcld->bm; - - struct GHash *origfaces = BLI_ghash_ptr_new(__func__); - struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); - - /* We need to have matching loop custom-data. */ - BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL); - - tcld->origfaces = origfaces; - tcld->bm_origfaces = bm_origfaces; - - bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); -} - -static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, - struct TransCustomDataLayer *tcld) -{ - BMesh *bm = tcld->bm; - BLI_assert(CustomData_has_math(&bm->ldata)); - - /* TODO: We don't need `layer_math_map` when there are no loops linked - * to one of the sliding vertices. */ - - /* Over allocate, only 'math' layers are indexed. */ - int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__); - int layer_math_map_len = 0; - for (int i = 0; i < bm->ldata.totlayer; i++) { - if (CustomData_layer_has_math(&bm->ldata, i)) { - customdatalayer_map[layer_math_map_len++] = i; - } - } - BLI_assert(layer_math_map_len != 0); - - tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len; - tcld->merge_group.customdatalayer_map = customdatalayer_map; - tcld->merge_group.customdatalayer_map_len = layer_math_map_len; - tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len); - tcld->merge_group.data = BLI_memarena_alloc( - tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); -} - -static void mesh_customdatacorrect_init_container(TransDataContainer *tc, - const bool use_merge_group) -{ - if (tc->custom.type.data) { - /* The custom-data correction has been initiated before. - * Free since some modes have different settings. */ - mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type); - } - - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; - - if (bm->shapenr > 1) { - /* Don't do this at all for non-basis shape keys, too easy to - * accidentally break uv maps or vertex colors then */ - /* create copies of faces for custom-data projection. */ - return; - } - if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - /* There is no custom-data to correct. */ - return; - } - - struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__); - tcld->bm = bm; - tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - - /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */ - tcld->cd_loop_mdisp_offset = -1; - tcld->use_merge_group = use_merge_group; - - mesh_customdatacorrect_init_container_generic(tc, tcld); - - if (tcld->use_merge_group) { - mesh_customdatacorrect_init_container_merge_group(tc, tcld); - } - - { - /* Setup Verts. */ - int i = 0; - - TransData *tob = tc->data; - for (int j = tc->data_len; j--; tob++, i++) { - mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i); - } - - TransDataMirror *td_mirror = tc->data_mirror; - for (int j = tc->data_mirror_len; j--; td_mirror++, i++) { - mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i); - } - } - - tc->custom.type.data = tcld; - tc->custom.type.free_cb = mesh_customdatacorrect_free_cb; -} - -void mesh_customdatacorrect_init(TransInfo *t) +static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) { - bool use_merge_group = false; - if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { - if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) { - /* No custom-data correction. */ - return; - } - use_merge_group = true; - } - else if (ELEM(t->mode, - TFM_TRANSLATION, - TFM_ROTATION, - TFM_RESIZE, - TFM_TOSPHERE, - TFM_SHEAR, - TFM_BEND, - TFM_SHRINKFATTEN, - TFM_TRACKBALL, - TFM_PUSHPULL, - TFM_ALIGN)) { - { - if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { - /* No custom-data correction. */ - return; - } - use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0; - } - } - else { - return; - } - - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - mesh_customdatacorrect_init_container(tc, use_merge_group); - } -} - -/** - * If we're sliding the vert, return its original location, if not, the current location is good. - */ -static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v) -{ - TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v); - return td ? td->iloc : v->co; -} - -static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld, - struct TransDataBasic *td, - struct TransCustomDataMergeGroup *merge_data, - bool do_loop_mdisps) -{ - BMesh *bm = tcld->bm; - BMVert *v = td->extra; - const float *co_orig_3d = td->iloc; - - BMIter liter; - int j, l_num; - float *loop_weights; - const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON); - const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len; - const float *v_proj_axis = v->no; - /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ - float v_proj[3][3]; - - if (do_loop_weight) { - project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis); - } - - // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) - BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); - l_num = liter.count; - loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL; - for (j = 0; j < l_num; j++) { - BMFace *f_copy; /* the copy of 'f' */ - BMLoop *l = BM_iter_step(&liter); - - f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); - -#ifdef USE_FACE_SUBSTITUTE - /* In some faces it is not possible to calculate interpolation, - * so we use a substitute. */ - if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) { - f_copy = mesh_customdatacorrect_face_substitute_get(f_copy); - } -#endif - - /* only loop data, no vertex data since that contains shape keys, - * and we do not want to mess up other shape keys */ - BM_loop_interp_from_face(bm, l, f_copy, false, false); - - /* weight the loop */ - if (do_loop_weight) { - const float eps = 1.0e-8f; - const BMLoop *l_prev = l->prev; - const BMLoop *l_next = l->next; - const float *co_prev = trans_vert_orig_co_get(tcld, l_prev->v); - const float *co_next = trans_vert_orig_co_get(tcld, l_next->v); - bool co_prev_ok; - bool co_next_ok; - - /* In the unlikely case that we're next to a zero length edge - - * walk around the to the next. - * - * Since we only need to check if the vertex is in this corner, - * its not important _which_ loop - as long as its not overlapping - * 'sv->co_orig_3d', see: T45096. */ - project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); - while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && - ((l_prev = l_prev->prev) != l->next))) { - co_prev = trans_vert_orig_co_get(tcld, l_prev->v); - project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); - } - project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); - while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && - ((l_next = l_next->next) != l->prev))) { - co_next = trans_vert_orig_co_get(tcld, l_next->v); - project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); - } - - if (co_prev_ok && co_next_ok) { - const float dist = dist_signed_squared_to_corner_v3v3v3( - v->co, UNPACK3(v_proj), v_proj_axis); - - loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); - if (UNLIKELY(!isfinite(loop_weights[j]))) { - loop_weights[j] = 0.0f; + if (tc->use_mirror_axis_any) { + int i; + TransData *td; + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) { + if (td->flag & TD_MIRROR_EDGE_X) { + td->loc[0] = 0.0f; } - } - else { - loop_weights[j] = 0.0f; - } - } - } - - if (tcld->use_merge_group) { - struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups; - if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) { - if (do_loop_weight) { - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - BM_vert_loop_groups_data_layer_merge_weights( - bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights); + if (td->flag & TD_MIRROR_EDGE_Y) { + td->loc[1] = 0.0f; } - } - else { - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - BM_vert_loop_groups_data_layer_merge( - bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]); + if (td->flag & TD_MIRROR_EDGE_Z) { + td->loc[2] = 0.0f; } } } - } - - /* Special handling for multires - * - * Interpolate from every other loop (not ideal) - * However values will only be taken from loops which overlap other mdisps. - */ - const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); - if (update_loop_mdisps) { - float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); - BMLoop *l; - - BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { - BM_face_calc_center_median(l->f, faces_center[j]); - } - - BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { - BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); - float f_copy_center[3]; - BMIter liter_other; - BMLoop *l_other; - int j_other; - - BM_face_calc_center_median(f_copy, f_copy_center); - - BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) { - BM_face_interp_multires_ex(bm, - l_other->f, - f_copy, - faces_center[j_other], - f_copy_center, - tcld->cd_loop_mdisp_offset); - } - } - } -} - -static void mesh_customdatacorrect_apply(TransInfo *t, bool is_final) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (!tc->custom.type.data) { - continue; - } - struct TransCustomDataLayer *tcld = tc->custom.type.data; - const bool use_merge_group = tcld->use_merge_group; - - struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; - TransData *tob = tc->data; - for (int i = tc->data_len; i--; tob++) { - mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final); - - if (use_merge_group) { - merge_data++; - } - } TransDataMirror *td_mirror = tc->data_mirror; - for (int i = tc->data_mirror_len; i--; td_mirror++) { - mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final); - - if (use_merge_group) { - merge_data++; + for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) { + copy_v3_v3(td_mirror->loc, td_mirror->loc_src); + if (td_mirror->flag & TD_MIRROR_X) { + td_mirror->loc[0] *= -1; } - } - } -} - -static void mesh_customdatacorrect_restore(struct TransInfo *t) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - struct TransCustomDataLayer *tcld = tc->custom.type.data; - if (!tcld) { - continue; - } - - BMesh *bm = tcld->bm; - BMesh *bm_copy = tcld->bm_origfaces; - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, tcld->origfaces) { - BMFace *f = BLI_ghashIterator_getKey(&gh_iter); - BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter); - BLI_assert(f->len == f_copy->len); - - BMLoop *l_iter, *l_first, *l_copy; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - l_copy = BM_FACE_FIRST_LOOP(f_copy); - do { - /* TODO: Restore only the elements that transform. */ - BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter); - l_copy = l_copy->next; - } while ((l_iter = l_iter->next) != l_first); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Recalc Mesh Data - * \{ */ - -static void mesh_apply_to_mirror(TransInfo *t) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (tc->use_mirror_axis_any) { - int i; - TransData *td; - for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { - if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) { - if (td->flag & TD_MIRROR_EDGE_X) { - td->loc[0] = 0.0f; - } - if (td->flag & TD_MIRROR_EDGE_Y) { - td->loc[1] = 0.0f; - } - if (td->flag & TD_MIRROR_EDGE_Z) { - td->loc[2] = 0.0f; - } - } + if (td_mirror->flag & TD_MIRROR_Y) { + td_mirror->loc[1] *= -1; } - - TransDataMirror *td_mirror = tc->data_mirror; - for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) { - copy_v3_v3(td_mirror->loc, td_mirror->loc_src); - if (td_mirror->flag & TD_MIRROR_X) { - td_mirror->loc[0] *= -1; - } - if (td_mirror->flag & TD_MIRROR_Y) { - td_mirror->loc[1] *= -1; - } - if (td_mirror->flag & TD_MIRROR_Z) { - td_mirror->loc[2] *= -1; - } + if (td_mirror->flag & TD_MIRROR_Z) { + td_mirror->loc[2] *= -1; } } } @@ -1637,20 +1655,24 @@ static void mesh_apply_to_mirror(TransInfo *t) void recalcData_mesh(TransInfo *t) { bool is_canceling = t->state == TRANS_CANCEL; - /* mirror modifier clipping? */ + /* Apply corrections. */ if (!is_canceling) { - /* apply clipping after so we never project past the clip plane T25423. */ applyProject(t); - clipMirrorModifier(t); - if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { - mesh_apply_to_mirror(t); - } + bool do_mirror = !(t->flag & T_NO_MIRROR); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* Apply clipping after so we never project past the clip plane T25423. */ + transform_convert_clip_mirror_modifier_apply(tc); + + if (do_mirror) { + tc_mesh_transdata_mirror_apply(tc); + } - mesh_customdatacorrect_apply(t, false); + tc_mesh_customdatacorrect_apply(tc, false); + } } else { - mesh_customdatacorrect_restore(t); + tc_mesh_customdatacorrect_restore(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -1674,7 +1696,9 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) if (!is_canceling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { /* NOTE(joeedh): Handle multi-res re-projection, * done on transform completion since it's really slow. */ - mesh_customdatacorrect_apply(t, true); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc_mesh_customdatacorrect_apply(tc, true); + } } if (use_automerge) { diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c index 5dbb1947773..68305c45280 100644 --- a/source/blender/editors/transform/transform_convert_mesh_skin.c +++ b/source/blender/editors/transform/transform_convert_mesh_skin.c @@ -47,9 +47,9 @@ /** \name Edit Mesh #CD_MVERT_SKIN Transform Creation * \{ */ -static float *mesh_skin_transdata_center(const struct TransIslandData *island_data, - const int island_index, - BMVert *eve) +static float *tc_mesh_skin_transdata_center(const struct TransIslandData *island_data, + const int island_index, + BMVert *eve) { if (island_data->center && island_index != -1) { return island_data->center[island_index]; @@ -57,11 +57,11 @@ static float *mesh_skin_transdata_center(const struct TransIslandData *island_da return eve->co; } -static void mesh_skin_transdata_create(TransDataBasic *td, - BMEditMesh *em, - BMVert *eve, - const struct TransIslandData *island_data, - const int island_index) +static void tc_mesh_skin_transdata_create(TransDataBasic *td, + BMEditMesh *em, + BMVert *eve, + const struct TransIslandData *island_data, + const int island_index) { BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); MVertSkin *vs = CustomData_bmesh_get(&em->bm->vdata, eve->head.data, CD_MVERT_SKIN); @@ -78,7 +78,7 @@ static void mesh_skin_transdata_create(TransDataBasic *td, td->flag |= TD_SELECTED; } - copy_v3_v3(td->center, mesh_skin_transdata_center(island_data, island_index, eve)); + copy_v3_v3(td->center, tc_mesh_skin_transdata_center(island_data, island_index, eve)); td->extra = eve; } @@ -209,7 +209,7 @@ void createTransMeshSkin(TransInfo *t) } if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) { - mesh_skin_transdata_create( + tc_mesh_skin_transdata_create( (TransDataBasic *)td_mirror, em, eve, &island_data, island_index); int elem_index = mirror_data.vert_map[a].index; @@ -221,7 +221,7 @@ void createTransMeshSkin(TransInfo *t) td_mirror++; } else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index); + tc_mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index); if (t->around == V3D_AROUND_LOCAL_ORIGINS) { createSpaceNormal(td->axismtx, eve->no); @@ -275,7 +275,7 @@ void createTransMeshSkin(TransInfo *t) /** \name Recalc Mesh Data * \{ */ -static void mesh_skin_apply_to_mirror(TransInfo *t) +static void tc_mesh_skin_apply_to_mirror(TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->use_mirror_axis_any) { @@ -292,8 +292,8 @@ void recalcData_mesh_skin(TransInfo *t) bool is_canceling = t->state == TRANS_CANCEL; /* mirror modifier clipping? */ if (!is_canceling) { - if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { - mesh_skin_apply_to_mirror(t); + if (!(t->flag & T_NO_MIRROR)) { + tc_mesh_skin_apply_to_mirror(t); } } diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 30418471d6d..c0a66b8846e 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -707,7 +707,7 @@ static void flushTransSeq(TransInfo *t) /* originally TFM_TIME_EXTEND, transform changes */ if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) { - /* Special annoying case here, need to calc metas with TFM_TIME_EXTEND only */ + /* Special annoying case here, need to calc meta-strips with TFM_TIME_EXTEND only */ /* calc all meta's then effects T27953. */ for (seq = seqbasep->first; seq; seq = seq->next) { diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index c0c4b22da98..71c91221fbb 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -414,8 +414,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve int orient_type_set = -1; int orient_type_matrix_set = -1; - bool use_orient_axis = false; - if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) { TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT]; orient_type_scene = orient_slot->type; @@ -435,7 +433,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis"))) { t->orient_axis = RNA_property_enum_get(op->ptr, prop); - use_orient_axis = true; } if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) { @@ -457,28 +454,23 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if (orient_type_set != -1) { orient_type_default = orient_type_set; + t->is_orient_set = true; } else if (orient_type_matrix_set != -1) { orient_type_default = orient_type_set = orient_type_matrix_set; + t->is_orient_set = true; } else if (t->con.mode & CON_APPLY) { orient_type_default = orient_type_set = orient_type_scene; } else { + orient_type_default = orient_type_scene; if (orient_type_scene == V3D_ORIENT_GLOBAL) { orient_type_set = V3D_ORIENT_LOCAL; } else { orient_type_set = V3D_ORIENT_GLOBAL; } - - if ((t->flag & T_MODAL) && (use_orient_axis || transform_mode_is_changeable(t->mode)) && - (t->mode != TFM_ALIGN)) { - orient_type_default = V3D_ORIENT_VIEW; - } - else { - orient_type_default = orient_type_scene; - } } BLI_assert(!ELEM(-1, orient_type_default, orient_type_set)); @@ -487,9 +479,9 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve orient_type_set = V3D_ORIENT_CUSTOM_MATRIX; } - orient_types[0] = (short)orient_type_default; - orient_types[1] = (short)orient_type_scene; - orient_types[2] = (short)orient_type_set; + orient_types[O_DEFAULT] = (short)orient_type_default; + orient_types[O_SCENE] = (short)orient_type_scene; + orient_types[O_SET] = (short)orient_type_set; for (int i = 0; i < 3; i++) { /* For efficiency, avoid calculating the same orientation twice. */ @@ -506,9 +498,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } } - /* Set orient_curr to -1 in order to force the update in - * `transform_orientations_current_set`. */ - t->orient_curr = -1; transform_orientations_current_set(t, (t->con.mode & CON_APPLY) ? 2 : 0); } @@ -538,7 +527,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } else { /* Avoid mirroring for unsupported contexts. */ - t->options |= CTX_NO_MIRROR; + t->flag |= T_NO_MIRROR; } /* setting PET flag only if property exist in operator. Otherwise, assume it's not supported */ diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index d14f693da9c..d61e7bb867c 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -45,6 +45,7 @@ #include "transform.h" #include "transform_convert.h" +#include "transform_orientations.h" #include "transform_snap.h" /* Own include. */ @@ -1263,7 +1264,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) if (t->data_type == TC_MESH_VERTS) { /* Init Custom Data correction. * Ideally this should be called when creating the TransData. */ - mesh_customdatacorrect_init(t); + transform_convert_mesh_customdatacorrect_init(t); } /* TODO(germano): Some of these operations change the `t->mode`. @@ -1271,4 +1272,38 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) * BLI_assert(t->mode == mode); */ } +/** + * When in modal and not set, initializes a default orientation for the mode. + */ +void transform_mode_default_modal_orientation_set(TransInfo *t, int type) +{ + /* Currently only these types are supported. */ + BLI_assert(ELEM(type, V3D_ORIENT_GLOBAL, V3D_ORIENT_VIEW)); + + if (t->is_orient_set) { + return; + } + + if (!(t->flag & T_MODAL)) { + return; + } + + if (t->orient[O_DEFAULT].type == type) { + return; + } + + RegionView3D *rv3d = NULL; + if ((type == V3D_ORIENT_VIEW) && (t->spacetype == SPACE_VIEW3D) && t->region && + (t->region->regiontype == RGN_TYPE_WINDOW)) { + rv3d = t->region->regiondata; + } + + t->orient[O_DEFAULT].type = ED_transform_calc_orientation_from_type_ex( + NULL, t->orient[O_DEFAULT].matrix, NULL, rv3d, NULL, NULL, type, 0); + + if (t->orient_curr == O_DEFAULT) { + /* Update Orientation. */ + transform_orientations_current_set(t, O_DEFAULT); + } +} /** \} */ diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index 6d7a0b528ae..106dc68c9ee 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -61,6 +61,7 @@ short getAnimEdit_SnapMode(TransInfo *t); void doAnimEdit_SnapFrame( TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap); void transform_mode_init(TransInfo *t, struct wmOperator *op, const int mode); +void transform_mode_default_modal_orientation_set(TransInfo *t, int type); /* transform_mode_align.c */ void initAlign(TransInfo *t); diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index ee63bf4be6f..68416c780ef 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -107,7 +107,6 @@ void initCurveShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index c78115561b2..b7b3de69731 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -148,5 +148,7 @@ void initNormalRotation(TransInfo *t) storeCustomLNorValue(tc, bm); } + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index e67c6c03481..7c496d271ef 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -117,7 +117,6 @@ void initGPOpacity(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index 95e3d89d2b7..608a49f38b1 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -119,7 +119,6 @@ void initGPShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index 3019984d70b..857ee37f0ad 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -133,7 +133,6 @@ void initMaskShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c index 9891af8b9a4..f225f1a7c06 100644 --- a/source/blender/editors/transform/transform_mode_mirror.c +++ b/source/blender/editors/transform/transform_mode_mirror.c @@ -235,8 +235,5 @@ void initMirror(TransInfo *t) initMouseInputMode(t, &t->mouse, INPUT_NONE); t->flag |= T_NULL_ONE; - if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; - } } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 4d0bb7fbe9c..0d7d0be3c0e 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -176,7 +176,6 @@ void initResize(TransInfo *t) t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; t->num.val_flag[1] |= NUM_NO_ZERO; @@ -194,5 +193,7 @@ void initResize(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; t->num.unit_type[1] = B_UNIT_NONE; t->num.unit_type[2] = B_UNIT_NONE; + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_GLOBAL); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 8d8594d5775..0fdbfb25989 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -249,5 +249,7 @@ void initRotation(TransInfo *t) if (t->flag & T_2D_EDIT) { t->flag |= T_NO_CONSTRAINT; } + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c index a41c49710b9..23ee55bf6c5 100644 --- a/source/blender/editors/transform/transform_mode_shear.c +++ b/source/blender/editors/transform/transform_mode_shear.c @@ -232,5 +232,7 @@ void initShear(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */ t->flag |= T_NO_CONSTRAINT; + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 8beacb844b9..77e57484bef 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -111,7 +111,6 @@ void initSkinResize(TransInfo *t) t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; t->num.val_flag[1] |= NUM_NO_ZERO; diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 41fc6ee0aaf..0bef6364214 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -470,5 +470,7 @@ void initTranslation(TransInfo *t) t->num.unit_type[1] = B_UNIT_NONE; t->num.unit_type[2] = B_UNIT_NONE; } + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_GLOBAL); } /** \} */ diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 5a5f478fc2b..b3ed294845d 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -671,10 +671,6 @@ const char *transform_orientations_spacename_get(TransInfo *t, const short orien void transform_orientations_current_set(TransInfo *t, const short orient_index) { - if (t->orient_curr == orient_index) { - return; - } - const short orientation = t->orient[orient_index].type; const char *spacename = transform_orientations_spacename_get(t, orientation); diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index d0f91802fff..193954fffb6 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -345,7 +345,7 @@ void applyProject(TransInfo *t) SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, - .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, .use_occlusion_test = false, .use_backface_culling = t->tsnap.use_backface_culling, }, @@ -1167,7 +1167,7 @@ short snapObjectsTransform( t->settings->snap_mode, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, - .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE, .use_backface_culling = t->tsnap.use_backface_culling, }, @@ -1201,7 +1201,7 @@ bool peelObjectsTransform(TransInfo *t, t->depsgraph, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, - .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, }, mval, -1.0f, diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index e5f6f207e3c..512f912a532 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -44,6 +44,7 @@ #include "BKE_duplilist.h" #include "BKE_editmesh.h" #include "BKE_geometry_set.h" +#include "BKE_global.h" #include "BKE_layer.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -145,9 +146,37 @@ struct SnapObjectContext { /** \name Utilities * \{ */ -static bool editmesh_eval_final_is_bmesh(const BMEditMesh *em) +/* Mesh used for snapping. + * If NULL the BMesh should be used. */ +static Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide) { - return (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH); + Mesh *me_eval = ob_eval->data; + bool use_hide = false; + if (BKE_object_is_in_editmode(ob_eval)) { + if (edit_mode_type == SNAP_GEOM_EDIT) { + return NULL; + } + + BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval); + if ((edit_mode_type == SNAP_GEOM_FINAL) && em_eval->mesh_eval_final) { + if (em_eval->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + return NULL; + } + me_eval = em_eval->mesh_eval_final; + use_hide = true; + } + else if ((edit_mode_type == SNAP_GEOM_CAGE) && em_eval->mesh_eval_cage) { + if (em_eval->mesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + return NULL; + } + me_eval = em_eval->mesh_eval_cage; + use_hide = true; + } + } + if (r_use_hide) { + *r_use_hide = use_hide; + } + return me_eval; } /** \} */ @@ -209,30 +238,69 @@ static void snap_object_data_clear(SnapObjectData *sod) memset(&sod->type, 0x0, sizeof(*sod) - offsetof(SnapObjectData, type)); } -static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob) +static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob_eval) { - SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval); if (sod == NULL) { if (sctx->cache.data_to_object_map != NULL) { - ob = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob->data); + ob_eval = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob_eval->data); /* Could be NULl when mixing edit-mode and non edit-mode objects. */ - if (ob != NULL) { - sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + if (ob_eval != NULL) { + sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval); } } } return sod; } -static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object *ob) +static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, + Object *ob_eval, + Mesh *me_eval, + bool use_hide) { SnapObjectData *sod; void **sod_p; bool init = false; - if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) { sod = *sod_p; + bool is_dirty = false; if (sod->type != SNAP_MESH) { + is_dirty = true; + } + else if (sod->treedata_mesh.tree && sod->treedata_mesh.cached && + !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->treedata_mesh.tree)) { + /* The tree is owned by the Mesh and may have been freed since we last used. */ + is_dirty = true; + } + else if (sod->bvhtree[0] && sod->cached[0] && + !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[0])) { + /* The tree is owned by the Mesh and may have been freed since we last used. */ + is_dirty = true; + } + else if (sod->bvhtree[1] && sod->cached[1] && + !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[1])) { + /* The tree is owned by the Mesh and may have been freed since we last used. */ + is_dirty = true; + } + else if (!sod->treedata_mesh.looptri_allocated && + sod->treedata_mesh.looptri != me_eval->runtime.looptris.array) { + is_dirty = true; + } + else if (!sod->treedata_mesh.vert_allocated && sod->treedata_mesh.vert != me_eval->mvert) { + is_dirty = true; + } + else if (!sod->treedata_mesh.loop_allocated && sod->treedata_mesh.loop != me_eval->mloop) { + is_dirty = true; + } + else if (!sod->treedata_mesh.edge_allocated && sod->treedata_mesh.edge != me_eval->medge) { + is_dirty = true; + } + else if (sod->poly != me_eval->mpoly) { + is_dirty = true; + } + + if (is_dirty) { snap_object_data_clear(sod); init = true; } @@ -244,8 +312,32 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object if (init) { sod->type = SNAP_MESH; - /* start assuming that it has each of these element types */ - sod->has_looptris = true; + + /* The BVHTree from looptris is always required. */ + BLI_assert(sod->treedata_mesh.tree == NULL); + BKE_bvhtree_from_mesh_get(&sod->treedata_mesh, + me_eval, + use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI, + 4); + + if (sod->treedata_mesh.tree == NULL) { + sod->treedata_mesh.vert = me_eval->mvert; + sod->treedata_mesh.loop = me_eval->mloop; + sod->treedata_mesh.looptri = BKE_mesh_runtime_looptri_ensure(me_eval); + BLI_assert(sod->has_looptris == false); + } + else { + BLI_assert(sod->treedata_mesh.vert != NULL); + BLI_assert(sod->treedata_mesh.loop != NULL); + BLI_assert(sod->treedata_mesh.looptri != NULL); + sod->has_looptris = true; + } + + /* Required for snapping with occlusion. */ + sod->treedata_mesh.edge = me_eval->medge; + sod->poly = me_eval->mpoly; + + /* Start assuming that it has each of these element types. */ sod->has_loose_edge = true; sod->has_loose_vert = true; } @@ -253,26 +345,26 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object return sod; } -static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob) +static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob_eval) { - BMEditMesh *em = BKE_editmesh_from_object(ob); - if (em->mesh_eval_final) { - return &em->mesh_eval_final->runtime; + BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval); + if (em_eval->mesh_eval_final) { + return &em_eval->mesh_eval_final->runtime; } - if (em->mesh_eval_cage) { - return &em->mesh_eval_cage->runtime; + if (em_eval->mesh_eval_cage) { + return &em_eval->mesh_eval_cage->runtime; } - return &((Mesh *)ob->data)->runtime; + return &((Mesh *)ob_eval->data)->runtime; } static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, - Object *ob, + Object *ob_eval, BMEditMesh *em) { SnapObjectData *sod; void **sod_p; - bool init = false, init_min_max = true, clear_cache = false; + bool init = false; { /* Use object-data as the key in ghash since the editmesh @@ -281,48 +373,53 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, sctx->cache.data_to_object_map = BLI_ghash_ptr_new(__func__); } void **ob_p; - if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob->data, &ob_p)) { - ob = *ob_p; + if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob_eval->data, &ob_p)) { + ob_eval = *ob_p; } else { - *ob_p = ob; + *ob_p = ob_eval; } } - if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) { sod = *sod_p; - bool clear = false; + bool is_dirty = false; /* Check if the geometry has changed. */ if (sod->type != SNAP_EDIT_MESH) { - clear = true; + is_dirty = true; } else if (sod->treedata_editmesh.em != em) { - clear_cache = true; - init = true; + is_dirty = true; } else if (sod->mesh_runtime) { - if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob)) { - clear_cache = true; - init = true; + if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) { + if (G.moving) { + /* Hack to avoid updating while transforming. */ + BLI_assert(!sod->treedata_editmesh.cached && !sod->cached[0] && !sod->cached[1]); + sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval); + } + else { + is_dirty = true; + } } else if (sod->treedata_editmesh.tree && sod->treedata_editmesh.cached && !bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->treedata_editmesh.tree)) { /* The tree is owned by the EditMesh and may have been freed since we last used! */ - clear = true; + is_dirty = true; } else if (sod->bvhtree[0] && sod->cached[0] && !bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[0])) { /* The tree is owned by the EditMesh and may have been freed since we last used! */ - clear = true; + is_dirty = true; } else if (sod->bvhtree[1] && sod->cached[1] && !bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[1])) { /* The tree is owned by the EditMesh and may have been freed since we last used! */ - clear = true; + is_dirty = true; } } - if (clear) { + if (is_dirty) { snap_object_data_clear(sod); init = true; } @@ -335,27 +432,8 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, if (init) { sod->type = SNAP_EDIT_MESH; sod->treedata_editmesh.em = em; - - if (clear_cache) { - /* Only init min and max when you have a non-custom bvhtree pending. */ - init_min_max = false; - if (sod->treedata_editmesh.cached) { - sod->treedata_editmesh.tree = NULL; - init_min_max = true; - } - for (int i = 0; i < ARRAY_SIZE(sod->bvhtree); i++) { - if (sod->cached[i]) { - sod->bvhtree[i] = NULL; - init_min_max = true; - } - } - } - - if (init_min_max) { - bm_mesh_minmax(em->bm, sod->min, sod->max); - } - - sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob); + sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval); + bm_mesh_minmax(em->bm, sod->min, sod->max); } return sod; @@ -368,9 +446,9 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, * \{ */ typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx, - Object *ob, + Object *ob_eval, float obmat[4][4], - bool use_obedit, + eSnapEditType edit_mode_type, bool use_backface_culling, bool is_object_active, void *data); @@ -387,10 +465,17 @@ static void iter_snap_objects(SnapObjectContext *sctx, ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); const View3D *v3d = sctx->v3d_data.v3d; const eSnapSelect snap_select = params->snap_select; - const bool use_object_edit_cage = params->use_object_edit_cage; + const eSnapEditType edit_mode_type = params->edit_mode_type; const bool use_backface_culling = params->use_backface_culling; Base *base_act = view_layer->basact; + if (snap_select == SNAP_ONLY_ACTIVE) { + Object *obj_eval = DEG_get_evaluated_object(depsgraph, base_act->object); + sob_callback( + sctx, obj_eval, obj_eval->obmat, edit_mode_type, use_backface_culling, true, data); + return; + } + for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { if (!BASE_VISIBLE(v3d, base)) { continue; @@ -425,7 +510,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, sob_callback(sctx, dupli_ob->ob, dupli_ob->mat, - use_object_edit_cage, + edit_mode_type, use_backface_culling, is_object_active, data); @@ -436,7 +521,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, sob_callback(sctx, obj_eval, obj_eval->obmat, - use_object_edit_cage, + edit_mode_type, use_backface_culling, is_object_active, data); @@ -464,7 +549,7 @@ struct RayCastAll_Data { float len_diff; float local_scale; - Object *ob; + Object *ob_eval; uint ob_uuid; /* output data */ @@ -476,7 +561,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth, const float co[3], const float no[3], int index, - Object *ob, + Object *ob_eval, const float obmat[4][4], uint ob_uuid) { @@ -487,7 +572,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth, copy_v3_v3(hit->no, no); hit->index = index; - hit->ob = ob; + hit->ob_eval = ob_eval; copy_m4_m4(hit->obmat, (float(*)[4])obmat); hit->ob_uuid = ob_uuid; @@ -529,7 +614,7 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH normalize_v3(normal); struct SnapObjectHitDepth *hit_item = hit_depth_create( - depth, location, normal, hit->index, data->ob, data->obmat, data->ob_uuid); + depth, location, normal, hit->index, data->ob_eval, data->obmat, data->ob_uuid); BLI_addtail(data->hit_list, hit_item); } } @@ -601,8 +686,8 @@ static void editmesh_looptri_raycast_backface_culling_cb(void *userdata, static bool raycastMesh(SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], - Object *ob, - Mesh *me, + Object *ob_eval, + Mesh *me_eval, const float obmat[4][4], const uint ob_index, bool use_hide, @@ -617,7 +702,7 @@ static bool raycastMesh(SnapObjectContext *sctx, { bool retval = false; - if (me->totpoly == 0) { + if (me_eval->totpoly == 0) { return retval; } @@ -641,7 +726,7 @@ static bool raycastMesh(SnapObjectContext *sctx, } /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob); + BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); if (bb) { /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!isect_ray_aabb_v3_simple( @@ -661,53 +746,18 @@ static bool raycastMesh(SnapObjectContext *sctx, len_diff = 0.0f; } - SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob); + SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide); BVHTreeFromMesh *treedata = &sod->treedata_mesh; - /* The tree is owned by the Mesh and may have been freed since we last used. */ - if (treedata->tree) { - BLI_assert(treedata->cached); - if (!bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) { - free_bvhtree_from_mesh(treedata); - } - else { - /* Update Pointers. */ - if (treedata->vert && treedata->vert_allocated == false) { - treedata->vert = me->mvert; - } - if (treedata->loop && treedata->loop_allocated == false) { - treedata->loop = me->mloop; - } - if (treedata->looptri && treedata->looptri_allocated == false) { - treedata->looptri = BKE_mesh_runtime_looptri_ensure(me); - } - /* required for snapping with occlusion. */ - treedata->edge = me->medge; - sod->poly = me->mpoly; - } - } - if (treedata->tree == NULL) { - if (use_hide) { - BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI_NO_HIDDEN, 4); - } - else { - BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4); - } - - /* required for snapping with occlusion. */ - treedata->edge = me->medge; - sod->poly = me->mpoly; - - if (treedata->tree == NULL) { - return retval; - } + return retval; } float timat[3][3]; /* transpose inverse matrix for normals */ transpose_m3_m4(timat, imat); + BLI_assert(treedata->raycast_callback != NULL); if (r_hit_list) { struct RayCastAll_Data data; @@ -717,7 +767,7 @@ static bool raycastMesh(SnapObjectContext *sctx, data.timat = timat; data.len_diff = len_diff; data.local_scale = local_scale; - data.ob = ob; + data.ob_eval = ob_eval; data.ob_uuid = ob_index; data.hit_list = r_hit_list; data.retval = retval; @@ -776,7 +826,7 @@ static bool raycastMesh(SnapObjectContext *sctx, static bool raycastEditMesh(SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], - Object *ob, + Object *ob_eval, BMEditMesh *em, const float obmat[4][4], const uint ob_index, @@ -813,7 +863,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx, local_depth *= local_scale; } - SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em); + SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em); /* Test BoundBox */ @@ -839,7 +889,8 @@ static bool raycastEditMesh(SnapObjectContext *sctx, if (treedata->tree == NULL) { /* Operators only update the editmesh looptris of the original mesh. */ - BLI_assert(sod->treedata_editmesh.em == BKE_editmesh_from_object(DEG_get_original_object(ob))); + BLI_assert(sod->treedata_editmesh.em == + BKE_editmesh_from_object(DEG_get_original_object(ob_eval))); em = sod->treedata_editmesh.em; if (sctx->callbacks.edit_mesh.test_face_fn) { @@ -886,7 +937,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx, data.timat = timat; data.len_diff = len_diff; data.local_scale = local_scale; - data.ob = ob; + data.ob_eval = ob_eval; data.ob_uuid = ob_index; data.hit_list = r_hit_list; data.retval = retval; @@ -967,9 +1018,9 @@ struct RaycastObjUserData { * \note Duplicate args here are documented at #snapObjectsRay */ static void raycast_obj_fn(SnapObjectContext *sctx, - Object *ob, + Object *ob_eval, float obmat[4][4], - bool use_obedit, + eSnapEditType edit_mode_type, bool use_backface_culling, bool is_object_active, void *data) @@ -982,53 +1033,46 @@ static void raycast_obj_fn(SnapObjectContext *sctx, bool retval = false; if (use_occlusion_test) { - if (use_obedit && sctx->use_v3d && XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) { + if ((edit_mode_type == SNAP_GEOM_EDIT) && sctx->use_v3d && + XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) { /* Use of occlude geometry in editing mode disabled. */ return; } - if (ELEM(ob->dt, OB_BOUNDBOX, OB_WIRE)) { + if (ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) { /* Do not hit objects that are in wire or bounding box * display mode. */ return; } } - switch (ob->type) { + switch (ob_eval->type) { case OB_MESH: { - Mesh *me = ob->data; bool use_hide = false; - if (BKE_object_is_in_editmode(ob)) { - if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) { - /* Operators only update the editmesh looptris of the original mesh. */ - BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob)); - retval = raycastEditMesh(sctx, - dt->ray_start, - dt->ray_dir, - ob, - em_orig, - obmat, - ob_index, - use_backface_culling, - ray_depth, - dt->r_loc, - dt->r_no, - dt->r_index, - dt->r_hit_list); - break; - } - - BMEditMesh *em = BKE_editmesh_from_object(ob); - if (em->mesh_eval_final) { - me = em->mesh_eval_final; - use_hide = true; - } + Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); + if (me_eval == NULL) { + /* Operators only update the editmesh looptris of the original mesh. */ + BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval)); + retval = raycastEditMesh(sctx, + dt->ray_start, + dt->ray_dir, + ob_eval, + em_orig, + obmat, + ob_index, + use_backface_culling, + ray_depth, + dt->r_loc, + dt->r_no, + dt->r_index, + dt->r_hit_list); + break; } retval = raycastMesh(sctx, dt->ray_start, dt->ray_dir, - ob, - me, + ob_eval, + me_eval, obmat, ob_index, use_hide, @@ -1044,12 +1088,12 @@ static void raycast_obj_fn(SnapObjectContext *sctx, case OB_SURF: case OB_FONT: { if (!is_object_active) { - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); if (mesh_eval) { retval = raycastMesh(sctx, dt->ray_start, dt->ray_dir, - ob, + ob_eval, mesh_eval, obmat, ob_index, @@ -1068,7 +1112,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx, if (retval) { if (dt->r_ob) { - *dt->r_ob = ob; + *dt->r_ob = ob_eval; } if (dt->r_obmat) { copy_m4_m4(dt->r_obmat, obmat); @@ -1323,6 +1367,33 @@ typedef struct Nearest2dUserData { bool use_backface_culling; } Nearest2dUserData; +static void nearest2d_data_init(SnapObjectData *sod, + bool is_persp, + bool use_backface_culling, + Nearest2dUserData *r_nearest2d) +{ + if (sod->type == SNAP_MESH) { + r_nearest2d->userdata = &sod->treedata_mesh; + r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; + r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; + r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; + r_nearest2d->get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get; + r_nearest2d->get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get; + } + else { + BLI_assert(sod->type == SNAP_EDIT_MESH); + r_nearest2d->userdata = sod->treedata_editmesh.em; + r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; + r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; + r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; + r_nearest2d->get_tri_verts_index = NULL; + r_nearest2d->get_tri_edges_index = NULL; + } + + r_nearest2d->is_persp = is_persp; + r_nearest2d->use_backface_culling = use_backface_culling; +} + static void cb_snap_vert(void *userdata, int index, const struct DistProjectedAABBPrecalc *precalc, @@ -1470,7 +1541,7 @@ static void cb_snap_tri_verts(void *userdata, static short snap_mesh_polygon(SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], bool use_backface_culling, /* read/write args */ @@ -1495,28 +1566,21 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); } - Nearest2dUserData nearest2d = { - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, - .use_backface_culling = use_backface_culling, - }; - BVHTreeNearest nearest = { .index = -1, .dist_sq = square_f(*dist_px), }; - SnapObjectData *sod = snap_object_data_lookup(sctx, ob); - + SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval); BLI_assert(sod != NULL); + Nearest2dUserData nearest2d; + nearest2d_data_init( + sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + if (sod->type == SNAP_MESH) { BVHTreeFromMesh *treedata = &sod->treedata_mesh; - nearest2d.userdata = treedata; - nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; - nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; - nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; - const MPoly *mp = &sod->poly[*r_index]; const MLoop *ml = &treedata->loop[mp->loopstart]; if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { @@ -1547,11 +1611,6 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BLI_assert(sod->type == SNAP_EDIT_MESH); BMEditMesh *em = sod->treedata_editmesh.em; - nearest2d.userdata = em; - nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; - nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; - nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; - BM_mesh_elem_table_ensure(em->bm, BM_FACE); BMFace *f = BM_face_at_index(em->bm, *r_index); BMLoop *l_iter, *l_first; @@ -1608,7 +1667,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], float original_dist_px, const float prev_co[3], @@ -1622,32 +1681,16 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, { short elem = SCE_SNAP_MODE_EDGE; - if (ob->type != OB_MESH) { + if (ob_eval->type != OB_MESH) { return elem; } - SnapObjectData *sod = snap_object_data_lookup(sctx, ob); - + SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval); BLI_assert(sod != NULL); Nearest2dUserData nearest2d; - { - nearest2d.is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; - nearest2d.use_backface_culling = use_backface_culling; - if (sod->type == SNAP_MESH) { - nearest2d.userdata = &sod->treedata_mesh; - nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; - nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; - nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; - } - else { - BLI_assert(sod->type == SNAP_EDIT_MESH); - nearest2d.userdata = sod->treedata_editmesh.em; - nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; - nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; - nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; - } - } + nearest2d_data_init( + sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); int vindex[2]; nearest2d.get_edge_verts_index(*r_index, vindex, nearest2d.userdata); @@ -1772,9 +1815,8 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, } static short snapArmature(SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], - bool use_obedit, /* read/write args */ float *dist_px, /* return args */ @@ -1795,11 +1837,11 @@ static short snapArmature(SnapData *snapdata, dist_squared_to_projected_aabb_precalc( &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); - use_obedit = use_obedit && BKE_object_is_in_editmode(ob); + bool use_obedit = ((bArmature *)ob_eval->data)->edbo != NULL; if (use_obedit == false) { /* Test BoundBox */ - BoundBox *bb = BKE_armature_boundbox_get(ob); + BoundBox *bb = BKE_armature_boundbox_get(ob_eval); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { return retval; @@ -1814,7 +1856,7 @@ static short snapArmature(SnapData *snapdata, bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; - bArmature *arm = ob->data; + bArmature *arm = ob_eval->data; if (arm->edbo) { LISTBASE_FOREACH (EditBone *, eBone, arm->edbo) { if (eBone->layer & arm->layer) { @@ -1858,8 +1900,8 @@ static short snapArmature(SnapData *snapdata, } } } - else if (ob->pose && ob->pose->chanbase.first) { - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + else if (ob_eval->pose && ob_eval->pose->chanbase.first) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_eval->pose->chanbase) { Bone *bone = pchan->bone; /* skip hidden bones */ if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { @@ -1917,7 +1959,7 @@ static short snapArmature(SnapData *snapdata, } static short snapCurve(SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], bool use_obedit, /* read/write args */ @@ -1934,7 +1976,7 @@ static short snapCurve(SnapData *snapdata, return 0; } - Curve *cu = ob->data; + Curve *cu = ob_eval->data; float dist_px_sq = square_f(*dist_px); float lpmat[4][4]; @@ -1944,11 +1986,11 @@ static short snapCurve(SnapData *snapdata, dist_squared_to_projected_aabb_precalc( &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); - use_obedit = use_obedit && BKE_object_is_in_editmode(ob); + use_obedit = use_obedit && BKE_object_is_in_editmode(ob_eval); if (use_obedit == false) { /* Test BoundBox */ - BoundBox *bb = BKE_curve_boundbox_get(ob); + BoundBox *bb = BKE_curve_boundbox_get(ob_eval); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { return 0; @@ -2068,7 +2110,7 @@ static short snapCurve(SnapData *snapdata, /* may extend later (for now just snaps to empty center) */ static short snap_object_center(SnapData *snapdata, - Object *ob, + Object *ob_eval, const float obmat[4][4], /* read/write args */ float *dist_px, @@ -2079,7 +2121,7 @@ static short snap_object_center(SnapData *snapdata, { short retval = 0; - if (ob->transflag & OB_DUPLI) { + if (ob_eval->transflag & OB_DUPLI) { return retval; } @@ -2222,9 +2264,10 @@ static short snapCamera(const SnapObjectContext *sctx, static short snapMesh(SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, - Mesh *me, + Object *ob_eval, + Mesh *me_eval, const float obmat[4][4], + bool use_hide, bool use_backface_culling, /* read/write args */ float *dist_px, @@ -2234,10 +2277,10 @@ static short snapMesh(SnapObjectContext *sctx, int *r_index) { BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); - if (me->totvert == 0) { + if (me_eval->totvert == 0) { return 0; } - if (me->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + if (me_eval->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { return 0; } @@ -2247,65 +2290,46 @@ static short snapMesh(SnapObjectContext *sctx, float dist_px_sq = square_f(*dist_px); /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob); + BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { return 0; } - SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob); + SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide); - BVHTreeFromMesh *treedata, dummy_treedata; + BVHTreeFromMesh *treedata, treedata_tmp; treedata = &sod->treedata_mesh; - /* The tree is owned by the Mesh and may have been freed since we last used! */ - if (treedata->cached && treedata->tree && - !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) { - free_bvhtree_from_mesh(treedata); - } - if (sod->cached[0] && sod->bvhtree[0] && - !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[0])) { - sod->bvhtree[0] = NULL; - } - if (sod->cached[1] && sod->bvhtree[1] && - !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[1])) { - sod->bvhtree[1] = NULL; - } - - if (sod->has_looptris && treedata->tree == NULL) { - BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4); - sod->has_looptris = (treedata->tree != NULL); - if (sod->has_looptris) { - /* Make sure that the array of edges is referenced in the callbacks. */ - treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */ - } - } if (sod->has_loose_edge && sod->bvhtree[0] == NULL) { - sod->bvhtree[0] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEEDGES, 2); - sod->has_loose_edge = sod->bvhtree[0] != NULL; - sod->cached[0] = dummy_treedata.cached; - - if (sod->has_loose_edge) { - BLI_assert(treedata->vert_allocated == false); - treedata->vert = dummy_treedata.vert; - treedata->vert_allocated = dummy_treedata.vert_allocated; - - BLI_assert(treedata->edge_allocated == false); - treedata->edge = dummy_treedata.edge; - treedata->edge_allocated = dummy_treedata.edge_allocated; + sod->bvhtree[0] = BKE_bvhtree_from_mesh_get( + &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEEDGES, 2); + if (sod->bvhtree[0] == NULL) { + sod->has_loose_edge = false; } + sod->cached[0] = treedata_tmp.cached; + BLI_assert(!ELEM(true, + treedata_tmp.vert_allocated, + treedata_tmp.edge_allocated, + treedata_tmp.face_allocated, + treedata_tmp.loop_allocated, + treedata_tmp.looptri_allocated)); } + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { if (sod->has_loose_vert && sod->bvhtree[1] == NULL) { - sod->bvhtree[1] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEVERTS, 2); - sod->has_loose_vert = sod->bvhtree[1] != NULL; - sod->cached[1] = dummy_treedata.cached; - - if (sod->has_loose_vert) { - BLI_assert(treedata->vert_allocated == false); - treedata->vert = dummy_treedata.vert; - treedata->vert_allocated = dummy_treedata.vert_allocated; + sod->bvhtree[1] = BKE_bvhtree_from_mesh_get( + &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEVERTS, 2); + if (sod->bvhtree[1] == NULL) { + sod->has_loose_vert = false; } + sod->cached[1] = treedata_tmp.cached; + BLI_assert(!ELEM(true, + treedata_tmp.vert_allocated, + treedata_tmp.edge_allocated, + treedata_tmp.face_allocated, + treedata_tmp.loop_allocated, + treedata_tmp.looptri_allocated)); } } else { @@ -2313,33 +2337,9 @@ static short snapMesh(SnapObjectContext *sctx, sod->has_loose_vert = false; } - /* Update pointers. */ - if (treedata->vert_allocated == false) { - treedata->vert = me->mvert; /* CustomData_get_layer(&me->vdata, CD_MVERT);? */ - } - if (treedata->tree || sod->bvhtree[0]) { - if (treedata->edge_allocated == false) { - /* If raycast has been executed before, `treedata->edge` can be NULL. */ - treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */ - } - if (treedata->loop && treedata->loop_allocated == false) { - treedata->loop = me->mloop; /* CustomData_get_layer(&me->edata, CD_MLOOP);? */ - } - if (treedata->looptri && treedata->looptri_allocated == false) { - treedata->looptri = BKE_mesh_runtime_looptri_ensure(me); - } - } - - Nearest2dUserData nearest2d = { - .userdata = treedata, - .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get, - .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get, - .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get, - .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get, - .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy, - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, - .use_backface_culling = use_backface_culling, - }; + Nearest2dUserData nearest2d; + nearest2d_data_init( + sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); BVHTreeNearest nearest = { .index = -1, @@ -2455,7 +2455,7 @@ static short snapMesh(SnapObjectContext *sctx, static short snapEditMesh(SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, + Object *ob_eval, BMEditMesh *em, const float obmat[4][4], bool use_backface_culling, @@ -2484,7 +2484,7 @@ static short snapEditMesh(SnapObjectContext *sctx, float dist_px_sq = square_f(*dist_px); - SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em); + SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em); /* Test BoundBox */ @@ -2558,14 +2558,9 @@ static short snapEditMesh(SnapObjectContext *sctx, } } - Nearest2dUserData nearest2d = { - .userdata = em, - .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get, - .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get, - .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy, - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, - .use_backface_culling = use_backface_culling, - }; + Nearest2dUserData nearest2d; + nearest2d_data_init( + sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); BVHTreeNearest nearest = { .index = -1, @@ -2659,9 +2654,9 @@ struct SnapObjUserData { * \note Duplicate args here are documented at #snapObjectsRay */ static void snap_obj_fn(SnapObjectContext *sctx, - Object *ob, + Object *ob_eval, float obmat[4][4], - bool use_obedit, + eSnapEditType edit_mode_type, bool use_backface_culling, bool UNUSED(is_object_active), void *data) @@ -2669,41 +2664,36 @@ static void snap_obj_fn(SnapObjectContext *sctx, struct SnapObjUserData *dt = data; short retval = 0; - switch (ob->type) { + switch (ob_eval->type) { case OB_MESH: { - Mesh *me = ob->data; - if (BKE_object_is_in_editmode(ob)) { - if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) { - /* Operators only update the editmesh looptris of the original mesh. */ - BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob)); - retval = snapEditMesh(sctx, - dt->snapdata, - ob, - em_orig, - obmat, - use_backface_culling, - dt->dist_px, - dt->r_loc, - dt->r_no, - dt->r_index); - break; - } - - BMEditMesh *em = BKE_editmesh_from_object(ob); - if (em->mesh_eval_final) { - me = em->mesh_eval_final; - } + bool use_hide; + Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); + if (me_eval == NULL) { + /* Operators only update the editmesh looptris of the original mesh. */ + BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval)); + retval = snapEditMesh(sctx, + dt->snapdata, + ob_eval, + em_orig, + obmat, + use_backface_culling, + dt->dist_px, + dt->r_loc, + dt->r_no, + dt->r_index); + break; } - else if (ob->dt == OB_BOUNDBOX) { + if (ob_eval->dt == OB_BOUNDBOX) { /* Do not snap to objects that are in bounding box display mode */ return; } retval = snapMesh(sctx, dt->snapdata, - ob, - me, + ob_eval, + me_eval, obmat, + use_hide, use_backface_culling, dt->dist_px, dt->r_loc, @@ -2713,21 +2703,28 @@ static void snap_obj_fn(SnapObjectContext *sctx, } case OB_ARMATURE: retval = snapArmature( - dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; case OB_CURVE: - retval = snapCurve( - dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + retval = snapCurve(dt->snapdata, + ob_eval, + obmat, + edit_mode_type == SNAP_GEOM_EDIT, + dt->dist_px, + dt->r_loc, + dt->r_no, + dt->r_index); break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */ case OB_SURF: case OB_FONT: { - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); if (mesh_eval) { retval |= snapMesh(sctx, dt->snapdata, - ob, + ob_eval, mesh_eval, obmat, + false, use_backface_culling, dt->dist_px, dt->r_loc, @@ -2740,17 +2737,17 @@ static void snap_obj_fn(SnapObjectContext *sctx, case OB_GPENCIL: case OB_LAMP: retval = snap_object_center( - dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; case OB_CAMERA: retval = snapCamera( - sctx, dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + sctx, dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; } if (retval) { if (dt->r_ob) { - *dt->r_ob = ob; + *dt->r_ob = ob_eval; } if (dt->r_obmat) { copy_m4_m4(dt->r_obmat, obmat); @@ -3023,7 +3020,7 @@ static short transform_snap_context_project_view3d_mixed_impl( short retval = 0; bool has_hit = false; - Object *ob = NULL; + Object *ob_eval = NULL; float loc[3]; /* Not all snapping callbacks set the normal, * initialize this since any hit copies both the `loc` and `no`. */ @@ -3060,7 +3057,7 @@ static short transform_snap_context_project_view3d_mixed_impl( loc, no, &index, - &ob, + &ob_eval, obmat, NULL); @@ -3072,7 +3069,7 @@ static short transform_snap_context_project_view3d_mixed_impl( copy_v3_v3(r_no, no); } if (r_ob) { - *r_ob = ob; + *r_ob = ob_eval; } if (r_obmat) { copy_m4_m4(r_obmat, obmat); @@ -3108,9 +3105,10 @@ static short transform_snap_context_project_view3d_mixed_impl( snapdata.has_occlusion_plane = false; /* By convention we only snap to the original elements of a curve. */ - if (has_hit && ob->type != OB_CURVE) { + if (has_hit && ob_eval->type != OB_CURVE) { /* Compute the new clip_pane but do not add it yet. */ float new_clipplane[4]; + BLI_ASSERT_UNIT_V3(no); plane_from_point_normal_v3(new_clipplane, loc, no); if (dot_v3v3(snapdata.clip_plane[0], new_clipplane) > 0.0f) { /* The plane is facing the wrong direction. */ @@ -3121,8 +3119,15 @@ static short transform_snap_context_project_view3d_mixed_impl( new_clipplane[3] += 0.01f; /* Try to snap only to the polygon. */ - elem_test = snap_mesh_polygon( - sctx, &snapdata, ob, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index); + elem_test = snap_mesh_polygon(sctx, + &snapdata, + ob_eval, + obmat, + params->use_backface_culling, + &dist_px_tmp, + loc, + no, + &index); if (elem_test) { elem = elem_test; } @@ -3137,7 +3142,7 @@ static short transform_snap_context_project_view3d_mixed_impl( } elem_test = snapObjectsRay( - sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob, obmat); + sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat); if (elem_test) { elem = elem_test; } @@ -3148,7 +3153,7 @@ static short transform_snap_context_project_view3d_mixed_impl( snapdata.snap_to_flag = snap_to_flag; elem = snap_mesh_edge_verts_mixed(sctx, &snapdata, - ob, + ob_eval, obmat, *dist_px, prev_co, @@ -3167,7 +3172,7 @@ static short transform_snap_context_project_view3d_mixed_impl( copy_v3_v3(r_no, no); } if (r_ob) { - *r_ob = ob; + *r_ob = ob_eval; } if (r_obmat) { copy_m4_m4(r_obmat, obmat); diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 54ea0103fe5..cd1597a742c 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -666,7 +666,7 @@ class CPPType : NonCopyable, NonMovable { template<typename T> bool is() const { - return this == &CPPType::get<T>(); + return this == &CPPType::get<std::decay_t<T>>(); } }; diff --git a/source/blender/functions/FN_generic_pointer.hh b/source/blender/functions/FN_generic_pointer.hh index 2bd66daa7fe..f88ff09f916 100644 --- a/source/blender/functions/FN_generic_pointer.hh +++ b/source/blender/functions/FN_generic_pointer.hh @@ -66,6 +66,16 @@ class GMutablePointer { return type_ != nullptr && type_->is<T>(); } + template<typename T> T relocate_out() + { + BLI_assert(this->is_type<T>()); + T value; + type_->relocate_to_initialized(data_, &value); + data_ = nullptr; + type_ = nullptr; + return value; + } + void destruct() { BLI_assert(data_ != nullptr); diff --git a/source/blender/functions/FN_generic_span.hh b/source/blender/functions/FN_generic_span.hh index 31b67dd3d70..e2c49697ba9 100644 --- a/source/blender/functions/FN_generic_span.hh +++ b/source/blender/functions/FN_generic_span.hh @@ -30,7 +30,7 @@ namespace blender::fn { * A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time. */ class GSpan { - private: + protected: const CPPType *type_; const void *data_; int64_t size_; @@ -85,6 +85,14 @@ class GSpan { BLI_assert(type_->is<T>()); return Span<T>(static_cast<const T *>(data_), size_); } + + GSpan slice(const int64_t start, int64_t size) const + { + BLI_assert(start >= 0); + BLI_assert(size >= 0); + const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start)); + return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size); + } }; /** @@ -92,7 +100,7 @@ class GSpan { * known at run-time. */ class GMutableSpan { - private: + protected: const CPPType *type_; void *data_; int64_t size_; @@ -153,6 +161,14 @@ class GMutableSpan { BLI_assert(type_->is<T>()); return MutableSpan<T>(static_cast<T *>(data_), size_); } + + GMutableSpan slice(const int64_t start, int64_t size) const + { + BLI_assert(start >= 0); + BLI_assert(size >= 0); + const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start)); + return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size); + } }; } // namespace blender::fn diff --git a/source/blender/functions/FN_generic_value_map.hh b/source/blender/functions/FN_generic_value_map.hh index 68cb945f1af..4e7fe298874 100644 --- a/source/blender/functions/FN_generic_value_map.hh +++ b/source/blender/functions/FN_generic_value_map.hh @@ -93,6 +93,11 @@ template<typename Key> class GValueMap { return values_.pop_as(key); } + template<typename ForwardKey> GPointer lookup(const ForwardKey &key) const + { + return values_.lookup_as(key); + } + /* Remove the value for the given name from the container and remove it. */ template<typename T, typename ForwardKey> T extract(const ForwardKey &key) { diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh index ae6eb8a614f..b02ed471875 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/functions/FN_generic_vector_array.hh @@ -123,7 +123,7 @@ template<typename T> class GVectorArray_TypedMutableRef { void extend(const int64_t index, const VArray<T> &values) { - GVArrayForVArray<T> array{values}; + GVArray_For_VArray<T> array{values}; this->extend(index, array); } @@ -134,12 +134,12 @@ template<typename T> class GVectorArray_TypedMutableRef { }; /* A generic virtual vector array implementation for a `GVectorArray`. */ -class GVVectorArrayForGVectorArray : public GVVectorArray { +class GVVectorArray_For_GVectorArray : public GVVectorArray { private: const GVectorArray &vector_array_; public: - GVVectorArrayForGVectorArray(const GVectorArray &vector_array) + GVVectorArray_For_GVectorArray(const GVectorArray &vector_array) : GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array) { } diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index c6230730a8d..d530d10b3c8 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -23,12 +23,23 @@ * the data type is only known at runtime. */ +#include <optional> + #include "BLI_virtual_array.hh" #include "FN_generic_span.hh" namespace blender::fn { +template<typename T> class GVArray_Typed; +template<typename T> class GVMutableArray_Typed; + +class GVArray; +class GVMutableArray; + +using GVArrayPtr = std::unique_ptr<GVArray>; +using GVMutableArrayPtr = std::unique_ptr<GVMutableArray>; + /* A generically typed version of `VArray<T>`. */ class GVArray { protected: @@ -86,13 +97,13 @@ class GVArray { /* Returns the internally used span of the virtual array. This invokes undefined behavior is the * virtual array is not stored as a span internally. */ - GSpan get_span() const + GSpan get_internal_span() const { BLI_assert(this->is_span()); if (size_ == 0) { return GSpan(*type_); } - return this->get_span_impl(); + return this->get_internal_span_impl(); } /* Returns true when the virtual array returns the same value for every index. */ @@ -107,57 +118,151 @@ class GVArray { /* Copies the value that is used for every element into `r_value`, which is expected to point to * initialized memory. This invokes undefined behavior if the virtual array would not return the * same value for every index. */ - void get_single(void *r_value) const + void get_internal_single(void *r_value) const { BLI_assert(this->is_single()); if (size_ == 1) { this->get(0, r_value); + return; } - this->get_single_impl(r_value); + this->get_internal_single_impl(r_value); } - /* Same as `get_single`, but `r_value` points to initialized memory. */ + /* Same as `get_internal_single`, but `r_value` points to initialized memory. */ void get_single_to_uninitialized(void *r_value) const { type_->construct_default(r_value); - this->get_single(r_value); + this->get_internal_single(r_value); } + void materialize(void *dst) const; + void materialize(const IndexMask mask, void *dst) const; + + void materialize_to_uninitialized(void *dst) const; void materialize_to_uninitialized(const IndexMask mask, void *dst) const; + template<typename T> const VArray<T> *try_get_internal_varray() const + { + BLI_assert(type_->is<T>()); + return (const VArray<T> *)this->try_get_internal_varray_impl(); + } + + /* Create a typed virtual array for this generic virtual array. */ + template<typename T> GVArray_Typed<T> typed() const + { + return GVArray_Typed<T>(*this); + } + + GVArrayPtr shallow_copy() const; + protected: virtual void get_impl(const int64_t index, void *r_value) const; virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0; virtual bool is_span_impl() const; - virtual GSpan get_span_impl() const; + virtual GSpan get_internal_span_impl() const; virtual bool is_single_impl() const; - virtual void get_single_impl(void *UNUSED(r_value)) const; + virtual void get_internal_single_impl(void *UNUSED(r_value)) const; + + virtual void materialize_impl(const IndexMask mask, void *dst) const; + virtual void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const; + + virtual const void *try_get_internal_varray_impl() const; +}; + +/* Similar to GVArray, but supports changing the elements in the virtual array. */ +class GVMutableArray : public GVArray { + public: + GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size) + { + } + + void set_by_copy(const int64_t index, const void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_copy_impl(index, value); + } + + void set_by_move(const int64_t index, void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_move_impl(index, value); + } + + void set_by_relocate(const int64_t index, void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_relocate_impl(index, value); + } + + GMutableSpan get_internal_span() + { + BLI_assert(this->is_span()); + GSpan span = static_cast<const GVArray *>(this)->get_internal_span(); + return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size()); + } + + template<typename T> VMutableArray<T> *try_get_internal_mutable_varray() + { + BLI_assert(type_->is<T>()); + return (VMutableArray<T> *)this->try_get_internal_mutable_varray_impl(); + } + + /* Create a typed virtual array for this generic virtual array. */ + template<typename T> GVMutableArray_Typed<T> typed() + { + return GVMutableArray_Typed<T>(*this); + } + + void fill(const void *value); + + /* Copy the values from the source buffer to all elements in the virtual array. */ + void set_all(const void *src) + { + this->set_all_impl(src); + } + + protected: + virtual void set_by_copy_impl(const int64_t index, const void *value); + virtual void set_by_relocate_impl(const int64_t index, void *value); + virtual void set_by_move_impl(const int64_t index, void *value) = 0; + + virtual void set_all_impl(const void *src); + + virtual void *try_get_internal_mutable_varray_impl(); }; -class GVArrayForGSpan : public GVArray { +class GVArray_For_GSpan : public GVArray { protected: - const void *data_; + const void *data_ = nullptr; const int64_t element_size_; public: - GVArrayForGSpan(const GSpan span) + GVArray_For_GSpan(const GSpan span) : GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size()) { } protected: + GVArray_For_GSpan(const CPPType &type, const int64_t size) + : GVArray(type, size), element_size_(type.size()) + { + } + void get_impl(const int64_t index, void *r_value) const override; void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; bool is_span_impl() const override; - GSpan get_span_impl() const override; + GSpan get_internal_span_impl() const override; }; -class GVArrayForEmpty : public GVArray { +class GVArray_For_Empty : public GVArray { public: - GVArrayForEmpty(const CPPType &type) : GVArray(type, 0) + GVArray_For_Empty(const CPPType &type) : GVArray(type, 0) { } @@ -168,108 +273,642 @@ class GVArrayForEmpty : public GVArray { } }; -class GVArrayForSingleValueRef : public GVArray { - private: - const void *value_; +class GVMutableArray_For_GMutableSpan : public GVMutableArray { + protected: + void *data_ = nullptr; + const int64_t element_size_; public: - GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value) + GVMutableArray_For_GMutableSpan(const GMutableSpan span) + : GVMutableArray(span.type(), span.size()), + data_(span.data()), + element_size_(span.type().size()) + { + } + + protected: + GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size) + : GVMutableArray(type, size), element_size_(type.size()) + { + } + + void get_impl(const int64_t index, void *r_value) const override; + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; + + void set_by_copy_impl(const int64_t index, const void *value) override; + void set_by_move_impl(const int64_t index, void *value) override; + void set_by_relocate_impl(const int64_t index, void *value) override; + + bool is_span_impl() const override; + GSpan get_internal_span_impl() const override; +}; + +/* Generic virtual array where each element has the same value. The value is not owned. */ +class GVArray_For_SingleValueRef : public GVArray { + protected: + const void *value_ = nullptr; + + public: + GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value) : GVArray(type, size), value_(value) { } protected: + GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size) + { + } + void get_impl(const int64_t index, void *r_value) const override; void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; bool is_span_impl() const override; - GSpan get_span_impl() const override; + GSpan get_internal_span_impl() const override; bool is_single_impl() const override; - void get_single_impl(void *r_value) const override; + void get_internal_single_impl(void *r_value) const override; }; -template<typename T> class GVArrayForVArray : public GVArray { - private: - const VArray<T> &array_; +/* Same as GVArray_For_SingleValueRef, but the value is owned. */ +class GVArray_For_SingleValue : public GVArray_For_SingleValueRef { + public: + GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value); + ~GVArray_For_SingleValue(); +}; + +/* Used to convert a typed virtual array into a generic one. */ +template<typename T> class GVArray_For_VArray : public GVArray { + protected: + const VArray<T> *varray_ = nullptr; public: - GVArrayForVArray(const VArray<T> &array) - : GVArray(CPPType::get<T>(), array.size()), array_(array) + GVArray_For_VArray(const VArray<T> &varray) + : GVArray(CPPType::get<T>(), varray.size()), varray_(&varray) { } protected: + GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), size) + { + } + void get_impl(const int64_t index, void *r_value) const override { - *(T *)r_value = array_.get(index); + *(T *)r_value = varray_->get(index); } void get_to_uninitialized_impl(const int64_t index, void *r_value) const override { - new (r_value) T(array_.get(index)); + new (r_value) T(varray_->get(index)); } bool is_span_impl() const override { - return array_.is_span(); + return varray_->is_span(); } - GSpan get_span_impl() const override + GSpan get_internal_span_impl() const override { - return GSpan(array_.get_span()); + return GSpan(varray_->get_internal_span()); } bool is_single_impl() const override { - return array_.is_single(); + return varray_->is_single(); } - void get_single_impl(void *r_value) const override + void get_internal_single_impl(void *r_value) const override { - *(T *)r_value = array_.get_single(); + *(T *)r_value = varray_->get_internal_single(); + } + + void materialize_impl(const IndexMask mask, void *dst) const override + { + varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override + { + varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + const void *try_get_internal_varray_impl() const override + { + return varray_; } }; -template<typename T> class VArrayForGVArray : public VArray<T> { - private: - const GVArray &array_; +/* Used to convert any generic virtual array into a typed one. */ +template<typename T> class VArray_For_GVArray : public VArray<T> { + protected: + const GVArray *varray_ = nullptr; public: - VArrayForGVArray(const GVArray &array) : VArray<T>(array.size()), array_(array) + VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray) { - BLI_assert(array_.type().template is<T>()); + BLI_assert(varray_->type().template is<T>()); } protected: + VArray_For_GVArray(const int64_t size) : VArray<T>(size) + { + } + T get_impl(const int64_t index) const override { T value; - array_.get(index, &value); + varray_->get(index, &value); return value; } bool is_span_impl() const override { - return array_.is_span(); + return varray_->is_span(); } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { - return array_.get_span().template typed<T>(); + return varray_->get_internal_span().template typed<T>(); } bool is_single_impl() const override { - return array_.is_single(); + return varray_->is_single(); } - T get_single_impl() const override + T get_internal_single_impl() const override { T value; - array_.get_single(&value); + varray_->get_internal_single(&value); return value; } }; +/* Used to convert an generic mutable virtual array into a typed one. */ +template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> { + protected: + GVMutableArray *varray_ = nullptr; + + public: + VMutableArray_For_GVMutableArray(GVMutableArray &varray) + : VMutableArray<T>(varray.size()), varray_(&varray) + { + BLI_assert(varray.type().template is<T>()); + } + + VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size) + { + } + + private: + T get_impl(const int64_t index) const override + { + T value; + varray_->get(index, &value); + return value; + } + + void set_impl(const int64_t index, T value) override + { + varray_->set_by_relocate(index, &value); + } + + bool is_span_impl() const override + { + return varray_->is_span(); + } + + Span<T> get_internal_span_impl() const override + { + return varray_->get_internal_span().template typed<T>(); + } + + bool is_single_impl() const override + { + return varray_->is_single(); + } + + T get_internal_single_impl() const override + { + T value; + varray_->get_internal_single(&value); + return value; + } +}; + +/* Used to convert any typed virtual mutable array into a generic one. */ +template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray { + protected: + VMutableArray<T> *varray_ = nullptr; + + public: + GVMutableArray_For_VMutableArray(VMutableArray<T> &varray) + : GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray) + { + } + + protected: + GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size) + { + } + + void get_impl(const int64_t index, void *r_value) const override + { + *(T *)r_value = varray_->get(index); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + new (r_value) T(varray_->get(index)); + } + + bool is_span_impl() const override + { + return varray_->is_span(); + } + + GSpan get_internal_span_impl() const override + { + Span<T> span = varray_->get_internal_span(); + return span; + } + + bool is_single_impl() const override + { + return varray_->is_single(); + } + + void get_internal_single_impl(void *r_value) const override + { + *(T *)r_value = varray_->get_internal_single(); + } + + void set_by_copy_impl(const int64_t index, const void *value) override + { + const T &value_ = *(const T *)value; + varray_->set(index, value_); + } + + void set_by_relocate_impl(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_->set(index, std::move(value_)); + value_.~T(); + } + + void set_by_move_impl(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_->set(index, std::move(value_)); + } + + void set_all_impl(const void *src) override + { + varray_->set_all(Span((T *)src, size_)); + } + + void materialize_impl(const IndexMask mask, void *dst) const override + { + varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override + { + varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + const void *try_get_internal_varray_impl() const override + { + return (const VArray<T> *)varray_; + } + + void *try_get_internal_mutable_varray_impl() override + { + return varray_; + } +}; + +/* A generic version of VArray_Span. */ +class GVArray_GSpan : public GSpan { + private: + const GVArray &varray_; + void *owned_data_ = nullptr; + + public: + GVArray_GSpan(const GVArray &varray); + ~GVArray_GSpan(); +}; + +/* A generic version of VMutableArray_Span. */ +class GVMutableArray_GSpan : public GMutableSpan { + private: + GVMutableArray &varray_; + void *owned_data_ = nullptr; + bool save_has_been_called_ = false; + bool show_not_saved_warning_ = true; + + public: + GVMutableArray_GSpan(GVMutableArray &varray, bool copy_values_to_span = true); + ~GVMutableArray_GSpan(); + + void save(); + void disable_not_applied_warning(); +}; + +/* Similar to GVArray_GSpan, but the resulting span is typed. */ +template<typename T> class GVArray_Span : public Span<T> { + private: + GVArray_GSpan varray_gspan_; + + public: + GVArray_Span(const GVArray &varray) : varray_gspan_(varray) + { + BLI_assert(varray.type().is<T>()); + this->data_ = (const T *)varray_gspan_.data(); + this->size_ = varray_gspan_.size(); + } +}; + +template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> { + private: + VArrayPtr<T> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + GVArray_For_OwnedVArray(VArrayPtr<T> varray) + : GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> { + private: + GVArrayPtr owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + VArray_For_OwnedGVArray(GVArrayPtr varray) + : VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> +class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> { + private: + VMutableArrayPtr<T> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr<T> varray) + : GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> +class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> { + private: + GVMutableArrayPtr owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + VMutableArray_For_OwnedGVMutableArray(GVMutableArrayPtr varray) + : VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give + * the compiler more opportunity to optimize the generic virtual array. */ +template<typename T, typename VArrayT> +class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> { + private: + VArrayT embedded_varray_; + + public: + template<typename... Args> + GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args) + : GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...) + { + this->varray_ = &embedded_varray_; + } +}; + +/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */ +template<typename T, typename VMutableArrayT> +class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray<T> { + private: + VMutableArrayT embedded_varray_; + + public: + template<typename... Args> + GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args) + : GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...) + { + this->varray_ = &embedded_varray_; + } +}; + +/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */ +template<typename Container, typename T = typename Container::value_type> +class GVArray_For_ArrayContainer + : public GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>> { + public: + GVArray_For_ArrayContainer(Container container) + : GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>>( + container.size(), std::move(container)) + { + } +}; + +/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */ +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +class GVArray_For_DerivedSpan + : public GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>> { + public: + GVArray_For_DerivedSpan(const Span<StructT> data) + : GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>>( + data.size(), data) + { + } +}; + +/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */ +template<typename StructT, + typename ElemT, + ElemT (*GetFunc)(const StructT &), + void (*SetFunc)(StructT &, ElemT)> +class GVMutableArray_For_DerivedSpan + : public GVMutableArray_For_EmbeddedVMutableArray< + ElemT, + VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> { + public: + GVMutableArray_For_DerivedSpan(const MutableSpan<StructT> data) + : GVMutableArray_For_EmbeddedVMutableArray< + ElemT, + VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(data.size(), data) + { + } +}; + +/* Same as VArray_For_Span, but for a generic virtual array. */ +template<typename T> +class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>> { + public: + GVArray_For_Span(const Span<T> data) + : GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data) + { + } +}; + +/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */ +template<typename T> +class GVMutableArray_For_MutableSpan + : public GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>> { + public: + GVMutableArray_For_MutableSpan(const MutableSpan<T> data) + : GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(data.size(), + data) + { + } +}; + +/** + * Utility class to create the "best" typed virtual array for a given generic virtual array. + * In most cases we don't just want to use VArray_For_GVArray, because it adds an additional + * indirection on element-access that can be avoided in many cases (e.g. when the virtual array is + * just a span or single value). + * + * This is not a virtual array itself, but is used to get a virtual array. + */ +template<typename T> class GVArray_Typed { + private: + const VArray<T> *varray_; + /* Of these optional virtual arrays, at most one is constructed at any time. */ + std::optional<VArray_For_Span<T>> varray_span_; + std::optional<VArray_For_Single<T>> varray_single_; + std::optional<VArray_For_GVArray<T>> varray_any_; + GVArrayPtr owned_gvarray_; + + public: + explicit GVArray_Typed(const GVArray &gvarray) + { + BLI_assert(gvarray.type().is<T>()); + if (gvarray.is_span()) { + const GSpan span = gvarray.get_internal_span(); + varray_span_.emplace(span.typed<T>()); + varray_ = &*varray_span_; + } + else if (gvarray.is_single()) { + T single_value; + gvarray.get_internal_single(&single_value); + varray_single_.emplace(single_value, gvarray.size()); + varray_ = &*varray_single_; + } + else if (const VArray<T> *internal_varray = gvarray.try_get_internal_varray<T>()) { + varray_ = internal_varray; + } + else { + varray_any_.emplace(gvarray); + varray_ = &*varray_any_; + } + } + + /* Same as the constructor above, but also takes ownership of the passed in virtual array. */ + explicit GVArray_Typed(GVArrayPtr gvarray) : GVArray_Typed(*gvarray) + { + owned_gvarray_ = std::move(gvarray); + } + + const VArray<T> &operator*() const + { + return *varray_; + } + + const VArray<T> *operator->() const + { + return varray_; + } + + /* Support implicit cast to the typed virtual array for convenience when `varray->typed<T>()` is + * used within an expression. */ + operator const VArray<T> &() const + { + return *varray_; + } + + T operator[](const int64_t index) const + { + return varray_->get(index); + } + + int64_t size() const + { + return varray_->size(); + } + + IndexRange index_range() const + { + return IndexRange(this->size()); + } +}; + +/* Same as GVArray_Typed, but for mutable virtual arrays. */ +template<typename T> class GVMutableArray_Typed { + private: + VMutableArray<T> *varray_; + std::optional<VMutableArray_For_MutableSpan<T>> varray_span_; + std::optional<VMutableArray_For_GVMutableArray<T>> varray_any_; + GVMutableArrayPtr owned_gvarray_; + + public: + explicit GVMutableArray_Typed(GVMutableArray &gvarray) + { + BLI_assert(gvarray.type().is<T>()); + if (gvarray.is_span()) { + const GMutableSpan span = gvarray.get_internal_span(); + varray_span_.emplace(span.typed<T>()); + varray_ = &*varray_span_; + } + else if (VMutableArray<T> *internal_varray = gvarray.try_get_internal_mutable_varray<T>()) { + varray_ = internal_varray; + } + else { + varray_any_.emplace(gvarray); + varray_ = &*varray_any_; + } + } + + explicit GVMutableArray_Typed(GVMutableArrayPtr gvarray) : GVMutableArray_Typed(*gvarray) + { + owned_gvarray_ = std::move(gvarray); + } + + VMutableArray<T> &operator*() + { + return *varray_; + } + + VMutableArray<T> *operator->() + { + return varray_; + } + + operator VMutableArray<T> &() + { + return *varray_; + } + + T operator[](const int64_t index) const + { + return varray_->get(index); + } + + int64_t size() const + { + return varray_->size(); + } +}; + } // namespace blender::fn diff --git a/source/blender/functions/FN_generic_virtual_vector_array.hh b/source/blender/functions/FN_generic_virtual_vector_array.hh index ef3f53b5c25..4155a55a801 100644 --- a/source/blender/functions/FN_generic_virtual_vector_array.hh +++ b/source/blender/functions/FN_generic_virtual_vector_array.hh @@ -100,13 +100,13 @@ class GVVectorArray { } }; -class GVArrayForGVVectorArrayIndex : public GVArray { +class GVArray_For_GVVectorArrayIndex : public GVArray { private: const GVVectorArray &vector_array_; const int64_t index_; public: - GVArrayForGVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index) + GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index) : GVArray(vector_array.type(), vector_array.get_vector_size(index)), vector_array_(vector_array), index_(index) @@ -118,12 +118,12 @@ class GVArrayForGVVectorArrayIndex : public GVArray { void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override; }; -class GVVectorArrayForSingleGVArray : public GVVectorArray { +class GVVectorArray_For_SingleGVArray : public GVVectorArray { private: const GVArray &array_; public: - GVVectorArrayForSingleGVArray(const GVArray &array, const int64_t size) + GVVectorArray_For_SingleGVArray(const GVArray &array, const int64_t size) : GVVectorArray(array.type(), size), array_(array) { } @@ -137,12 +137,12 @@ class GVVectorArrayForSingleGVArray : public GVVectorArray { bool is_single_vector_impl() const override; }; -class GVVectorArrayForSingleGSpan : public GVVectorArray { +class GVVectorArray_For_SingleGSpan : public GVVectorArray { private: const GSpan span_; public: - GVVectorArrayForSingleGSpan(const GSpan span, const int64_t size) + GVVectorArray_For_SingleGSpan(const GSpan span, const int64_t size) : GVVectorArray(span.type(), size), span_(span) { } @@ -156,12 +156,12 @@ class GVVectorArrayForSingleGSpan : public GVVectorArray { bool is_single_vector_impl() const override; }; -template<typename T> class VVectorArrayForGVVectorArray : public VVectorArray<T> { +template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray<T> { private: const GVVectorArray &vector_array_; public: - VVectorArrayForGVVectorArray(const GVVectorArray &vector_array) + VVectorArray_For_GVVectorArray(const GVVectorArray &vector_array) : VVectorArray<T>(vector_array.size()), vector_array_(vector_array) { } diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 72ebc0d9b94..e292d11def7 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -27,6 +27,7 @@ #include "BLI_resource_scope.hh" +#include "FN_generic_pointer.hh" #include "FN_generic_vector_array.hh" #include "FN_generic_virtual_vector_array.hh" #include "FN_multi_function_signature.hh" @@ -55,13 +56,19 @@ class MFParamsBuilder { template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct<GVArrayForSingleValueRef>( + this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>( __func__, CPPType::get<T>(), min_array_size_, value), expected_name); } void add_readonly_single_input(const GSpan span, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct<GVArrayForGSpan>(__func__, span), + this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(__func__, span), + expected_name); + } + void add_readonly_single_input(GPointer value, StringRef expected_name = "") + { + this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>( + __func__, *value.type(), min_array_size_, value.get()), expected_name); } void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "") @@ -74,7 +81,7 @@ class MFParamsBuilder { void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "") { this->add_readonly_vector_input( - scope_.construct<GVVectorArrayForGVectorArray>(__func__, vector_array), expected_name); + scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name); } void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") { @@ -177,7 +184,7 @@ class MFParams { template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "") { const GVArray &array = this->readonly_single_input(param_index, name); - return builder_->scope_.construct<VArrayForGVArray<T>>(__func__, array); + return builder_->scope_.construct<VArray_For_GVArray<T>>(__func__, array); } const GVArray &readonly_single_input(int param_index, StringRef name = "") { @@ -202,7 +209,7 @@ class MFParams { const VVectorArray<T> &readonly_vector_input(int param_index, StringRef name = "") { const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name); - return builder_->scope_.construct<VVectorArrayForGVVectorArray<T>>(__func__, vector_array); + return builder_->scope_.construct<VVectorArray_For_GVVectorArray<T>>(__func__, vector_array); } const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "") { diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc index b3c5517cc43..3335b07e559 100644 --- a/source/blender/functions/intern/generic_vector_array.cc +++ b/source/blender/functions/intern/generic_vector_array.cc @@ -60,21 +60,21 @@ void GVectorArray::extend(const int64_t index, const GVArray &values) void GVectorArray::extend(const int64_t index, const GSpan values) { - GVArrayForGSpan varray{values}; + GVArray_For_GSpan varray{values}; this->extend(index, varray); } void GVectorArray::extend(IndexMask mask, const GVVectorArray &values) { for (const int i : mask) { - GVArrayForGVVectorArrayIndex array{values, i}; + GVArray_For_GVVectorArrayIndex array{values, i}; this->extend(i, array); } } void GVectorArray::extend(IndexMask mask, const GVectorArray &values) { - GVVectorArrayForGVectorArray virtual_values{values}; + GVVectorArray_For_GVectorArray virtual_values{values}; this->extend(mask, virtual_values); } diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc index 9380eb257b2..87dae06ccdc 100644 --- a/source/blender/functions/intern/generic_virtual_array.cc +++ b/source/blender/functions/intern/generic_virtual_array.cc @@ -18,8 +18,72 @@ namespace blender::fn { +/* -------------------------------------------------------------------- + * GVArray_For_ShallowCopy. + */ + +class GVArray_For_ShallowCopy : public GVArray { + private: + const GVArray &varray_; + + public: + GVArray_For_ShallowCopy(const GVArray &varray) + : GVArray(varray.type(), varray.size()), varray_(varray) + { + } + + private: + void get_impl(const int64_t index, void *r_value) const override + { + varray_.get(index, r_value); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + varray_.get_to_uninitialized(index, r_value); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override + { + varray_.materialize_to_uninitialized(mask, dst); + } +}; + +/* -------------------------------------------------------------------- + * GVArray. + */ + +void GVArray::materialize(void *dst) const +{ + this->materialize(IndexMask(size_), dst); +} + +void GVArray::materialize(const IndexMask mask, void *dst) const +{ + this->materialize_impl(mask, dst); +} + +void GVArray::materialize_impl(const IndexMask mask, void *dst) const +{ + for (const int64_t i : mask) { + void *elem_dst = POINTER_OFFSET(dst, type_->size() * i); + this->get(i, elem_dst); + } +} + +void GVArray::materialize_to_uninitialized(void *dst) const +{ + this->materialize_to_uninitialized(IndexMask(size_), dst); +} + void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const { + BLI_assert(mask.min_array_size() <= size_); + this->materialize_to_uninitialized_impl(mask, dst); +} + +void GVArray::materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const +{ for (const int64_t i : mask) { void *elem_dst = POINTER_OFFSET(dst, type_->size() * i); this->get_to_uninitialized(i, elem_dst); @@ -37,7 +101,7 @@ bool GVArray::is_span_impl() const return false; } -GSpan GVArray::get_span_impl() const +GSpan GVArray::get_internal_span_impl() const { BLI_assert(false); return GSpan(*type_); @@ -48,60 +112,279 @@ bool GVArray::is_single_impl() const return false; } -void GVArray::get_single_impl(void *UNUSED(r_value)) const +void GVArray::get_internal_single_impl(void *UNUSED(r_value)) const { BLI_assert(false); } -void GVArrayForGSpan::get_impl(const int64_t index, void *r_value) const +const void *GVArray::try_get_internal_varray_impl() const +{ + return nullptr; +} + +/** + * Creates a new `std::unique_ptr<GVArray>` based on this `GVArray`. + * The lifetime of the returned virtual array must not be longer than the lifetime of this virtual + * array. + */ +GVArrayPtr GVArray::shallow_copy() const +{ + if (this->is_span()) { + return std::make_unique<GVArray_For_GSpan>(this->get_internal_span()); + } + if (this->is_single()) { + BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer); + this->get_internal_single(buffer); + std::unique_ptr new_varray = std::make_unique<GVArray_For_SingleValue>(*type_, size_, buffer); + type_->destruct(buffer); + return new_varray; + } + return std::make_unique<GVArray_For_ShallowCopy>(*this); +} + +/* -------------------------------------------------------------------- + * GVMutableArray. + */ + +void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value) +{ + BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer); + type_->copy_to_uninitialized(value, buffer); + this->set_by_move_impl(index, buffer); + type_->destruct(buffer); +} + +void GVMutableArray::set_by_relocate_impl(const int64_t index, void *value) +{ + this->set_by_move_impl(index, value); + type_->destruct(value); +} + +void GVMutableArray::set_all_impl(const void *src) +{ + if (this->is_span()) { + const GMutableSpan span = this->get_internal_span(); + type_->copy_to_initialized_n(src, span.data(), size_); + } + else { + for (int64_t i : IndexRange(size_)) { + this->set_by_copy(i, POINTER_OFFSET(src, type_->size() * i)); + } + } +} + +void *GVMutableArray::try_get_internal_mutable_varray_impl() +{ + return nullptr; +} + +void GVMutableArray::fill(const void *value) +{ + if (this->is_span()) { + const GMutableSpan span = this->get_internal_span(); + type_->fill_initialized(value, span.data(), size_); + } + else { + for (int64_t i : IndexRange(size_)) { + this->set_by_copy(i, value); + } + } +} + +/* -------------------------------------------------------------------- + * GVArray_For_GSpan. + */ + +void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const { type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value); } -void GVArrayForGSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const +void GVArray_For_GSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const { type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value); } -bool GVArrayForGSpan::is_span_impl() const +bool GVArray_For_GSpan::is_span_impl() const { return true; } -GSpan GVArrayForGSpan::get_span_impl() const +GSpan GVArray_For_GSpan::get_internal_span_impl() const { return GSpan(*type_, data_, size_); } -void GVArrayForSingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const +/* -------------------------------------------------------------------- + * GVMutableArray_For_GMutableSpan. + */ + +void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const +{ + type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value); +} + +void GVMutableArray_For_GMutableSpan::get_to_uninitialized_impl(const int64_t index, + void *r_value) const +{ + type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value); +} + +void GVMutableArray_For_GMutableSpan::set_by_copy_impl(const int64_t index, const void *value) +{ + type_->copy_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +void GVMutableArray_For_GMutableSpan::set_by_move_impl(const int64_t index, void *value) +{ + type_->move_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +void GVMutableArray_For_GMutableSpan::set_by_relocate_impl(const int64_t index, void *value) +{ + type_->relocate_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +bool GVMutableArray_For_GMutableSpan::is_span_impl() const +{ + return true; +} + +GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const +{ + return GSpan(*type_, data_, size_); +} + +/* -------------------------------------------------------------------- + * GVArray_For_SingleValueRef. + */ + +void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const { type_->copy_to_initialized(value_, r_value); } -void GVArrayForSingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index), - void *r_value) const +void GVArray_For_SingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index), + void *r_value) const { type_->copy_to_uninitialized(value_, r_value); } -bool GVArrayForSingleValueRef::is_span_impl() const +bool GVArray_For_SingleValueRef::is_span_impl() const { return size_ == 1; } -GSpan GVArrayForSingleValueRef::get_span_impl() const +GSpan GVArray_For_SingleValueRef::get_internal_span_impl() const { return GSpan{*type_, value_, 1}; } -bool GVArrayForSingleValueRef::is_single_impl() const +bool GVArray_For_SingleValueRef::is_single_impl() const { return true; } -void GVArrayForSingleValueRef::get_single_impl(void *r_value) const +void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const { type_->copy_to_initialized(value_, r_value); } +/* -------------------------------------------------------------------- + * GVArray_For_SingleValue. + */ + +GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type, + const int64_t size, + const void *value) + : GVArray_For_SingleValueRef(type, size) +{ + value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + type.copy_to_uninitialized(value, (void *)value_); +} + +GVArray_For_SingleValue::~GVArray_For_SingleValue() +{ + type_->destruct((void *)value_); + MEM_freeN((void *)value_); +} + +/* -------------------------------------------------------------------- + * GVArray_GSpan. + */ + +GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray) +{ + size_ = varray_.size(); + if (varray_.is_span()) { + data_ = varray_.get_internal_span().data(); + } + else { + owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__); + varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_); + data_ = owned_data_; + } +} + +GVArray_GSpan::~GVArray_GSpan() +{ + if (owned_data_ != nullptr) { + type_->destruct_n(owned_data_, size_); + MEM_freeN(owned_data_); + } +} + +/* -------------------------------------------------------------------- + * GVMutableArray_GSpan. + */ + +GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span) + : GMutableSpan(varray.type()), varray_(varray) +{ + size_ = varray_.size(); + if (varray_.is_span()) { + data_ = varray_.get_internal_span().data(); + } + else { + owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__); + if (copy_values_to_span) { + varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_); + } + else { + type_->construct_default_n(owned_data_, size_); + } + data_ = owned_data_; + } +} + +GVMutableArray_GSpan::~GVMutableArray_GSpan() +{ + if (show_not_saved_warning_) { + if (!save_has_been_called_) { + std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n"; + } + } + if (owned_data_ != nullptr) { + type_->destruct_n(owned_data_, size_); + MEM_freeN(owned_data_); + } +} + +void GVMutableArray_GSpan::save() +{ + save_has_been_called_ = true; + if (data_ != owned_data_) { + return; + } + const int64_t element_size = type_->size(); + for (int64_t i : IndexRange(size_)) { + varray_.set_by_copy(i, POINTER_OFFSET(owned_data_, element_size * i)); + } +} + +void GVMutableArray_GSpan::disable_not_applied_warning() +{ + show_not_saved_warning_ = false; +} + } // namespace blender::fn diff --git a/source/blender/functions/intern/generic_virtual_vector_array.cc b/source/blender/functions/intern/generic_virtual_vector_array.cc index f6504cee41e..aa3d90883c6 100644 --- a/source/blender/functions/intern/generic_virtual_vector_array.cc +++ b/source/blender/functions/intern/generic_virtual_vector_array.cc @@ -18,48 +18,48 @@ namespace blender::fn { -void GVArrayForGVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const +void GVArray_For_GVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const { vector_array_.get_vector_element(index_, index_in_vector, r_value); } -void GVArrayForGVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector, - void *r_value) const +void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector, + void *r_value) const { type_->construct_default(r_value); vector_array_.get_vector_element(index_, index_in_vector, r_value); } -int64_t GVVectorArrayForSingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const +int64_t GVVectorArray_For_SingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const { return array_.size(); } -void GVVectorArrayForSingleGVArray::get_vector_element_impl(const int64_t UNUSED(index), - const int64_t index_in_vector, - void *r_value) const +void GVVectorArray_For_SingleGVArray::get_vector_element_impl(const int64_t UNUSED(index), + const int64_t index_in_vector, + void *r_value) const { array_.get(index_in_vector, r_value); } -bool GVVectorArrayForSingleGVArray::is_single_vector_impl() const +bool GVVectorArray_For_SingleGVArray::is_single_vector_impl() const { return true; } -int64_t GVVectorArrayForSingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const +int64_t GVVectorArray_For_SingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const { return span_.size(); } -void GVVectorArrayForSingleGSpan::get_vector_element_impl(const int64_t UNUSED(index), - const int64_t index_in_vector, - void *r_value) const +void GVVectorArray_For_SingleGSpan::get_vector_element_impl(const int64_t UNUSED(index), + const int64_t index_in_vector, + void *r_value) const { type_->copy_to_initialized(span_[index_in_vector], r_value); } -bool GVVectorArrayForSingleGSpan::is_single_vector_impl() const +bool GVVectorArray_For_SingleGSpan::is_single_vector_impl() const { return true; } diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index 86ac4f6a179..9a0cb0c35ce 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -974,11 +974,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); if (value->is_single_allocated) { - return scope.construct<GVArrayForSingleValueRef>( + return scope.construct<GVArray_For_SingleValueRef>( __func__, value->span.type(), min_array_size_, value->span.data()); } - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } if (any_value->type == ValueType::InputSingle) { InputSingleValue *value = static_cast<InputSingleValue *>(any_value); @@ -987,11 +987,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS if (any_value->type == ValueType::OutputSingle) { OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); BLI_assert(value->is_computed); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } BLI_assert(false); - return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>()); + return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); } const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket, @@ -1004,7 +1004,7 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); BLI_assert(value->span.size() == 1); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } if (any_value->type == ValueType::InputSingle) { InputSingleValue *value = static_cast<InputSingleValue *>(any_value); @@ -1015,11 +1015,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); BLI_assert(value->is_computed); BLI_assert(value->span.size() == 1); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } BLI_assert(false); - return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>()); + return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); } const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( @@ -1033,10 +1033,10 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); if (value->vector_array->size() == 1) { GSpan span = (*value->vector_array)[0]; - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_); } - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } if (any_value->type == ValueType::InputVector) { InputVectorValue *value = static_cast<InputVectorValue *>(any_value); @@ -1044,11 +1044,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( } if (any_value->type == ValueType::OutputVector) { OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } BLI_assert(false); - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); } const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( @@ -1061,7 +1061,7 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( if (any_value->type == ValueType::OwnVector) { OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } if (any_value->type == ValueType::InputVector) { InputVectorValue *value = static_cast<InputVectorValue *>(any_value); @@ -1071,11 +1071,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( if (any_value->type == ValueType::OutputVector) { OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } BLI_assert(false); - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); } /** \} */ diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc index 51e116b5983..7b9738e5ca4 100644 --- a/source/blender/functions/tests/FN_multi_function_network_test.cc +++ b/source/blender/functions/tests/FN_multi_function_network_test.cc @@ -223,7 +223,7 @@ TEST(multi_function_network, Test2) Array<int> output_value_2(5, -1); MFParamsBuilder params(network_fn, 5); - GVVectorArrayForSingleGSpan inputs_1{input_value_1.as_span(), 5}; + GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5}; params.add_readonly_vector_input(inputs_1); params.add_readonly_single_input(&input_value_2); params.add_vector_output(output_value_1); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index a156fca5b7b..94285b5032e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -203,6 +203,20 @@ void gpencil_modifier_curve_panel_draw(const bContext *UNUSED(C), Panel *panel) uiTemplateCurveMapping(layout, ptr, "curve", 0, false, false, false, false); } +void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel) +{ + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayout *layout = panel->layout; + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "object", 0, NULL, ICON_CUBE); + uiLayout *sub = uiLayoutColumn(layout, true); + uiItemR(sub, ptr, "fading_start", 0, NULL, ICON_NONE); + uiItemR(sub, ptr, "fading_end", 0, IFACE_("End"), ICON_NONE); + uiItemR(layout, ptr, "fading_end_factor", 0, NULL, ICON_NONE); +} + /** * Draw modifier error message. */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h index 782b36d47ed..75907aaa781 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h @@ -37,6 +37,8 @@ void gpencil_modifier_masking_panel_draw(Panel *panel, bool use_material, bool u void gpencil_modifier_curve_header_draw(const bContext *C, Panel *panel); void gpencil_modifier_curve_panel_draw(const bContext *C, Panel *panel); +void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel); + void gpencil_modifier_panel_end(struct uiLayout *layout, PointerRNA *ptr); struct PointerRNA *gpencil_modifier_panel_get_property_pointers(struct Panel *panel, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index d7fc08e38f3..d9f0fc9bddd 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -455,8 +455,10 @@ static void generate_geometry( /* Compute start and end frames for the animation effect * By default, the upper bound is given by the "maximum length" setting */ - float start_frame = gpf->framenum + mmd->start_delay; - float end_frame = start_frame + mmd->length; + float start_frame = is_percentage ? gpf->framenum : gpf->framenum + mmd->start_delay; + /* When use percentage don't need a limit in the upper bound, so use a maximum value for the last + * frame. */ + float end_frame = is_percentage ? start_frame + 9999 : start_frame + mmd->length; if (gpf->next) { /* Use the next frame or upper bound as end frame, whichever is lower/closer */ @@ -547,6 +549,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); int mode = RNA_enum_get(ptr, "mode"); + const bool use_percentage = RNA_boolean_get(ptr, "use_percentage"); uiLayoutSetPropSep(layout, true); @@ -558,8 +561,12 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemS(layout); uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "start_delay", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "length", 0, IFACE_("Frames"), ICON_NONE); + row = uiLayoutRow(layout, true); + uiLayoutSetActive(row, !use_percentage); + uiItemR(row, ptr, "start_delay", 0, NULL, ICON_NONE); + row = uiLayoutRow(layout, true); + uiLayoutSetActive(row, !use_percentage); + uiItemR(row, ptr, "length", 0, IFACE_("Frames"), ICON_NONE); uiItemS(layout); @@ -567,7 +574,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropDecorate(row, false); uiItemR(row, ptr, "use_percentage", 0, "", ICON_NONE); sub = uiLayoutRow(row, true); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_percentage")); + uiLayoutSetActive(sub, use_percentage); uiItemR(sub, ptr, "percentage_factor", 0, "", ICON_NONE); uiItemDecoratorR(row, ptr, "percentage_factor", 0); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 080a3caa32f..a2f35c8e575 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -335,7 +335,7 @@ static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel) if (use_multiple_levels) { uiLayout *col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "level_start", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "level_end", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "level_end", 0, IFACE_("End"), ICON_NONE); } else { uiItemR(layout, ptr, "level_start", 0, IFACE_("Level"), ICON_NONE); @@ -367,7 +367,7 @@ static void transparency_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *row = uiLayoutRow(layout, true); uiLayoutSetPropDecorate(row, false); - uiLayout *sub = uiLayoutRow(row, true); + uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks")); char text[2] = "0"; PropertyRNA *prop = RNA_struct_find_property(ptr, "use_transparency_mask"); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c index bffb324f07f..cd29a006aae 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -26,7 +26,11 @@ #include "BLI_listbase.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + +#include "BLI_hash.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" @@ -71,7 +75,7 @@ static void deformStroke(GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), Object *ob, bGPDlayer *gpl, - bGPDframe *UNUSED(gpf), + bGPDframe *gpf, bGPDstroke *gps) { OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md; @@ -94,6 +98,46 @@ static void deformStroke(GpencilModifierData *md, mmd->flag & GP_OFFSET_INVERT_MATERIAL)) { return; } + + int seed = mmd->seed; + /* Make sure different modifiers get different seeds. */ + seed += BLI_hash_string(ob->id.name + 2); + seed += BLI_hash_string(md->name); + + float rand[3][3]; + float rand_offset = BLI_hash_int_01(seed); + + /* Get stroke index for random offset. */ + int rnd_index = BLI_findindex(&gpf->strokes, gps); + for (int j = 0; j < 3; j++) { + const uint primes[3] = {2, 3, 7}; + double offset[3] = {0.0f, 0.0f, 0.0f}; + double r[3]; + /* To ensure a nice distribution, we use halton sequence and offset using the seed. */ + BLI_halton_3d(primes, offset, rnd_index, r); + + if ((mmd->flag & GP_OFFSET_UNIFORM_RANDOM_SCALE) && j == 2) { + float rand_value; + rand_value = fmodf(r[0] * 2.0f - 1.0f + rand_offset, 1.0f); + rand_value = fmodf(sin(rand_value * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f); + copy_v3_fl(rand[j], rand_value); + } + else { + for (int i = 0; i < 3; i++) { + rand[j][i] = fmodf(r[i] * 2.0f - 1.0f + rand_offset, 1.0f); + rand[j][i] = fmodf(sin(rand[j][i] * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f); + } + } + } + /* Calculate Random matrix. */ + float mat_rnd[4][4]; + float rnd_loc[3], rnd_rot[3]; + float rnd_scale[3] = {1.0f, 1.0f, 1.0f}; + mul_v3_v3v3(rnd_loc, mmd->rnd_offset, rand[0]); + mul_v3_v3v3(rnd_rot, mmd->rnd_rot, rand[1]); + madd_v3_v3v3(rnd_scale, mmd->rnd_scale, rand[2]); + loc_eul_size_to_mat4(mat_rnd, rnd_loc, rnd_rot, rnd_scale); + bGPdata *gpd = ob->data; for (int i = 0; i < gps->totpoints; i++) { @@ -106,6 +150,9 @@ static void deformStroke(GpencilModifierData *md, if (weight < 0.0f) { continue; } + /* Apply randomness matrix. */ + mul_m4_v3(mat_rnd, &pt->x); + /* Calculate matrix. */ mul_v3_v3fl(loc, mmd->loc, weight); mul_v3_v3fl(rot, mmd->rot, weight); @@ -161,6 +208,21 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_panel_end(layout, ptr); } +static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "random_offset", 0, IFACE_("Offset"), ICON_NONE); + uiItemR(layout, ptr, "random_rotation", 0, IFACE_("Rotation"), ICON_NONE); + uiItemR(layout, ptr, "random_scale", 0, IFACE_("Scale"), ICON_NONE); + uiItemR(layout, ptr, "use_uniform_random_scale", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "seed", 0, NULL, ICON_NONE); +} + static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) { gpencil_modifier_masking_panel_draw(panel, true, true); @@ -171,6 +233,8 @@ static void panelRegister(ARegionType *region_type) PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Offset, panel_draw); gpencil_modifier_subpanel_register( + region_type, "randomize", "Randomize", NULL, random_panel_draw, panel_type); + gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index c193fc49362..ea37558fa7f 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -47,6 +47,8 @@ #include "BKE_screen.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "UI_interface.h" #include "UI_resources.h" @@ -84,6 +86,39 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity); } +static float give_opacity_fading_factor(OpacityGpencilModifierData *mmd, + Object *ob_this, + float *pos, + bool apply_obmat) +{ + float factor_depth = 1.0f; + + if (((mmd->flag & GP_OPACITY_FADING) == 0) || ((mmd->object) == NULL)) { + return factor_depth; + } + + float gvert[3]; + if (apply_obmat) { + mul_v3_m4v3(gvert, ob_this->obmat, pos); + } + float dist = len_v3v3(mmd->object->obmat[3], gvert); + float fading_max = MAX2(mmd->fading_start, mmd->fading_end); + float fading_min = MIN2(mmd->fading_start, mmd->fading_end); + + /* Better with ratiof() function from line art. */ + if (dist > fading_max) { + factor_depth = 0.0f; + } + else if (dist <= fading_max && dist > fading_min) { + factor_depth = (fading_max - dist) / (fading_max - fading_min); + } + else { + factor_depth = 1.0f; + } + + return factor_depth; +} + /* opacity strokes */ static void deformStroke(GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), @@ -138,6 +173,9 @@ static void deformStroke(GpencilModifierData *md, factor_curve *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value); } + float factor_depth = give_opacity_fading_factor(mmd, ob, &pt->x, true); + factor_curve = interpf(factor_curve, mmd->fading_end_factor, factor_depth); + if (def_nr < 0) { if (mmd->flag & GP_OPACITY_NORMALIZE) { pt->strength = factor_curve; @@ -167,6 +205,10 @@ static void deformStroke(GpencilModifierData *md, /* Fill using opacity factor. */ if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { gps->fill_opacity_fac = mmd->factor; + + float factor_depth = give_opacity_fading_factor(mmd, ob, ob->obmat[3], true); + gps->fill_opacity_fac = interpf(mmd->factor, mmd->fading_end_factor, factor_depth); + CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f); } } @@ -201,6 +243,18 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); + walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP); +} + +static void updateDepsgraph(GpencilModifierData *md, + const ModifierUpdateDepsgraphContext *ctx, + const int UNUSED(mode)) +{ + OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; + if (mmd->object != NULL) { + DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier"); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -228,6 +282,20 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_panel_end(layout, ptr); } +static void fading_header_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE); +} + +static void fading_panel_draw(const bContext *C, Panel *panel) +{ + gpencil_modifier_fading_draw(C, panel); +} + static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) { PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); @@ -266,6 +334,9 @@ static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Opacity, panel_draw); + + gpencil_modifier_subpanel_register( + region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type); PanelType *mask_panel_type = gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); gpencil_modifier_subpanel_register( @@ -289,7 +360,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Opacity = { /* initData */ initData, /* freeData */ freeData, /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, + /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ NULL, /* foreachIDLink */ foreachIDLink, /* foreachTexLink */ NULL, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index a13f8d64755..512e3af063a 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -43,6 +43,8 @@ #include "BKE_screen.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "UI_interface.h" #include "UI_resources.h" @@ -128,6 +130,30 @@ static void deformStroke(GpencilModifierData *md, } float curvef = 1.0f; + + float factor_depth = 1.0f; + + if (mmd->flag & GP_THICK_FADING) { + if (mmd->object) { + float gvert[3]; + mul_v3_m4v3(gvert, ob->obmat, &pt->x); + float dist = len_v3v3(mmd->object->obmat[3], gvert); + float fading_max = MAX2(mmd->fading_start, mmd->fading_end); + float fading_min = MIN2(mmd->fading_start, mmd->fading_end); + + /* Better with ratiof() function from line art. */ + if (dist > fading_max) { + factor_depth = 0.0f; + } + else if (dist <= fading_max && dist > fading_min) { + factor_depth = (fading_max - dist) / (fading_max - fading_min); + } + else { + factor_depth = 1.0f; + } + } + } + if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) { /* Normalize value to evaluate curve. */ float value = (float)i / (gps->totpoints - 1); @@ -144,6 +170,9 @@ static void deformStroke(GpencilModifierData *md, weight *= curvef; } + float fac_begin = mmd->flag & GP_THICK_NORMALIZE ? 1 : mmd->thickness_fac; + target *= interpf(fac_begin, mmd->fading_end_factor, factor_depth); + pt->pressure = interpf(target, pt->pressure, weight); CLAMP_MIN(pt->pressure, 0.0f); @@ -171,6 +200,32 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md; walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); + walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP); +} + +static void updateDepsgraph(GpencilModifierData *md, + const ModifierUpdateDepsgraphContext *ctx, + const int UNUSED(mode)) +{ + ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md; + if (mmd->object != NULL) { + DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier"); +} + +static void fading_header_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE); +} + +static void fading_panel_draw(const bContext *C, Panel *panel) +{ + gpencil_modifier_fading_draw(C, panel); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -202,6 +257,8 @@ static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Thick, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type); PanelType *mask_panel_type = gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); gpencil_modifier_subpanel_register(region_type, @@ -229,7 +286,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Thick = { /* initData */ initData, /* freeData */ freeData, /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, + /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ NULL, /* foreachIDLink */ foreachIDLink, /* foreachTexLink */ NULL, diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 56cd7fa1456..44ff0616fe9 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -322,9 +322,11 @@ typedef enum eLineartTriangleFlags { LRT_TRIANGLE_NO_INTERSECTION = (1 << 4), } eLineartTriangleFlags; -/** Controls how many edges a worker thread is processing at one request. +/** + * Controls how many edges a worker thread is processing at one request. * There's no significant performance impact on choosing different values. - * Don't make it too small so that the worker thread won't request too many times. */ + * Don't make it too small so that the worker thread won't request too many times. + */ #define LRT_THREAD_EDGE_COUNT 1000 typedef struct LineartRenderTaskInfo { diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index b95053a3715..f54ecece659 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -37,6 +37,15 @@ int GPU_max_textures(void); int GPU_max_textures_vert(void); int GPU_max_textures_geom(void); int GPU_max_textures_frag(void); +int GPU_max_uniforms_vert(void); +int GPU_max_uniforms_frag(void); +int GPU_max_batch_indices(void); +int GPU_max_batch_vertices(void); +int GPU_max_vertex_attribs(void); +int GPU_max_varying_floats(void); + +int GPU_extensions_len(void); +const char *GPU_extension_get(int i); int GPU_texture_size_with_limit(int res, bool limit_gl_texture_size); diff --git a/source/blender/gpu/GPU_common.h b/source/blender/gpu/GPU_common.h index 1be74701176..c00bf581e8c 100644 --- a/source/blender/gpu/GPU_common.h +++ b/source/blender/gpu/GPU_common.h @@ -24,6 +24,7 @@ #pragma once #define PROGRAM_NO_OPTI 0 +//#define GPU_NO_USE_PY_REFERENCES #if defined(NDEBUG) # define TRUST_NO_ONE 0 diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index af94c1fb0e6..6482d9a9d3e 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -209,6 +209,11 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, void (*callback)(void *userData, int level), void *userData); +#ifndef GPU_NO_USE_PY_REFERENCES +void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb); +void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref); +#endif + void GPU_framebuffer_push(GPUFrameBuffer *fb); GPUFrameBuffer *GPU_framebuffer_pop(void); uint GPU_framebuffer_stack_level_get(void); diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h index c457b829bf7..fa7d5d7fba8 100644 --- a/source/blender/gpu/GPU_platform.h +++ b/source/blender/gpu/GPU_platform.h @@ -68,6 +68,9 @@ extern "C" { bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver); eGPUSupportLevel GPU_platform_support_level(void); +const char *GPU_platform_vendor(void); +const char *GPU_platform_renderer(void); +const char *GPU_platform_version(void); const char *GPU_platform_support_level_key(void); const char *GPU_platform_gpu_name(void); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index d9a01663de0..f980c8fdcd7 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -269,6 +269,12 @@ bool GPU_texture_cube(const GPUTexture *tex); bool GPU_texture_depth(const GPUTexture *tex); bool GPU_texture_stencil(const GPUTexture *tex); bool GPU_texture_integer(const GPUTexture *tex); + +#ifndef GPU_NO_USE_PY_REFERENCES +void **GPU_texture_py_reference_get(GPUTexture *tex); +void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref); +#endif + int GPU_texture_opengl_bindcode(const GPUTexture *tex); void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size); diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index 6d9182dcf17..d8764502800 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -82,6 +82,46 @@ int GPU_max_textures(void) return GCaps.max_textures; } +int GPU_max_uniforms_vert(void) +{ + return GCaps.max_uniforms_vert; +} + +int GPU_max_uniforms_frag(void) +{ + return GCaps.max_uniforms_frag; +} + +int GPU_max_batch_indices(void) +{ + return GCaps.max_batch_indices; +} + +int GPU_max_batch_vertices(void) +{ + return GCaps.max_batch_vertices; +} + +int GPU_max_vertex_attribs(void) +{ + return GCaps.max_vertex_attribs; +} + +int GPU_max_varying_floats(void) +{ + return GCaps.max_varying_floats; +} + +int GPU_extensions_len(void) +{ + return GCaps.extensions_len; +} + +const char *GPU_extension_get(int i) +{ + return GCaps.extension_get ? GCaps.extension_get(i) : "\0"; +} + bool GPU_mip_render_workaround(void) { return GCaps.mip_render_workaround; diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 2b3292749f8..7c1d4590ce8 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -41,6 +41,15 @@ struct GPUCapabilities { int max_textures_vert = 0; int max_textures_geom = 0; int max_textures_frag = 0; + int max_uniforms_vert = 0; + int max_uniforms_frag = 0; + int max_batch_indices = 0; + int max_batch_vertices = 0; + int max_vertex_attribs = 0; + int max_varying_floats = 0; + int extensions_len = 0; + const char *(*extension_get)(int); + bool mem_stats_support = false; bool shader_image_load_store_support = false; /* OpenGL related workarounds. */ diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index a9d32dcf297..b5a437b46f7 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -24,7 +24,7 @@ * Use these instead of glGenBuffers & its friends * - alloc must be called from a thread that is bound * to the context that will be used for drawing with - * this vao. + * this VAO. * - free can be called from any thread */ diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc index 63e7024b74b..2d9fb7822ae 100644 --- a/source/blender/gpu/intern/gpu_debug.cc +++ b/source/blender/gpu/intern/gpu_debug.cc @@ -86,8 +86,8 @@ bool GPU_debug_group_match(const char *ref) if (ctx == nullptr) { return false; } - DebugStack &stack = ctx->debug_stack; - for (StringRef &name : stack) { + const DebugStack &stack = ctx->debug_stack; + for (const StringRef &name : stack) { if (name == ref) { return true; } diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 1e3cf479462..1293cc0953d 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -71,6 +71,12 @@ FrameBuffer::~FrameBuffer() reinterpret_cast<Texture *>(attachment.tex)->detach_from(this); } } + +#ifndef GPU_NO_USE_PY_REFERENCES + if (this->py_ref) { + *this->py_ref = nullptr; + } +#endif } /** \} */ @@ -473,6 +479,19 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb, unwrap(gpu_fb)->recursive_downsample(max_lvl, callback, userData); } +#ifndef GPU_NO_USE_PY_REFERENCES +void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb) +{ + return unwrap(gpu_fb)->py_ref; +} + +void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref) +{ + BLI_assert(py_ref == nullptr || unwrap(gpu_fb)->py_ref == nullptr); + unwrap(gpu_fb)->py_ref = py_ref; +} +#endif + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh index d63d72cf4f7..2b00c239af4 100644 --- a/source/blender/gpu/intern/gpu_framebuffer_private.hh +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -100,6 +100,20 @@ class FrameBuffer { bool scissor_test_ = false; bool dirty_state_ = true; +#ifndef GPU_NO_USE_PY_REFERENCES + public: + /** + * Reference of a pointer that needs to be cleaned when deallocating the frame-buffer. + * Points to #BPyGPUFrameBuffer.fb + */ + void **py_ref = nullptr; +#endif + + public: + /* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer. + * Points to #BPyGPUFrameBuffer::fb */ + void **ref = nullptr; + public: FrameBuffer(const char *name); virtual ~FrameBuffer(); diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 2ae50d913da..569b51a407a 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -543,7 +543,7 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, &precalc->dims.zmax); if (isinf(precalc->dims.zmax)) { /* We cannot retrieve the actual value of the clip_end. - * Use `FLT_MAX` to avoid nans. */ + * Use `FLT_MAX` to avoid NAN's. */ precalc->dims.zmax = FLT_MAX; } for (int i = 0; i < 4; i++) { diff --git a/source/blender/gpu/intern/gpu_platform.cc b/source/blender/gpu/intern/gpu_platform.cc index 6b9878f2ba4..49dde473300 100644 --- a/source/blender/gpu/intern/gpu_platform.cc +++ b/source/blender/gpu/intern/gpu_platform.cc @@ -41,10 +41,10 @@ namespace blender::gpu { GPUPlatformGlobal GPG; -void GPUPlatformGlobal::create_key(eGPUSupportLevel support_level, - const char *vendor, - const char *renderer, - const char *version) +static char *create_key(eGPUSupportLevel support_level, + const char *vendor, + const char *renderer, + const char *version) { DynStr *ds = BLI_dynstr_new(); BLI_dynstr_appendf(ds, "{%s/%s/%s}=", vendor, renderer, version); @@ -58,29 +58,56 @@ void GPUPlatformGlobal::create_key(eGPUSupportLevel support_level, BLI_dynstr_append(ds, "UNSUPPORTED"); } - support_key = BLI_dynstr_get_cstring(ds); + char *support_key = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); BLI_str_replace_char(support_key, '\n', ' '); BLI_str_replace_char(support_key, '\r', ' '); + return support_key; } -void GPUPlatformGlobal::create_gpu_name(const char *vendor, - const char *renderer, - const char *version) +static char *create_gpu_name(const char *vendor, const char *renderer, const char *version) { DynStr *ds = BLI_dynstr_new(); BLI_dynstr_appendf(ds, "%s %s %s", vendor, renderer, version); - gpu_name = BLI_dynstr_get_cstring(ds); + char *gpu_name = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); BLI_str_replace_char(gpu_name, '\n', ' '); BLI_str_replace_char(gpu_name, '\r', ' '); + return gpu_name; +} + +void GPUPlatformGlobal::init(eGPUDeviceType gpu_device, + eGPUOSType os_type, + eGPUDriverType driver_type, + eGPUSupportLevel gpu_support_level, + const char *vendor_str, + const char *renderer_str, + const char *version_str) +{ + this->clear(); + + this->initialized = true; + + this->device = gpu_device; + this->os = os_type; + this->driver = driver_type; + this->support_level = gpu_support_level; + + this->vendor = BLI_strdup(vendor_str); + this->renderer = BLI_strdup(renderer_str); + this->version = BLI_strdup(version_str); + this->support_key = create_key(gpu_support_level, vendor_str, renderer_str, version_str); + this->gpu_name = create_gpu_name(vendor_str, renderer_str, version_str); } void GPUPlatformGlobal::clear() { - MEM_SAFE_FREE(GPG.support_key); - MEM_SAFE_FREE(GPG.gpu_name); + MEM_SAFE_FREE(vendor); + MEM_SAFE_FREE(renderer); + MEM_SAFE_FREE(version); + MEM_SAFE_FREE(support_key); + MEM_SAFE_FREE(gpu_name); initialized = false; } @@ -96,22 +123,44 @@ using namespace blender::gpu; eGPUSupportLevel GPU_platform_support_level() { + BLI_assert(GPG.initialized); return GPG.support_level; } -const char *GPU_platform_support_level_key() +const char *GPU_platform_vendor(void) +{ + BLI_assert(GPG.initialized); + return GPG.vendor; +} + +const char *GPU_platform_renderer(void) +{ + BLI_assert(GPG.initialized); + return GPG.renderer; +} + +const char *GPU_platform_version(void) +{ + BLI_assert(GPG.initialized); + return GPG.version; +} + +const char *GPU_platform_support_level_key(void) { + BLI_assert(GPG.initialized); return GPG.support_key; } const char *GPU_platform_gpu_name(void) { + BLI_assert(GPG.initialized); return GPG.gpu_name; } /* GPU Types */ bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver) { + BLI_assert(GPG.initialized); return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver); } diff --git a/source/blender/gpu/intern/gpu_platform_private.hh b/source/blender/gpu/intern/gpu_platform_private.hh index 02d99efa4a9..f823269ab54 100644 --- a/source/blender/gpu/intern/gpu_platform_private.hh +++ b/source/blender/gpu/intern/gpu_platform_private.hh @@ -34,16 +34,20 @@ class GPUPlatformGlobal { eGPUOSType os; eGPUDriverType driver; eGPUSupportLevel support_level; + char *vendor = nullptr; + char *renderer = nullptr; + char *version = nullptr; char *support_key = nullptr; char *gpu_name = nullptr; public: - void create_key(eGPUSupportLevel support_level, - const char *vendor, - const char *renderer, - const char *version); - - void create_gpu_name(const char *vendor, const char *renderer, const char *version); + void init(eGPUDeviceType gpu_device, + eGPUOSType os_type, + eGPUDriverType driver_type, + eGPUSupportLevel gpu_support_level, + const char *vendor_str, + const char *renderer_str, + const char *version_str); void clear(void); }; diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 75fe7652715..aea27756708 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -723,7 +723,7 @@ void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, cons static int g_shader_builtin_srgb_transform = 0; static bool g_shader_builtin_srgb_is_dirty = false; -static bool gpu_shader_srgb_uniform_dirty_get(void) +static bool gpu_shader_srgb_uniform_dirty_get() { return g_shader_builtin_srgb_is_dirty; } diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index 782d25747bb..5c33066c720 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -248,7 +248,7 @@ eGPUWriteMask GPU_write_mask_get() uint GPU_stencil_mask_get() { - GPUStateMutable &state = Context::get()->state_manager->mutable_state; + const GPUStateMutable &state = Context::get()->state_manager->mutable_state; return state.stencil_write_mask; } @@ -267,7 +267,7 @@ eGPUStencilTest GPU_stencil_test_get() /* NOTE: Already premultiplied by U.pixelsize. */ float GPU_line_width_get() { - GPUStateMutable &state = Context::get()->state_manager->mutable_state; + const GPUStateMutable &state = Context::get()->state_manager->mutable_state; return state.line_width; } @@ -292,7 +292,7 @@ void GPU_viewport_size_get_i(int coords[4]) bool GPU_depth_mask_get() { - GPUState &state = Context::get()->state_manager->state; + const GPUState &state = Context::get()->state_manager->state; return (state.write_mask & GPU_WRITE_DEPTH) != 0; } diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index c3e9058c6c7..997064e82a2 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -60,6 +60,12 @@ Texture::~Texture() fb_[i]->attachment_remove(fb_attachment_[i]); } } + +#ifndef GPU_NO_USE_PY_REFERENCES + if (this->py_ref) { + *this->py_ref = nullptr; + } +#endif } bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) @@ -581,6 +587,19 @@ bool GPU_texture_array(const GPUTexture *tex) return (reinterpret_cast<const Texture *>(tex)->type_get() & GPU_TEXTURE_ARRAY) != 0; } +#ifndef GPU_NO_USE_PY_REFERENCES +void **GPU_texture_py_reference_get(GPUTexture *tex) +{ + return unwrap(tex)->py_ref; +} + +void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref) +{ + BLI_assert(py_ref == nullptr || unwrap(tex)->py_ref == nullptr); + unwrap(tex)->py_ref = py_ref; +} +#endif + /* TODO remove */ int GPU_texture_opengl_bindcode(const GPUTexture *tex) { diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 3d808bce152..a8f2e482bdd 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -80,6 +80,13 @@ class Texture { int refcount = 1; /** Width & Height (of source data), optional. */ int src_w = 0, src_h = 0; +#ifndef GPU_NO_USE_PY_REFERENCES + /** + * Reference of a pointer that needs to be cleaned when deallocating the texture. + * Points to #BPyGPUTexture.tex + */ + void **py_ref = nullptr; +#endif protected: /* ---- Texture format (immutable after init). ---- */ diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index a04d4be0e52..e7d67be6d0e 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -41,39 +41,42 @@ namespace blender::gpu { void GLBackend::platform_init() { BLI_assert(!GPG.initialized); - GPG.initialized = true; + + const char *vendor = (const char *)glGetString(GL_VENDOR); + const char *renderer = (const char *)glGetString(GL_RENDERER); + const char *version = (const char *)glGetString(GL_VERSION); + eGPUDeviceType device = GPU_DEVICE_ANY; + eGPUOSType os = GPU_OS_ANY; + eGPUDriverType driver = GPU_DRIVER_ANY; + eGPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED; #ifdef _WIN32 - GPG.os = GPU_OS_WIN; + os = GPU_OS_WIN; #elif defined(__APPLE__) - GPG.os = GPU_OS_MAC; + os = GPU_OS_MAC; #else - GPG.os = GPU_OS_UNIX; + os = GPU_OS_UNIX; #endif - const char *vendor = (const char *)glGetString(GL_VENDOR); - const char *renderer = (const char *)glGetString(GL_RENDERER); - const char *version = (const char *)glGetString(GL_VERSION); - if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) { - GPG.device = GPU_DEVICE_ATI; - GPG.driver = GPU_DRIVER_OFFICIAL; + device = GPU_DEVICE_ATI; + driver = GPU_DRIVER_OFFICIAL; } else if (strstr(vendor, "NVIDIA")) { - GPG.device = GPU_DEVICE_NVIDIA; - GPG.driver = GPU_DRIVER_OFFICIAL; + device = GPU_DEVICE_NVIDIA; + driver = GPU_DRIVER_OFFICIAL; } else if (strstr(vendor, "Intel") || /* src/mesa/drivers/dri/intel/intel_context.c */ strstr(renderer, "Mesa DRI Intel") || strstr(renderer, "Mesa DRI Mobile Intel")) { - GPG.device = GPU_DEVICE_INTEL; - GPG.driver = GPU_DRIVER_OFFICIAL; + device = GPU_DEVICE_INTEL; + driver = GPU_DRIVER_OFFICIAL; if (strstr(renderer, "UHD Graphics") || /* Not UHD but affected by the same bugs. */ strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2") || strstr(renderer, "Whiskey Lake")) { - GPG.device |= GPU_DEVICE_INTEL_UHD; + device |= GPU_DEVICE_INTEL_UHD; } } else if ((strstr(renderer, "Mesa DRI R")) || @@ -81,49 +84,47 @@ void GLBackend::platform_init() (strstr(renderer, "AMD") && strstr(vendor, "X.Org")) || (strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) || (strstr(renderer, "Gallium ") && strstr(renderer, " on AMD "))) { - GPG.device = GPU_DEVICE_ATI; - GPG.driver = GPU_DRIVER_OPENSOURCE; + device = GPU_DEVICE_ATI; + driver = GPU_DRIVER_OPENSOURCE; } else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) { - GPG.device = GPU_DEVICE_NVIDIA; - GPG.driver = GPU_DRIVER_OPENSOURCE; + device = GPU_DEVICE_NVIDIA; + driver = GPU_DRIVER_OPENSOURCE; } else if (strstr(vendor, "Mesa")) { - GPG.device = GPU_DEVICE_SOFTWARE; - GPG.driver = GPU_DRIVER_SOFTWARE; + device = GPU_DEVICE_SOFTWARE; + driver = GPU_DRIVER_SOFTWARE; } else if (strstr(vendor, "Microsoft")) { - GPG.device = GPU_DEVICE_SOFTWARE; - GPG.driver = GPU_DRIVER_SOFTWARE; + device = GPU_DEVICE_SOFTWARE; + driver = GPU_DRIVER_SOFTWARE; } else if (strstr(vendor, "Apple")) { /* Apple Silicon. */ - GPG.device = GPU_DEVICE_APPLE; - GPG.driver = GPU_DRIVER_OFFICIAL; + device = GPU_DEVICE_APPLE; + driver = GPU_DRIVER_OFFICIAL; } else if (strstr(renderer, "Apple Software Renderer")) { - GPG.device = GPU_DEVICE_SOFTWARE; - GPG.driver = GPU_DRIVER_SOFTWARE; + device = GPU_DEVICE_SOFTWARE; + driver = GPU_DRIVER_SOFTWARE; } else if (strstr(renderer, "llvmpipe") || strstr(renderer, "softpipe")) { - GPG.device = GPU_DEVICE_SOFTWARE; - GPG.driver = GPU_DRIVER_SOFTWARE; + device = GPU_DEVICE_SOFTWARE; + driver = GPU_DRIVER_SOFTWARE; } else { printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n"); printf("Detected OpenGL configuration:\n"); printf("Vendor: %s\n", vendor); printf("Renderer: %s\n", renderer); - GPG.device = GPU_DEVICE_ANY; - GPG.driver = GPU_DRIVER_ANY; } /* Detect support level */ if (!GLEW_VERSION_3_3) { - GPG.support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED; + support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED; } else { - if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY)) { + if ((device & GPU_DEVICE_INTEL) && (os & GPU_OS_WIN)) { /* Old Intel drivers with known bugs that cause material properties to crash. * Version Build 10.18.14.5067 is the latest available and appears to be working * ok with our workarounds, so excluded from this list. */ @@ -132,19 +133,19 @@ void GLBackend::platform_init() strstr(version, "Build 9.18") || strstr(version, "Build 10.18.10.3") || strstr(version, "Build 10.18.10.4") || strstr(version, "Build 10.18.10.5") || strstr(version, "Build 10.18.14.4")) { - GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED; + support_level = GPU_SUPPORT_LEVEL_LIMITED; } } - if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_ANY)) { + if ((device & GPU_DEVICE_ATI) && (os & GPU_OS_UNIX)) { /* Platform seems to work when SB backend is disabled. This can be done * by adding the environment variable `R600_DEBUG=nosb`. */ if (strstr(renderer, "AMD CEDAR")) { - GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED; + support_level = GPU_SUPPORT_LEVEL_LIMITED; } } } - GPG.create_key(GPG.support_level, vendor, renderer, version); - GPG.create_gpu_name(vendor, renderer, version); + + GPG.init(device, os, driver, support_level, vendor, renderer, version); } void GLBackend::platform_exit() @@ -204,6 +205,11 @@ static bool detect_mip_render_workaround() return enable_workaround; } +static const char *gl_extension_get(int i) +{ + return (char *)glGetStringi(GL_EXTENSIONS, i); +} + static void detect_workarounds() { const char *vendor = (const char *)glGetString(GL_VENDOR); @@ -419,6 +425,16 @@ void GLBackend::capabilities_init() glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &GCaps.max_textures_vert); glGetIntegerv(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, &GCaps.max_textures_geom); glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &GCaps.max_textures); + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &GCaps.max_uniforms_vert); + glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &GCaps.max_uniforms_frag); + glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &GCaps.max_batch_indices); + glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &GCaps.max_batch_vertices); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &GCaps.max_vertex_attribs); + glGetIntegerv(GL_MAX_VARYING_FLOATS, &GCaps.max_varying_floats); + + glGetIntegerv(GL_NUM_EXTENSIONS, &GCaps.extensions_len); + GCaps.extension_get = gl_extension_get; + GCaps.mem_stats_support = GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo; GCaps.shader_image_load_store_support = GLEW_ARB_shader_image_load_store; /* GL specific capabilities. */ diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc index 976a31ab5cb..fc1d1006d0d 100644 --- a/source/blender/gpu/opengl/gl_batch.cc +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -44,7 +44,7 @@ using namespace blender::gpu; /* -------------------------------------------------------------------- */ -/** \name Vao cache +/** \name VAO Cache * * Each #GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration. * TODO(fclem): Could be revisited to avoid so much cross references. diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh index 444ae1c0ab1..704b6471dd5 100644 --- a/source/blender/gpu/opengl/gl_batch.hh +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -43,7 +43,7 @@ class GLShaderInterface; #define GPU_VAO_STATIC_LEN 3 -/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer) +/* VAO management: remembers all geometry state (vertex attribute bindings & element buffer) * for each shader interface. Start with a static number of vaos and fallback to dynamic count * if necessary. Once a batch goes dynamic it does not go back. */ class GLVaoCache { diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh index d0bf0fcdf1f..b42f439f9ec 100644 --- a/source/blender/gpu/opengl/gl_immediate.hh +++ b/source/blender/gpu/opengl/gl_immediate.hh @@ -38,7 +38,7 @@ namespace blender::gpu { class GLImmediate : public Immediate { private: - /* Use two buffers for strict and unstrict vertex count to + /* Use two buffers for strict and non-strict vertex count to * avoid some huge driver slowdown (see T70922). * Use accessor functions to get / modify. */ struct { diff --git a/source/blender/gpu/tests/gpu_testing.cc b/source/blender/gpu/tests/gpu_testing.cc index b9fc78dc084..ac42c5875c8 100644 --- a/source/blender/gpu/tests/gpu_testing.cc +++ b/source/blender/gpu/tests/gpu_testing.cc @@ -2,6 +2,8 @@ #include "testing/testing.h" +#include "CLG_log.h" + #include "GPU_context.h" #include "GPU_init_exit.h" #include "gpu_testing.hh" @@ -13,6 +15,7 @@ namespace blender::gpu { void GPUTest::SetUp() { GHOST_GLSettings glSettings = {0}; + CLG_init(); ghost_system = GHOST_CreateSystem(); ghost_context = GHOST_CreateOpenGLContext(ghost_system, glSettings); context = GPU_context_create(nullptr); @@ -26,6 +29,7 @@ void GPUTest::TearDown() GPU_context_discard(context); GHOST_DisposeOpenGLContext(ghost_system, ghost_context); GHOST_DisposeSystem(ghost_system); + CLG_exit(); } } // namespace blender::gpu diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index 7ce795280a3..be0e364c85f 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -166,12 +166,6 @@ if(WITH_CODEC_FFMPEG) ${OPENJPEG_LIBRARIES} ) add_definitions(-DWITH_FFMPEG) - - remove_strict_c_flags_file( - intern/anim_movie.c - intern/indexer.c - intern/util.c - ) endif() if(WITH_IMAGE_DDS) diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index 1239d3881de..7d7864306a1 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -135,7 +135,7 @@ struct anim { struct ImBuf *last_frame; int64_t last_pts; int64_t next_pts; - AVPacket next_packet; + AVPacket *next_packet; #endif char index_dir[768]; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 96cd1fb61a4..cdf6ca5c181 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -79,6 +79,7 @@ # include <libavcodec/avcodec.h> # include <libavformat/avformat.h> +# include <libavutil/imgutils.h> # include <libavutil/rational.h> # include <libswscale/swscale.h> @@ -519,12 +520,10 @@ static int startffmpeg(struct anim *anim) double frs_den; int streamcount; -# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT /* The following for color space determination */ int srcRange, dstRange, brightness, contrast, saturation; int *table; const int *inv_table; -# endif if (anim == NULL) { return (-1); @@ -547,7 +546,7 @@ static int startffmpeg(struct anim *anim) video_stream_index = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { - if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (streamcount > 0) { streamcount--; continue; @@ -563,16 +562,17 @@ static int startffmpeg(struct anim *anim) } video_stream = pFormatCtx->streams[video_stream_index]; - pCodecCtx = video_stream->codec; /* Find the decoder for the video stream */ - pCodec = avcodec_find_decoder(pCodecCtx->codec_id); + pCodec = avcodec_find_decoder(video_stream->codecpar->codec_id); if (pCodec == NULL) { avformat_close_input(&pFormatCtx); return -1; } - pCodecCtx->workaround_bugs = 1; + pCodecCtx = avcodec_alloc_context3(NULL); + avcodec_parameters_to_context(pCodecCtx, video_stream->codecpar); + pCodecCtx->workaround_bugs = FF_BUG_AUTODETECT; if (pCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { pCodecCtx->thread_count = 0; @@ -593,7 +593,7 @@ static int startffmpeg(struct anim *anim) return -1; } if (pCodecCtx->pix_fmt == AV_PIX_FMT_NONE) { - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&pFormatCtx); return -1; } @@ -639,7 +639,7 @@ static int startffmpeg(struct anim *anim) anim->params = 0; anim->x = pCodecCtx->width; - anim->y = av_get_cropped_height_from_codec(pCodecCtx); + anim->y = pCodecCtx->height; anim->pFormatCtx = pFormatCtx; anim->pCodecCtx = pCodecCtx; @@ -654,7 +654,8 @@ static int startffmpeg(struct anim *anim) anim->last_frame = 0; anim->last_pts = -1; anim->next_pts = -1; - anim->next_packet.stream_index = -1; + anim->next_packet = av_packet_alloc(); + anim->next_packet->stream_index = -1; anim->pFrame = av_frame_alloc(); anim->pFrameComplete = false; @@ -668,8 +669,9 @@ static int startffmpeg(struct anim *anim) if (av_frame_get_buffer(anim->pFrameRGB, 32) < 0) { fprintf(stderr, "Could not allocate frame data.\n"); - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->next_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -678,10 +680,11 @@ static int startffmpeg(struct anim *anim) } } - if (avpicture_get_size(AV_PIX_FMT_RGBA, anim->x, anim->y) != anim->x * anim->y * 4) { + if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) { fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n"); - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->next_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -690,14 +693,17 @@ static int startffmpeg(struct anim *anim) } if (anim->ib_flags & IB_animdeinterlace) { - avpicture_fill((AVPicture *)anim->pFrameDeinterlaced, - MEM_callocN(avpicture_get_size(anim->pCodecCtx->pix_fmt, - anim->pCodecCtx->width, - anim->pCodecCtx->height), - "ffmpeg deinterlace"), - anim->pCodecCtx->pix_fmt, - anim->pCodecCtx->width, - anim->pCodecCtx->height); + av_image_fill_arrays(anim->pFrameDeinterlaced->data, + anim->pFrameDeinterlaced->linesize, + MEM_callocN(av_image_get_buffer_size(anim->pCodecCtx->pix_fmt, + anim->pCodecCtx->width, + anim->pCodecCtx->height, + 1), + "ffmpeg deinterlace"), + anim->pCodecCtx->pix_fmt, + anim->pCodecCtx->width, + anim->pCodecCtx->height, + 1); } if (pCodecCtx->has_b_frames) { @@ -720,8 +726,9 @@ static int startffmpeg(struct anim *anim) if (!anim->img_convert_ctx) { fprintf(stderr, "Can't transform color space??? Bailing out...\n"); - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->next_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -729,7 +736,6 @@ static int startffmpeg(struct anim *anim) return -1; } -# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT /* Try do detect if input has 0-255 YCbCR range (JFIF Jpeg MotionJpeg) */ if (!sws_getColorspaceDetails(anim->img_convert_ctx, (int **)&inv_table, @@ -756,7 +762,6 @@ static int startffmpeg(struct anim *anim) else { fprintf(stderr, "Warning: Could not set libswscale colorspace details.\n"); } -# endif return 0; } @@ -795,11 +800,11 @@ static void ffmpeg_postprocess(struct anim *anim) input->data[3]); if (anim->ib_flags & IB_animdeinterlace) { - if (avpicture_deinterlace((AVPicture *)anim->pFrameDeinterlaced, - (const AVPicture *)anim->pFrame, - anim->pCodecCtx->pix_fmt, - anim->pCodecCtx->width, - anim->pCodecCtx->height) < 0) { + if (av_image_deinterlace(anim->pFrameDeinterlaced, + anim->pFrame, + anim->pCodecCtx->pix_fmt, + anim->pCodecCtx->width, + anim->pCodecCtx->height) < 0) { filter_y = true; } else { @@ -808,16 +813,18 @@ static void ffmpeg_postprocess(struct anim *anim) } if (!need_aligned_ffmpeg_buffer(anim)) { - avpicture_fill((AVPicture *)anim->pFrameRGB, - (unsigned char *)ibuf->rect, - AV_PIX_FMT_RGBA, - anim->x, - anim->y); + av_image_fill_arrays(anim->pFrameRGB->data, + anim->pFrameRGB->linesize, + (unsigned char *)ibuf->rect, + AV_PIX_FMT_RGBA, + anim->x, + anim->y, + 1); } # if defined(__x86_64__) || defined(_M_X64) /* Scale and flip image over Y axis in one go, using negative strides. - * This doesn't work with arm/powerpc though and may be misusing the API. + * This doesn't work with ARM/PowerPC though and may be misusing the API. * Limit it x86_64 where it appears to work. * http://trac.ffmpeg.org/ticket/9060 */ int *dstStride = anim->pFrameRGB->linesize; @@ -903,82 +910,70 @@ static int ffmpeg_decode_video_frame(struct anim *anim) av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n"); - if (anim->next_packet.stream_index == anim->videoStream) { - av_free_packet(&anim->next_packet); - anim->next_packet.stream_index = -1; + if (anim->next_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->next_packet); + anim->next_packet->stream_index = -1; } - while ((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) { + while ((rval = av_read_frame(anim->pFormatCtx, anim->next_packet)) >= 0) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n", - (anim->next_packet.stream_index == anim->videoStream) ? "->" : " ", - anim->next_packet.stream_index, + (anim->next_packet->stream_index == anim->videoStream) ? "->" : " ", + anim->next_packet->stream_index, anim->videoStream, - (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.dts, - (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.pts, - (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); - if (anim->next_packet.stream_index == anim->videoStream) { + (anim->next_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->dts, + (anim->next_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->pts, + (anim->next_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); + if (anim->next_packet->stream_index == anim->videoStream) { anim->pFrameComplete = 0; - avcodec_decode_video2( - anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet); + avcodec_send_packet(anim->pCodecCtx, anim->next_packet); + anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE: next_pts=%" PRId64 " pkt_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + " FRAME DONE: next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts, (int64_t)anim->next_pts); break; } } - av_free_packet(&anim->next_packet); - anim->next_packet.stream_index = -1; + av_packet_unref(anim->next_packet); + anim->next_packet->stream_index = -1; } if (rval == AVERROR_EOF) { - /* this sets size and data fields to zero, - * which is necessary to decode the remaining data - * in the decoder engine after EOF. It also prevents a memory - * leak, since av_read_frame spills out a full size packet even - * on EOF... (and: it's safe to call on NULL packets) */ - - av_free_packet(&anim->next_packet); - - anim->next_packet.size = 0; - anim->next_packet.data = 0; - + /* Flush any remaining frames out of the decoder. */ anim->pFrameComplete = 0; - avcodec_decode_video2( - anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet); + avcodec_send_packet(anim->pCodecCtx, NULL); + anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE (after EOF): next_pts=%" PRId64 " pkt_pts=%" PRId64 - ", guessed_pts=%" PRId64 "\n", + " FRAME DONE (after EOF): next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts, (int64_t)anim->next_pts); rval = 0; } } if (rval < 0) { - anim->next_packet.stream_index = -1; + av_packet_unref(anim->next_packet); + anim->next_packet->stream_index = -1; av_log(anim->pFormatCtx, AV_LOG_ERROR, " DECODE READ FAILED: av_read_frame() " - "returned error: %d\n", - rval); + "returned error: %s\n", + av_err2str(rval)); } return (rval >= 0); @@ -1154,7 +1149,7 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_p /* Read first video stream packet. */ AVPacket read_packet = {0}; while (av_read_frame(anim->pFormatCtx, &read_packet) >= 0) { - if (anim->next_packet.stream_index == anim->videoStream) { + if (anim->next_packet->stream_index == anim->videoStream) { break; } } @@ -1246,9 +1241,9 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ anim->next_pts = -1; - if (anim->next_packet.stream_index == anim->videoStream) { - av_free_packet(&anim->next_packet); - anim->next_packet.stream_index = -1; + if (anim->next_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->next_packet); + anim->next_packet->stream_index = -1; } /* memset(anim->pFrame, ...) ?? */ @@ -1351,32 +1346,30 @@ static void free_anim_ffmpeg(struct anim *anim) } if (anim->pCodecCtx) { - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->next_packet); - /* Special case here: pFrame could share pointers with codec, - * so in order to avoid double-free we don't use av_frame_free() - * to free the frame. - * - * Could it be a bug in FFmpeg? - */ - av_free(anim->pFrame); + av_frame_free(&anim->pFrame); if (!need_aligned_ffmpeg_buffer(anim)) { /* If there's no need for own aligned buffer it means that FFmpeg's * frame shares the same buffer as temporary ImBuf. In this case we * should not free the buffer when freeing the FFmpeg buffer. */ - avpicture_fill((AVPicture *)anim->pFrameRGB, NULL, AV_PIX_FMT_RGBA, anim->x, anim->y); + av_image_fill_arrays(anim->pFrameRGB->data, + anim->pFrameRGB->linesize, + NULL, + AV_PIX_FMT_RGBA, + anim->x, + anim->y, + 1); } av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); sws_freeContext(anim->img_convert_ctx); IMB_freeImBuf(anim->last_frame); - if (anim->next_packet.stream_index != -1) { - av_free_packet(&anim->next_packet); - } } anim->duration_in_frames = 0; } diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index ef9f6d861a3..11ce77e3091 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -48,6 +48,7 @@ #ifdef WITH_FFMPEG # include "ffmpeg_compat.h" +# include <libavutil/imgutils.h> #endif static const char magic[] = "BlenMIdx"; @@ -488,14 +489,14 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->of = avformat_alloc_context(); rv->of->oformat = av_guess_format("avi", NULL, NULL); - BLI_strncpy(rv->of->filename, fname, sizeof(rv->of->filename)); + rv->of->url = av_strdup(fname); - fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename); + fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url); rv->st = avformat_new_stream(rv->of, NULL); rv->st->id = 0; - rv->c = rv->st->codec; + rv->c = avcodec_alloc_context3(NULL); rv->c->codec_type = AVMEDIA_TYPE_VIDEO; rv->c->codec_id = AV_CODEC_ID_H264; rv->c->width = width; @@ -513,7 +514,9 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( fprintf(stderr, "No ffmpeg encoder available? " "Proxy not built!\n"); - av_free(rv->of); + avcodec_free_context(&rv->c); + avformat_free_context(rv->of); + MEM_freeN(rv); return NULL; } @@ -524,7 +527,7 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P; } - rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio; + rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->sample_aspect_ratio; rv->c->time_base.den = 25; rv->c->time_base.num = 1; @@ -557,34 +560,54 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( } if (rv->of->flags & AVFMT_GLOBALHEADER) { - rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER; + rv->c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } - if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) { + avcodec_parameters_from_context(rv->st->codecpar, rv->c); + + int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE); + + if (ret < 0) { fprintf(stderr, - "Couldn't open outputfile! " - "Proxy not built!\n"); - av_free(rv->of); - return 0; + "Couldn't open IO: %s\n" + "Proxy not built!\n", + av_err2str(ret)); + avcodec_free_context(&rv->c); + avformat_free_context(rv->of); + MEM_freeN(rv); + return NULL; } - avcodec_open2(rv->c, rv->codec, &codec_opts); + ret = avcodec_open2(rv->c, rv->codec, &codec_opts); + if (ret < 0) { + fprintf(stderr, + "Couldn't open codec: %s\n" + "Proxy not built!\n", + av_err2str(ret)); + avcodec_free_context(&rv->c); + avformat_free_context(rv->of); + MEM_freeN(rv); + return NULL; + } - rv->orig_height = av_get_cropped_height_from_codec(st->codec); + rv->orig_height = st->codecpar->height; - if (st->codec->width != width || st->codec->height != height || - st->codec->pix_fmt != rv->c->pix_fmt) { + if (st->codecpar->width != width || st->codecpar->height != height || + st->codecpar->format != rv->c->pix_fmt) { rv->frame = av_frame_alloc(); - avpicture_fill((AVPicture *)rv->frame, - MEM_mallocN(avpicture_get_size(rv->c->pix_fmt, round_up(width, 16), height), - "alloc proxy output frame"), - rv->c->pix_fmt, - round_up(width, 16), - height); - - rv->sws_ctx = sws_getContext(st->codec->width, + av_image_fill_arrays( + rv->frame->data, + rv->frame->linesize, + MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1), + "alloc proxy output frame"), + rv->c->pix_fmt, + round_up(width, 16), + height, + 1); + + rv->sws_ctx = sws_getContext(st->codecpar->width, rv->orig_height, - st->codec->pix_fmt, + st->codecpar->format, width, height, rv->c->pix_fmt, @@ -594,26 +617,30 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( NULL); } - if (avformat_write_header(rv->of, NULL) < 0) { + ret = avformat_write_header(rv->of, NULL); + if (ret < 0) { fprintf(stderr, - "Couldn't set output parameters? " - "Proxy not built!\n"); - av_free(rv->of); - return 0; + "Couldn't write header: %s\n" + "Proxy not built!\n", + av_err2str(ret)); + + if (rv->frame) { + av_frame_free(&rv->frame); + } + + avcodec_free_context(&rv->c); + avformat_free_context(rv->of); + MEM_freeN(rv); + return NULL; } return rv; } -static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame) +static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame) { - AVPacket packet = {0}; - int ret, got_output; - - av_init_packet(&packet); - if (!ctx) { - return 0; + return; } if (ctx->sws_ctx && frame && @@ -633,35 +660,46 @@ static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fra frame->pts = ctx->cfra++; } - ret = avcodec_encode_video2(ctx->c, &packet, frame, &got_output); + int ret = avcodec_send_frame(ctx->c, frame); if (ret < 0) { - fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", ctx->cfra - 1, ctx->of->filename); - return 0; + /* Can't send frame to encoder. This shouldn't happen. */ + fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret)); + return; } + AVPacket *packet = av_packet_alloc(); + + while (ret >= 0) { + ret = avcodec_receive_packet(ctx->c, packet); - if (got_output) { - if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, ctx->c->time_base, ctx->st->time_base); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + /* No more packets to flush. */ + break; } - if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, ctx->c->time_base, ctx->st->time_base); + if (ret < 0) { + fprintf(stderr, + "Error encoding proxy frame %d for '%s': %s\n", + ctx->cfra - 1, + ctx->of->url, + av_err2str(ret)); + break; } - packet.stream_index = ctx->st->index; + packet->stream_index = ctx->st->index; + av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base); - if (av_interleaved_write_frame(ctx->of, &packet) != 0) { + int write_ret = av_interleaved_write_frame(ctx->of, packet); + if (write_ret != 0) { fprintf(stderr, "Error writing proxy frame %d " - "into '%s'\n", + "into '%s': %s\n", ctx->cfra - 1, - ctx->of->filename); - return 0; + ctx->of->url, + av_err2str(write_ret)); + break; } - - return 1; } - return 0; + av_packet_free(&packet); } static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) @@ -674,15 +712,15 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) } if (!rollback) { - while (add_to_proxy_output_ffmpeg(ctx, NULL)) { - } + /* Flush the remaining packets. */ + add_to_proxy_output_ffmpeg(ctx, NULL); } avcodec_flush_buffers(ctx->c); av_write_trailer(ctx->of); - avcodec_close(ctx->c); + avcodec_free_context(&ctx->c); if (ctx->of->oformat) { if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) { @@ -777,7 +815,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, /* Find the video stream */ context->videoStream = -1; for (i = 0; i < context->iFormatCtx->nb_streams; i++) { - if (context->iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (context->iFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (streamcount > 0) { streamcount--; continue; @@ -794,9 +832,8 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, } context->iStream = context->iFormatCtx->streams[context->videoStream]; - context->iCodecCtx = context->iStream->codec; - context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id); + context->iCodec = avcodec_find_decoder(context->iStream->codecpar->codec_id); if (context->iCodec == NULL) { avformat_close_input(&context->iFormatCtx); @@ -804,7 +841,9 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, return NULL; } - context->iCodecCtx->workaround_bugs = 1; + context->iCodecCtx = avcodec_alloc_context3(NULL); + avcodec_parameters_to_context(context->iCodecCtx, context->iStream->codecpar); + context->iCodecCtx->workaround_bugs = FF_BUG_AUTODETECT; if (context->iCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { context->iCodecCtx->thread_count = 0; @@ -822,19 +861,19 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) { avformat_close_input(&context->iFormatCtx); + avcodec_free_context(&context->iCodecCtx); MEM_freeN(context); return NULL; } for (i = 0; i < num_proxy_sizes; i++) { if (proxy_sizes_in_use & proxy_sizes[i]) { - context->proxy_ctx[i] = alloc_proxy_output_ffmpeg( - anim, - context->iStream, - proxy_sizes[i], - context->iCodecCtx->width * proxy_fac[i], - av_get_cropped_height_from_codec(context->iCodecCtx) * proxy_fac[i], - quality); + context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(anim, + context->iStream, + proxy_sizes[i], + context->iCodecCtx->width * proxy_fac[i], + context->iCodecCtx->height * proxy_fac[i], + quality); if (!context->proxy_ctx[i]) { proxy_sizes_in_use &= ~proxy_sizes[i]; } @@ -873,7 +912,7 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int } } - avcodec_close(context->iCodecCtx); + avcodec_free_context(&context->iCodecCtx); avformat_close_input(&context->iFormatCtx); MEM_freeN(context); @@ -938,23 +977,18 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, short *do_update, float *progress) { - AVFrame *in_frame = 0; - AVPacket next_packet; + AVFrame *in_frame = av_frame_alloc(); + AVPacket *next_packet = av_packet_alloc(); uint64_t stream_size; - memset(&next_packet, 0, sizeof(AVPacket)); - - in_frame = av_frame_alloc(); - stream_size = avio_size(context->iFormatCtx->pb); context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL)); context->pts_time_base = av_q2d(context->iStream->time_base); - while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) { - int frame_finished = 0; + while (av_read_frame(context->iFormatCtx, next_packet) >= 0) { float next_progress = - (float)((int)floor(((double)next_packet.pos) * 100 / ((double)stream_size) + 0.5)) / 100; + (float)((int)floor(((double)next_packet->pos) * 100 / ((double)stream_size) + 0.5)) / 100; if (*progress != next_progress) { *progress = next_progress; @@ -962,50 +996,59 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, } if (*stop) { - av_free_packet(&next_packet); break; } - if (next_packet.stream_index == context->videoStream) { - if (next_packet.flags & AV_PKT_FLAG_KEY) { + if (next_packet->stream_index == context->videoStream) { + if (next_packet->flags & AV_PKT_FLAG_KEY) { context->last_seek_pos = context->seek_pos; context->last_seek_pos_dts = context->seek_pos_dts; - context->seek_pos = next_packet.pos; - context->seek_pos_dts = next_packet.dts; - context->seek_pos_pts = next_packet.pts; + context->seek_pos = next_packet->pos; + context->seek_pos_dts = next_packet->dts; + context->seek_pos_pts = next_packet->pts; } - avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet); - } + int ret = avcodec_send_packet(context->iCodecCtx, next_packet); + while (ret >= 0) { + ret = avcodec_receive_frame(context->iCodecCtx, in_frame); - if (frame_finished) { - index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + /* No more frames to flush. */ + break; + } + if (ret < 0) { + fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret)); + break; + } + index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame); + } } - av_free_packet(&next_packet); } /* process pictures still stuck in decoder engine after EOF - * according to ffmpeg docs using 0-size packets. + * according to ffmpeg docs using NULL packets. * * At least, if we haven't already stopped... */ - /* this creates the 0-size packet and prevents a memory leak. */ - av_free_packet(&next_packet); - if (!*stop) { - int frame_finished; - - do { - frame_finished = 0; + int ret = avcodec_send_packet(context->iCodecCtx, NULL); - avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet); + while (ret >= 0) { + ret = avcodec_receive_frame(context->iCodecCtx, in_frame); - if (frame_finished) { - index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + /* No more frames to flush. */ + break; } - } while (frame_finished); + if (ret < 0) { + fprintf(stderr, "Error flushing proxy frame: %s\n", av_err2str(ret)); + break; + } + index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame); + } } + av_packet_free(&next_packet); av_free(in_frame); return 1; diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 001cd4e1575..12f7553d5e4 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -374,7 +374,7 @@ static void scanline_separate_32bit(float *rectf, const float *fbuf, int scanlin static void imb_read_tiff_resolution(ImBuf *ibuf, TIFF *image) { - uint16 unit; + uint16_t unit; float xres; float yres; @@ -569,7 +569,7 @@ ImBuf *imb_loadtiff(const unsigned char *mem, TIFF *image = NULL; ImBuf *ibuf = NULL, *hbuf; ImbTIFFMemFile memFile; - uint32 width, height; + uint32_t width, height; char *format = NULL; int level; short spp; @@ -690,7 +690,7 @@ void imb_loadtiletiff( ImBuf *ibuf, const unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect) { TIFF *image = NULL; - uint32 width, height; + uint32_t width, height; ImbTIFFMemFile memFile; image = imb_tiff_client_open(&memFile, mem, size); @@ -761,7 +761,7 @@ void imb_loadtiletiff( bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) { TIFF *image = NULL; - uint16 samplesperpixel, bitspersample; + uint16_t samplesperpixel, bitspersample; size_t npixels; unsigned char *pixels = NULL; unsigned char *from = NULL, *to = NULL; @@ -774,7 +774,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) /* check for a valid number of bytes per pixel. Like the PNG writer, * the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding * to gray, RGB, RGBA respectively. */ - samplesperpixel = (uint16)((ibuf->planes + 7) >> 3); + samplesperpixel = (uint16_t)((ibuf->planes + 7) >> 3); if ((samplesperpixel > 4) || (samplesperpixel == 2)) { fprintf(stderr, "imb_savetiff: unsupported number of bytes per " diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 64dad5de902..fabf6c5c3bd 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -245,7 +245,6 @@ static void ffmpeg_log_callback(void *ptr, int level, const char *format, va_lis void IMB_ffmpeg_init(void) { - av_register_all(); avdevice_register_all(); ffmpeg_last_error[0] = '\0'; @@ -269,7 +268,6 @@ static int isffmpeg(const char *filepath) unsigned int i; int videoStream; AVCodec *pCodec; - AVCodecContext *pCodecCtx; if (BLI_path_extension_check_n(filepath, ".swf", @@ -310,8 +308,8 @@ static int isffmpeg(const char *filepath) /* Find the first video stream */ videoStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { - if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codec && - (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)) { + if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codecpar && + (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) { videoStream = i; break; } @@ -322,21 +320,15 @@ static int isffmpeg(const char *filepath) return 0; } - pCodecCtx = pFormatCtx->streams[videoStream]->codec; + AVCodecParameters *codec_par = pFormatCtx->streams[videoStream]->codecpar; /* Find the decoder for the video stream */ - pCodec = avcodec_find_decoder(pCodecCtx->codec_id); + pCodec = avcodec_find_decoder(codec_par->codec_id); if (pCodec == NULL) { avformat_close_input(&pFormatCtx); return 0; } - if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { - avformat_close_input(&pFormatCtx); - return 0; - } - - avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 1; diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index c00e57c8edc..29b29324ee3 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -250,7 +250,7 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) UVSample uvs_and_indices; - if (!frame_has_been_written_ && args_.export_params->uvs) { + if (args_.export_params->uvs) { const char *name = get_uv_sample(uvs_and_indices, m_custom_data_config, &mesh->ldata); if (!uvs_and_indices.indices.empty() && !uvs_and_indices.uvs.empty()) { @@ -312,7 +312,7 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); UVSample sample; - if (!frame_has_been_written_ && args_.export_params->uvs) { + if (args_.export_params->uvs) { const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); if (!sample.indices.empty() && !sample.uvs.empty()) { diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp index 194a9ea397e..ac4c65464c8 100644 --- a/source/blender/io/collada/Materials.cpp +++ b/source/blender/io/collada/Materials.cpp @@ -133,6 +133,19 @@ void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, in nodeAddLink(ntree, from_node, from_socket, to_node, to_socket); } +void MaterialNode::add_link(bNode *from_node, + const char *from_label, + bNode *to_node, + const char *to_label) +{ + bNodeSocket *from_socket = nodeFindSocket(from_node, SOCK_OUT, from_label); + bNodeSocket *to_socket = nodeFindSocket(to_node, SOCK_IN, to_label); + + if (from_socket && to_socket) { + nodeAddLink(ntree, from_node, from_socket, to_node, to_socket); + } +} + void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val) { float reflectivity = val.getFloatValue(); @@ -218,22 +231,32 @@ void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode, void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot) { int locy = -300 * (node_map.size() - 2); - if (cot.isColor()) { - COLLADAFW::Color col = cot.getColor(); - bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color"); - float *fcol = (float *)socket->default_value; - fcol[0] = material->r = col.getRed(); - fcol[1] = material->g = col.getGreen(); - fcol[2] = material->b = col.getBlue(); - fcol[3] = material->a = col.getAlpha(); - } - else if (cot.isTexture()) { + if (cot.isTexture()) { bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color"); if (texture_node != nullptr) { add_link(texture_node, 0, shader_node, 0); } } + else { + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color"); + float *fcol = (float *)socket->default_value; + + if (cot.isColor()) { + COLLADAFW::Color col = cot.getColor(); + fcol[0] = material->r = col.getRed(); + fcol[1] = material->g = col.getGreen(); + fcol[2] = material->b = col.getBlue(); + fcol[3] = material->a = col.getAlpha(); + } + else { + /* no diffuse term = same as black */ + fcol[0] = material->r = 0.0f; + fcol[1] = material->g = 0.0f; + fcol[2] = material->b = 0.0f; + fcol[3] = material->a = 1.0f; + } + } } Image *MaterialNode::get_diffuse_image() @@ -326,7 +349,7 @@ void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot) else if (cot.isTexture()) { bNode *texture_node = add_texture_node(cot, -300, locy, "Emission"); if (texture_node != nullptr) { - add_link(texture_node, 0, shader_node, 0); + add_link(texture_node, "Color", shader_node, "Emission"); } } @@ -363,18 +386,38 @@ void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot) void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot) { + bool has_specularity = true; int locy = -300 * (node_map.size() - 2); if (cot.isColor()) { COLLADAFW::Color col = cot.getColor(); - bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular"); - set_color(node, col); - /* TODO: Connect node */ + + if (col.getRed() == 0 && col.getGreen() == 0 && col.getBlue() == 0) { + has_specularity = false; + } + else { + bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular"); + set_color(node, col); + /* TODO: Connect node */ + } } - /* texture */ else if (cot.isTexture()) { add_texture_node(cot, -300, locy, "Specular"); /* TODO: Connect node */ } + else { + /* no specular term) */ + has_specularity = false; + } + + if (!has_specularity) { + /* If specularity is black or not defined reset the Specular value to 0 + TODO: This is a solution only for a corner case. We must find a better + way to handle specularity in general. Also note that currently we + do not export specularity values, see EffectExporter::operator() + */ + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Specular"); + ((bNodeSocketValueFloat *)socket->default_value)->value = 0.0f; + } } bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot, diff --git a/source/blender/io/collada/Materials.h b/source/blender/io/collada/Materials.h index 2d8c823a4c2..1886acb7a6a 100644 --- a/source/blender/io/collada/Materials.h +++ b/source/blender/io/collada/Materials.h @@ -48,6 +48,7 @@ class MaterialNode { bNodeTree *prepare_material_nodetree(); bNode *add_node(int node_type, int locx, int locy, std::string label); void add_link(bNode *from_node, int from_index, bNode *to_node, int to_index); + void add_link(bNode *from_node, const char *from_label, bNode *to_node, const char *to_label); bNode *add_texture_node(COLLADAFW::ColorOrTexture &cot, int locx, int locy, std::string label); void setShaderType(); diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc index dc76b6ed661..73b3e46959b 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc @@ -102,7 +102,7 @@ bool GpencilImporterSVG::read() bGPDlayer *gpl = (bGPDlayer *)BLI_findstring( &gpd_->layers, layer_id, offsetof(bGPDlayer, info)); if (gpl == nullptr) { - gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true); + gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true, false); /* Disable lights. */ gpl->flag &= ~GP_LAYER_USE_LIGHTS; } diff --git a/source/blender/io/gpencil/nanosvg/nanosvg.h b/source/blender/io/gpencil/nanosvg/nanosvg.h index 6db32e9f40a..886b6402267 100644 --- a/source/blender/io/gpencil/nanosvg/nanosvg.h +++ b/source/blender/io/gpencil/nanosvg/nanosvg.h @@ -1289,7 +1289,7 @@ static const char *nsvg__parseNumber(const char *s, char *it, const int size) return s; } -static const char *nsvg__getNextPathItem(const char *s, char *it) +static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs) { it[0] = '\0'; // Skip white spaces and commas @@ -1297,6 +1297,15 @@ static const char *nsvg__getNextPathItem(const char *s, char *it) s++; if (!*s) return s; + + /* Blender: Special case for arc command's 4th and 5th arguments. */ + if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) { + it[0] = s[0]; + it[1] = '\0'; + s++; + return s; + } + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { s = nsvg__parseNumber(s, it, 64); } @@ -1576,8 +1585,8 @@ static int nsvg__isCoordinate(const char *s) // optional sign if (*s == '-' || *s == '+') s++; - // must have at least one digit - return nsvg__isdigit(*s); + // must have at least one digit, or start by a dot + return (nsvg__isdigit(*s) || *s == '.'); } static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str) @@ -2413,7 +2422,7 @@ static void nsvg__parsePath(NSVGparser *p, const char **attr) nargs = 0; while (*s) { - s = nsvg__getNextPathItem(s, item); + s = nsvg__getNextPathItem(s, item, cmd, nargs); if (!*item) break; if (cmd != '\0' && nsvg__isCoordinate(item)) { @@ -2740,7 +2749,7 @@ static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag) s = attr[i + 1]; nargs = 0; while (*s) { - s = nsvg__getNextPathItem(s, item); + s = nsvg__getNextPathItem(s, item, '\0', nargs); args[nargs++] = (float)nsvg__atof(item); if (nargs >= 2) { if (npts == 0) diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 5cc525a6cff..583e56de9c2 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -265,9 +265,10 @@ typedef struct bPoseChannel { * since the alternative is highly complicated - campbell */ struct bPoseChannel *custom_tx; - float custom_scale; - - char _pad1[4]; + float custom_scale; /* Deprecated */ + float custom_scale_xyz[3]; + float custom_translation[3]; + float custom_rotation_euler[3]; /** Transforms - written in by actions or transform. */ float loc[3]; @@ -417,9 +418,9 @@ typedef enum ePchan_DrawFlag { PCHAN_DRAW_NO_CUSTOM_BONE_SIZE = (1 << 0), } ePchan_DrawFlag; -#define PCHAN_CUSTOM_DRAW_SIZE(pchan) \ - (pchan)->custom_scale *( \ - ((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length) +/* Note: It doesn't take custom_scale_xyz into account */ +#define PCHAN_CUSTOM_BONE_LENGTH(pchan) \ + (((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length) #ifdef DNA_DEPRECATED_ALLOW /* PoseChannel->bboneflag */ diff --git a/source/blender/makesdna/DNA_boid_types.h b/source/blender/makesdna/DNA_boid_types.h index 882d4eb1b3b..540446ccd9d 100644 --- a/source/blender/makesdna/DNA_boid_types.h +++ b/source/blender/makesdna/DNA_boid_types.h @@ -97,7 +97,8 @@ typedef struct BoidRuleFollowLeader { } BoidRuleFollowLeader; typedef struct BoidRuleAverageSpeed { BoidRule rule; - float wander, level, speed, rt; + float wander, level, speed; + char _pad0[4]; } BoidRuleAverageSpeed; typedef struct BoidRuleFight { BoidRule rule; @@ -178,7 +179,7 @@ typedef struct BoidState { //} BoidSignal; // typedef struct BoidSignalDefine { // struct BoidSignalDefine *next, *prev; -// int id, rt; +// int id, _pad[4]; // char name[32]; //} BoidSignalDefine; diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index a11e7d77c67..986c009ac26 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -133,7 +133,8 @@ typedef struct BrushGpencilSettings { /** Factor to extend stroke extremes using fill tool. */ float fill_extend_fac; - char _pad3[4]; + /** Number of pixels to dilate fill area. */ + int dilate_pixels; struct CurveMapping *curve_sensitivity; struct CurveMapping *curve_strength; diff --git a/source/blender/makesdna/DNA_effect_types.h b/source/blender/makesdna/DNA_effect_types.h index 33f2e1b47c0..307a212a139 100644 --- a/source/blender/makesdna/DNA_effect_types.h +++ b/source/blender/makesdna/DNA_effect_types.h @@ -71,13 +71,15 @@ extern "C" { typedef struct Effect { struct Effect *next, *prev; - short type, flag, buttype, rt; + short type, flag, buttype; + char _pad0[2]; } Effect; typedef struct BuildEff { struct BuildEff *next, *prev; - short type, flag, buttype, rt; + short type, flag, buttype; + char _pad0[2]; float len, sfra; @@ -88,7 +90,8 @@ typedef struct BuildEff { typedef struct Particle { float co[3], no[3]; float time, lifetime; - short mat_nr, rt; + short mat_nr; + char _pad0[2]; } Particle; struct Collection; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index ea03789ddab..b816a45426b 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -184,6 +184,8 @@ .layer_pass = 0, \ .hardeness = 1.0f, \ .curve_intensity = NULL, \ + .fading_end = 10.0f, \ + .fading_end_factor = 0.2f, \ } #define _DNA_DEFAULT_SimplifyGpencilModifierData \ @@ -251,6 +253,8 @@ .thickness_fac = 1.0f, \ .thickness = 30, \ .layer_pass = 0, \ + .fading_end = 10.0f, \ + .fading_end_factor = 0.2f, \ } #define _DNA_DEFAULT_TimeGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 8d7b3896ef9..c03b358dc8a 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -187,7 +187,12 @@ typedef struct ThickGpencilModifierData { int thickness; /** Custom index for passes. */ int layer_pass; - char _pad[4]; + /** Start/end distances of the fading effect. */ + float fading_start; + float fading_end; + float fading_end_factor; + /** Fading reference object */ + struct Object *object; struct CurveMapping *curve_thickness; } ThickGpencilModifierData; @@ -199,6 +204,7 @@ typedef enum eThickGpencil_Flag { GP_THICK_NORMALIZE = (1 << 4), GP_THICK_INVERT_LAYERPASS = (1 << 5), GP_THICK_INVERT_MATERIAL = (1 << 6), + GP_THICK_FADING = (1 << 7), } eThickGpencil_Flag; typedef struct TimeGpencilModifierData { @@ -291,9 +297,16 @@ typedef struct OpacityGpencilModifierData { int flag; /** Main Opacity factor. */ float factor; + /** Fading controlling object */ + int _pad0; + struct Object *object; + /** Start/end distances of the fading effect. */ + float fading_start; + float fading_end; + float fading_end_factor; /** Modify stroke, fill or both. */ char modify_color; - char _pad[3]; + char _pad1[3]; /** Custom index for passes. */ int layer_pass; @@ -309,6 +322,7 @@ typedef enum eOpacityGpencil_Flag { GP_OPACITY_INVERT_MATERIAL = (1 << 5), GP_OPACITY_CUSTOM_CURVE = (1 << 6), GP_OPACITY_NORMALIZE = (1 << 7), + GP_OPACITY_FADING = (1 << 8), } eOpacityGpencil_Flag; typedef struct ArrayGpencilModifierData { @@ -616,6 +630,14 @@ typedef struct OffsetGpencilModifierData { float loc[3]; float rot[3]; float scale[3]; + /** Random Offset. */ + float rnd_offset[3]; + /** Random Rotation. */ + float rnd_rot[3]; + /** Random Scales. */ + float rnd_scale[3]; + /** (first element is the index) random values. */ + int seed; /** Custom index for passes. */ int layer_pass; } OffsetGpencilModifierData; @@ -626,6 +648,7 @@ typedef enum eOffsetGpencil_Flag { GP_OFFSET_INVERT_VGROUP = (1 << 2), GP_OFFSET_INVERT_LAYERPASS = (1 << 3), GP_OFFSET_INVERT_MATERIAL = (1 << 4), + GP_OFFSET_UNIFORM_RANDOM_SCALE = (1 << 5), } eOffsetGpencil_Flag; typedef struct SmoothGpencilModifierData { diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 8facdca2f9c..bf7b9d70fb6 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -47,7 +47,7 @@ struct Curve; #define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2 #define GPENCIL_MIN_FILL_FAC 0.05f -#define GPENCIL_MAX_FILL_FAC 5.0f +#define GPENCIL_MAX_FILL_FAC 8.0f /* ***************************************** */ /* GP Stroke Points */ diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h index 8bb94976414..c5e207c4e20 100644 --- a/source/blender/makesdna/DNA_ipo_types.h +++ b/source/blender/makesdna/DNA_ipo_types.h @@ -77,8 +77,9 @@ typedef struct IpoCurve { short totvert; /** Interpolation and extrapolation modes . */ short ipo, extrap; - /** Flag= settings; rt= ???. */ - short flag, rt; + /** Flag= settings. */ + short flag; + char _pad0[2]; /** Minimum/maximum y-extents for curve. */ float ymin, ymax; /** ???. */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 282d71f6a87..58c94b6f369 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -45,6 +45,8 @@ struct bNodePreview; struct bNodeTreeExec; struct bNodeType; struct uiBlock; +struct Tex; +struct Material; #define NODE_MAXSTR 64 @@ -165,6 +167,8 @@ typedef enum eNodeSocketDatatype { SOCK_IMAGE = 9, SOCK_GEOMETRY = 10, SOCK_COLLECTION = 11, + SOCK_TEXTURE = 12, + SOCK_MATERIAL = 13, } eNodeSocketDatatype; /* socket shape */ @@ -593,6 +597,14 @@ typedef struct bNodeSocketValueCollection { struct Collection *value; } bNodeSocketValueCollection; +typedef struct bNodeSocketValueTexture { + struct Tex *value; +} bNodeSocketValueTexture; + +typedef struct bNodeSocketValueMaterial { + struct Material *value; +} bNodeSocketValueMaterial; + /* data structs, for node->storage */ enum { CMP_NODE_MASKTYPE_ADD = 0, @@ -1184,10 +1196,31 @@ typedef struct NodeAttributeVectorMath { uint8_t input_type_c; } NodeAttributeVectorMath; +typedef struct NodeAttributeVectorRotate { + /* GeometryNodeAttributeVectorRotateMode */ + uint8_t mode; + + /* GeometryNodeAttributeInputMode */ + uint8_t input_type_vector; + uint8_t input_type_center; + uint8_t input_type_axis; + uint8_t input_type_angle; + uint8_t input_type_rotation; + char _pad[2]; +} NodeAttributeVectorRotate; + typedef struct NodeAttributeColorRamp { ColorBand color_ramp; } NodeAttributeColorRamp; +typedef struct NodeAttributeCurveMap { + /* CustomDataType. */ + uint8_t data_type; + char _pad[7]; + CurveMapping *curve_vec; + CurveMapping *curve_rgb; +} NodeAttributeCurveMap; + typedef struct NodeInputVector { float vector[3]; } NodeInputVector; @@ -1280,10 +1313,9 @@ typedef struct NodeAttributeSeparateXYZ { typedef struct NodeAttributeConvert { /* CustomDataType. */ - uint8_t data_type; - char _pad[1]; + int8_t data_type; /* AttributeDomain. */ - int16_t domain; + int8_t domain; } NodeAttributeConvert; typedef struct NodeGeometryMeshCircle { @@ -1308,6 +1340,23 @@ typedef struct NodeGeometryMeshLine { uint8_t count_mode; } NodeGeometryMeshLine; +typedef struct NodeSwitch { + /* NodeSwitch. */ + uint8_t input_type; +} NodeSwitch; + +typedef struct NodeGeometryCurveResample { + /* GeometryNodeCurveSampleMode. */ + uint8_t mode; +} NodeGeometryCurveResample; + +typedef struct NodeGeometryAttributeTransfer { + /* AttributeDomain. */ + int8_t domain; + /* GeometryNodeAttributeTransferMapMode. */ + uint8_t mapping; +} NodeGeometryAttributeTransfer; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1751,6 +1800,14 @@ typedef enum GeometryNodeRotatePointsType { GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1, } GeometryNodeRotatePointsType; +typedef enum GeometryNodeAttributeVectorRotateMode { + GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0, + GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1, + GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y = 2, + GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z = 3, + GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ = 4, +} GeometryNodeAttributeVectorRotateMode; + typedef enum GeometryNodeAttributeRandomizeMode { GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE = 0, GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD = 1, @@ -1802,6 +1859,16 @@ typedef enum GeometryNodeMeshLineCountMode { GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1, } GeometryNodeMeshLineCountMode; +typedef enum GeometryNodeCurveSampleMode { + GEO_NODE_CURVE_SAMPLE_COUNT = 0, + GEO_NODE_CURVE_SAMPLE_LENGTH = 1, +} GeometryNodeCurveSampleMode; + +typedef enum GeometryNodeAttributeTransferMapMode { + GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, + GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, +} GeometryNodeAttributeTransferMapMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h index 37013f5b4d6..3d8418fb734 100644 --- a/source/blender/makesdna/DNA_object_force_types.h +++ b/source/blender/makesdna/DNA_object_force_types.h @@ -182,8 +182,8 @@ typedef struct EffectorWeights { /** Effector type specific weights. */ float weight[14]; float global_gravity; - short flag, rt[3]; - char _pad[4]; + short flag; + char _pad[2]; } EffectorWeights; /* EffectorWeights->flag */ @@ -267,10 +267,9 @@ typedef struct SoftBody { char namedVG_Spring_K[64]; /* baking */ - int sfra, efra; - int interval; + char _pad1[6]; /** Local==1: use local coords for baking. */ - short local, solverflags; + char local, solverflags; /* -- these must be kept for backwards compatibility -- */ /** Array of size totpointkey. */ diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index cc40e26b92b..30b1fbe09d3 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -64,7 +64,7 @@ typedef struct BoidParticle { struct BoidData data; float gravity[3]; float wander[3]; - float rt; + char _pad0[4]; } BoidParticle; typedef struct ParticleSpring { @@ -82,7 +82,7 @@ typedef struct ChildParticle { float w[4]; /** Face vertex weights and offset. */ float fuv[4], foffset; - float rt; + char _pad0[4]; } ChildParticle; typedef struct ParticleTarget { @@ -99,7 +99,8 @@ typedef struct ParticleDupliWeight { short count; short flag; /** Only updated on file save and used on file load. */ - short index, rt; + short index; + char _pad0[2]; } ParticleDupliWeight; typedef struct ParticleData { @@ -191,7 +192,8 @@ typedef struct ParticleSettings { struct EffectorWeights *effector_weights; struct Collection *collision_group; - int flag, rt; + int flag; + char _pad1[4]; short type, from, distr, texact; /* physics modes */ short phystype, rotmode, avemode, reactevent; diff --git a/source/blender/makesdna/DNA_pointcache_types.h b/source/blender/makesdna/DNA_pointcache_types.h index de2fa3f10fe..ad5f386bf2b 100644 --- a/source/blender/makesdna/DNA_pointcache_types.h +++ b/source/blender/makesdna/DNA_pointcache_types.h @@ -107,7 +107,8 @@ typedef struct PointCache { int totpoint; /** Modifier stack index. */ int index; - short compression, rt; + short compression; + char _pad0[2]; char name[64]; char prev_name[64]; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index c7f7e610a1a..0b07b8271a5 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -952,7 +952,8 @@ typedef struct ParticleEditSettings { /** Runtime. */ void *paintcursor; - float emitterdist, rt; + float emitterdist; + char _pad0[4]; int selectmode; int edittype; @@ -1550,7 +1551,8 @@ typedef struct UnitSettings { typedef struct PhysicsSettings { float gravity[3]; - int flag, quick_cache_step, rt; + int flag, quick_cache_step; + char _pad0[4]; } PhysicsSettings; /* ------------------------------------------- */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index fa11a7dfd13..46ac4435e95 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -521,7 +521,7 @@ enum { SEQ_SCENE_NO_GPENCIL = (1 << 28), SEQ_USE_VIEWS = (1 << 29), - /* access scene strips directly (like a metastrip) */ + /* Access scene strips directly (like a meta-strip). */ SEQ_SCENE_STRIPS = (1 << 30), SEQ_INVALID_EFFECT = (1u << 31), diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index ce3875dfbc7..9f3576a2cbe 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1909,7 +1909,7 @@ typedef struct SpaceSpreadsheet { /** * List of #SpreadsheetContext. * This is a path to the data that is displayed in the spreadsheet. - * It can be set explicitely by an action of the user (e.g. clicking the preview icon in a + * It can be set explicitly by an action of the user (e.g. clicking the preview icon in a * geometry node) or it can be derived from context automatically based on some heuristic. */ ListBase context_path; diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index 5381c524e4d..60c255e8637 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -57,7 +57,8 @@ typedef struct MTex { short colormodel, pmapto, pmaptoneg; short normapspace, which_output; float r, g, b, k; - float def_var, rt; + float def_var; + char _pad1[4]; /* common */ float colfac, varfac; diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index 2ce32a723a7..8e63760fef7 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -58,6 +58,21 @@ typedef enum eXRSessionBasePoseType { XR_BASE_POSE_CUSTOM = 2, } eXRSessionBasePoseType; +/** XR action type. Enum values match those in GHOST_XrActionType enum for consistency. */ +typedef enum eXrActionType { + XR_BOOLEAN_INPUT = 1, + XR_FLOAT_INPUT = 2, + XR_VECTOR2F_INPUT = 3, + XR_POSE_INPUT = 4, + XR_VIBRATION_OUTPUT = 100, +} eXrActionType; + +typedef enum eXrOpFlag { + XR_OP_PRESS = 0, + XR_OP_RELEASE = 1, + XR_OP_MODAL = 2, +} eXrOpFlag; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 54e077c624c..39a46948321 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -845,6 +845,7 @@ const char *RNA_property_description(PropertyRNA *prop); PropertyType RNA_property_type(PropertyRNA *prop); PropertySubType RNA_property_subtype(PropertyRNA *prop); PropertyUnit RNA_property_unit(PropertyRNA *prop); +PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop); int RNA_property_flag(PropertyRNA *prop); int RNA_property_override_flag(PropertyRNA *prop); int RNA_property_tags(PropertyRNA *prop); diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index 17309d847bd..a31182b2f5a 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -388,6 +388,7 @@ void RNA_def_property_string_default(PropertyRNA *prop, const char *value); void RNA_def_property_ui_text(PropertyRNA *prop, const char *name, const char *description); void RNA_def_property_ui_range( PropertyRNA *prop, double min, double max, double step, int precision); +void RNA_def_property_ui_scale_type(PropertyRNA *prop, PropertyScaleType scale_type); void RNA_def_property_ui_icon(PropertyRNA *prop, int icon, int consecutive); void RNA_def_property_update(PropertyRNA *prop, int noteflag, const char *updatefunc); diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index b0895609e9a..71af69f77a1 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -237,6 +237,7 @@ extern const EnumPropertyItem rna_enum_curveprofile_preset_items[]; extern const EnumPropertyItem rna_enum_preference_section_items[]; extern const EnumPropertyItem rna_enum_attribute_type_items[]; +extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[]; extern const EnumPropertyItem rna_enum_attribute_domain_items[]; extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[]; extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free); diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 6cd3678017e..4a6d6dddec7 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -95,6 +95,32 @@ typedef enum PropertyUnit { PROP_UNIT_TEMPERATURE = (11 << 16), /* C */ } PropertyUnit; +/** + * Use values besides #PROP_SCALE_LINEAR + * so the movement of the mouse doesn't map linearly to the value of the slider. + * + * For some settings it's useful to space motion in a non-linear way, see T77868. + * + * NOTE: The scale types are available for all float sliders. + * For integer sliders they are only available if they use the visible value bar. + * Sliders with logarithmic scale and value bar must have a range > 0 + * while logarithmic sliders without the value bar can have a range of >= 0. + */ +typedef enum PropertyScaleType { + /** Linear scale (default). */ + PROP_SCALE_LINEAR = 0, + /** + * Logarithmic scale + * - Maximum range: `0 <= x < inf` + */ + PROP_SCALE_LOG = 1, + /** + * Cubic scale. + * - Maximum range: `-inf < x < inf` + */ + PROP_SCALE_CUBIC = 2, +} PropertyScaleType; + #define RNA_SUBTYPE_UNIT(subtype) ((subtype)&0x00FF0000) #define RNA_SUBTYPE_VALUE(subtype) ((subtype) & ~0x00FF0000) #define RNA_SUBTYPE_UNIT_VALUE(subtype) ((subtype) >> 16) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index b20d9218316..ef65a99c73b 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -684,6 +684,29 @@ static char *rna_def_property_get_func( } } } + + /* Check log scale sliders for negative range. */ + if (prop->type == PROP_FLOAT) { + FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop; + /* NOTE: UI_BTYPE_NUM_SLIDER can't have a softmin of zero. */ + if ((fprop->ui_scale_type == PROP_SCALE_LOG) && (fprop->hardmin < 0 || fprop->softmin < 0)) { + CLOG_ERROR( + &LOG, "\"%s.%s\", range for log scale < 0.", srna->identifier, prop->identifier); + DefRNA.error = true; + return NULL; + } + } + if (prop->type == PROP_INT) { + IntPropertyRNA *iprop = (IntPropertyRNA *)prop; + /* Only UI_BTYPE_NUM_SLIDER is implemented and that one can't have a softmin of zero. */ + if ((iprop->ui_scale_type == PROP_SCALE_LOG) && + (iprop->hardmin <= 0 || iprop->softmin <= 0)) { + CLOG_ERROR( + &LOG, "\"%s.%s\", range for log scale <= 0.", srna->identifier, prop->identifier); + DefRNA.error = true; + return NULL; + } + } } func = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "get"); @@ -3935,6 +3958,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr rna_function_string(iprop->getarray_ex), rna_function_string(iprop->setarray_ex), rna_function_string(iprop->range_ex)); + rna_int_print(f, iprop->ui_scale_type); + fprintf(f, ", "); rna_int_print(f, iprop->softmin); fprintf(f, ", "); rna_int_print(f, iprop->softmax); @@ -3969,6 +3994,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr rna_function_string(fprop->getarray_ex), rna_function_string(fprop->setarray_ex), rna_function_string(fprop->range_ex)); + rna_float_print(f, fprop->ui_scale_type); + fprintf(f, ", "); rna_float_print(f, fprop->softmin); fprintf(f, ", "); rna_float_print(f, fprop->softmax); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 657af98f5fe..4fe19493614 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -148,6 +148,8 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items # include "DEG_depsgraph_build.h" # include "DEG_depsgraph_query.h" +# include "ED_asset.h" + # include "WM_api.h" void rna_ID_override_library_property_operation_refname_get(PointerRNA *ptr, char *value) @@ -575,6 +577,22 @@ static ID *rna_ID_copy(ID *id, Main *bmain) return newid; } +static void rna_ID_asset_mark(ID *id, bContext *C) +{ + if (ED_asset_mark_id(C, id)) { + WM_main_add_notifier(NC_ID | NA_EDITED, NULL); + WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL); + } +} + +static void rna_ID_asset_clear(ID *id) +{ + if (ED_asset_clear_id(id)) { + WM_main_add_notifier(NC_ID | NA_EDITED, NULL); + WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL); + } +} + static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) { if (!ID_IS_OVERRIDABLE_LIBRARY(id)) { @@ -1716,6 +1734,18 @@ static void rna_def_ID(BlenderRNA *brna) parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID"); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "asset_mark", "rna_ID_asset_mark"); + RNA_def_function_ui_description( + func, + "Enable easier reuse of the data-block through the Asset Browser, with the help of " + "customizable metadata (like previews, descriptions and tags)"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + + func = RNA_def_function(srna, "asset_clear", "rna_ID_asset_clear"); + RNA_def_function_ui_description( + func, + "Delete all asset metadata and turn the asset data-block back into a normal data-block"); + func = RNA_def_function(srna, "override_create", "rna_ID_override_create"); RNA_def_function_ui_description(func, "Create an overridden local copy of this linked data-block (not " diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 9b57096ec19..150a455f1c7 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1189,6 +1189,24 @@ PropertyUnit RNA_property_unit(PropertyRNA *prop) return RNA_SUBTYPE_UNIT(RNA_property_subtype(prop)); } +PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop) +{ + PropertyRNA *rna_prop = rna_ensure_property(prop); + + switch (rna_prop->type) { + case PROP_INT: { + IntPropertyRNA *iprop = (IntPropertyRNA *)rna_prop; + return iprop->ui_scale_type; + } + case PROP_FLOAT: { + FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_prop; + return fprop->ui_scale_type; + } + default: + return PROP_SCALE_LINEAR; + } +} + int RNA_property_flag(PropertyRNA *prop) { return rna_ensure_property(prop)->flag; diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 7976df3e4e4..a256002ffc1 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -49,6 +49,19 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { + {CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""}, + {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"}, + {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"}, + {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"}, + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point precisions"}, + {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"}, + {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, + {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, + {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_attribute_domain_items[] = { /* Not implement yet */ // {ATTR_DOMAIN_GEOMETRY, "GEOMETRY", 0, "Geometry", "Attribute on (whole) geometry"}, @@ -58,7 +71,7 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = { {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"}, /* Not implement yet */ // {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"}, - {ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", "Attribute on hair curve"}, + {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"}, {0, NULL, 0, NULL, NULL}, }; @@ -68,6 +81,7 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = { {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"}, {ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"}, {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"}, + {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index e7daa55af6c..7e1d513502c 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1617,6 +1617,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna) prop, "Stroke Extension", "Strokes end extension for closing gaps, use zero to disable"); RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + /* Number of pixels to dilate fill area. */ + prop = RNA_def_property(srna, "dilate", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "dilate_pixels"); + RNA_def_property_range(prop, 0, 20); + RNA_def_property_int_default(prop, 1); + RNA_def_property_ui_text(prop, "Dilate", "Number of pixels to dilate fill area"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + /* Flags */ prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 2fdf7e5eaa7..9b9d561603b 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -1754,6 +1754,28 @@ void RNA_def_property_ui_range( } } +void RNA_def_property_ui_scale_type(PropertyRNA *prop, PropertyScaleType ui_scale_type) +{ + StructRNA *srna = DefRNA.laststruct; + + switch (prop->type) { + case PROP_INT: { + IntPropertyRNA *iprop = (IntPropertyRNA *)prop; + iprop->ui_scale_type = ui_scale_type; + break; + } + case PROP_FLOAT: { + FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop; + fprop->ui_scale_type = ui_scale_type; + break; + } + default: + CLOG_ERROR(&LOG, "\"%s.%s\", invalid type for scale.", srna->identifier, prop->identifier); + DefRNA.error = true; + break; + } +} + void RNA_def_property_range(PropertyRNA *prop, double min, double max) { StructRNA *srna = DefRNA.laststruct; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index d4697721bc2..1b89d866aba 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1160,6 +1160,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna) /* define common props */ prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Additive", "Values generated by this modifier are applied on top of " @@ -1168,12 +1169,14 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna) prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, generator_mode_items); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Mode", "Type of generator to use"); RNA_def_property_update( prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_verify_data_update"); /* order of the polynomial */ prop = RNA_def_property(srna, "poly_order", PROP_INT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Polynomial Order", @@ -1184,6 +1187,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna) /* coefficients array */ prop = RNA_def_property(srna, "coefficients", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_array(prop, 32); RNA_def_property_flag(prop, PROP_DYNAMIC); RNA_def_property_dynamic_array_funcs(prop, "rna_FModifierGenerator_coefficients_get_length"); @@ -1221,25 +1225,30 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna) /* coefficients */ prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Amplitude", "Scale factor determining the maximum/minimum values"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "phase_multiplier", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Phase Multiple", "Scale factor determining the 'speed' of the function"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "phase_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Phase Offset", "Constant factor to offset time by for function"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "value_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Value Offset", "Constant factor to offset values by"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); /* flags */ prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE); RNA_def_property_ui_text(prop, "Additive", @@ -1248,6 +1257,7 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna) RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "function_type", PROP_ENUM, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "Type", "Type of built-in function to use"); @@ -1271,17 +1281,20 @@ static void rna_def_fmodifier_envelope_ctrl(BlenderRNA *brna) */ prop = RNA_def_property(srna, "min", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "min"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Minimum Value", "Lower bound of envelope at this control-point"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "max", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "max"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Maximum Value", "Upper bound of envelope at this control-point"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); /* Frame */ prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "time"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Frame", "Frame this control-point occurs on"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); @@ -1340,6 +1353,7 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna) /* Collections */ prop = RNA_def_property(srna, "control_points", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "data", "totvert"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_struct_type(prop, "FModifierEnvelopeControlPoint"); RNA_def_property_ui_text( prop, "Control Points", "Control points defining the shape of the envelope"); @@ -1348,18 +1362,21 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna) /* Range Settings */ prop = RNA_def_property(srna, "reference_value", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "midval"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Reference Value", "Value that envelope's influence is centered around / based on"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "default_min", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "min"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Default Minimum", "Lower distance from Reference Value for 1:1 default influence"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "default_max", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "max"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Default Maximum", "Upper distance from Reference Value for 1:1 default influence"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); @@ -1395,12 +1412,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna) /* before */ prop = RNA_def_property(srna, "mode_before", PROP_ENUM, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_enum_sdna(prop, NULL, "before_mode"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "Before Mode", "Cycling mode to use before first keyframe"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "cycles_before", PROP_INT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_int_sdna(prop, NULL, "before_cycles"); RNA_def_property_ui_text( prop, @@ -1410,12 +1429,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna) /* after */ prop = RNA_def_property(srna, "mode_after", PROP_ENUM, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_enum_sdna(prop, NULL, "after_mode"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "After Mode", "Cycling mode to use after last keyframe"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "cycles_after", PROP_INT, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_int_sdna(prop, NULL, "after_cycles"); RNA_def_property_ui_text(prop, "After Cycles", @@ -1450,26 +1471,31 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "use_min_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMIN); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Minimum X", "Use the minimum X value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_min_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMIN); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Minimum Y", "Use the minimum Y value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_max_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Maximum X", "Use the maximum X value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_max_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Maximum Y", "Use the maximum Y value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "min_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.xmin"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierLimits_minx_set", "rna_FModifierLimits_minx_range"); RNA_def_property_ui_text(prop, "Minimum X", "Lowest X value to allow"); @@ -1477,6 +1503,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "min_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.ymin"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierLimits_miny_set", "rna_FModifierLimits_miny_range"); RNA_def_property_ui_text(prop, "Minimum Y", "Lowest Y value to allow"); @@ -1484,6 +1511,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "max_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.xmax"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierLimits_maxx_set", "rna_FModifierLimits_maxx_range"); RNA_def_property_ui_text(prop, "Maximum X", "Highest X value to allow"); @@ -1491,6 +1519,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "max_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.ymax"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierLimits_maxy_set", "rna_FModifierLimits_maxy_range"); RNA_def_property_ui_text(prop, "Maximum Y", "Highest Y value to allow"); @@ -1519,16 +1548,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna) prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "modification"); RNA_def_property_enum_items(prop, prop_modification_items); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Blend Type", "Method of modifying the existing F-Curve"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "size"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Scale", "Scaling (in time) of the noise"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "strength"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Strength", @@ -1537,16 +1569,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna) prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "phase"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Phase", "A random seed for the noise effect"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "offset"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Offset", "Time offset for the noise effect"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "depth", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "depth"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Depth", "Amount of fine level detail present in the noise"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); } @@ -1569,11 +1604,13 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna) /* properties */ prop = RNA_def_property(srna, "frame_step", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "step_size"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Step Size", "Number of frames to hold each value"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "frame_offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "offset"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Offset", "Reference number of frames before frames get held " @@ -1582,18 +1619,21 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna) prop = RNA_def_property(srna, "use_frame_start", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_BEFORE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Use Start Frame", "Restrict modifier to only act after its 'start' frame"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_frame_end", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_AFTER); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Use End Frame", "Restrict modifier to only act before its 'end' frame"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "start_frame"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs(prop, NULL, "rna_FModifierStepped_frame_start_set", @@ -1604,6 +1644,7 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna) prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "end_frame"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifierStepped_frame_end_set", "rna_FModifierStepped_end_frame_range"); RNA_def_property_ui_text( @@ -1641,12 +1682,14 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0); + // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_show_expanded_set"); RNA_def_property_ui_text(prop, "Expanded", "F-Curve Modifier's panel is expanded in UI"); RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1); prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_MUTED); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Enabled", "Enable F-Curve modifier evaluation"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1); @@ -1654,6 +1697,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FMODIFIER_FLAG_DISABLED); + // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Disabled", "F-Curve Modifier has invalid settings and will not be evaluated"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); @@ -1661,6 +1705,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) /* TODO: setting this to true must ensure that all others in stack are turned off too... */ prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_ACTIVE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Active", "F-Curve modifier will show settings in the editor"); RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_active_set"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_active_update"); @@ -1669,6 +1714,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) /* restricted range */ prop = RNA_def_property(srna, "use_restricted_range", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_RANGERESTRICT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Restrict Frame Range", @@ -1678,6 +1724,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "sfra"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifier_start_frame_set", "rna_FModifier_start_frame_range"); RNA_def_property_ui_text( @@ -1688,6 +1735,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "efra"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs( prop, NULL, "rna_FModifier_end_frame_set", "rna_FModifier_end_frame_range"); RNA_def_property_ui_text( @@ -1698,6 +1746,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendin"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range"); RNA_def_property_ui_text( prop, "Blend In", "Number of frames from start frame for influence to take effect"); @@ -1705,6 +1754,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendout"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range"); RNA_def_property_ui_text( prop, "Blend Out", "Number of frames from end frame for influence to fade out"); @@ -1713,12 +1763,14 @@ static void rna_def_fmodifier(BlenderRNA *brna) /* influence */ prop = RNA_def_property(srna, "use_influence", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_USEINFLUENCE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text( prop, "Use Influence", "F-Curve Modifier's effects will be tempered by a default factor"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "influence"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_float_default(prop, 1.0f); RNA_def_property_ui_text( @@ -2162,6 +2214,7 @@ static void rna_def_fcurve_modifiers(BlenderRNA *brna, PropertyRNA *cprop) /* Collection active property */ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "FModifier"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_pointer_funcs( prop, "rna_FCurve_active_modifier_get", "rna_FCurve_active_modifier_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 1fbbffa99d5..91e13a4bee3 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -1037,7 +1037,7 @@ static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src) static bGPDlayer *rna_GPencil_layer_new(bGPdata *gpd, const char *name, bool setactive) { - bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0); + bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0, false); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -2571,7 +2571,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Auto-Lock Layers", - "Lock automatically all layers except active one to avoid accidental changes"); + "Automatically lock all layers except the active one to avoid accidental changes"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_autolock"); prop = RNA_def_property(srna, "edit_line_color", PROP_FLOAT, PROP_COLOR_GAMMA); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 03a55c5a7da..85a3bd2192c 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -351,6 +351,8 @@ static void greasepencil_modifier_object_set(Object *self, RNA_GP_MOD_OBJECT_SET(Armature, object, OB_ARMATURE); RNA_GP_MOD_OBJECT_SET(Lattice, object, OB_LATTICE); RNA_GP_MOD_OBJECT_SET(Mirror, object, OB_EMPTY); +RNA_GP_MOD_OBJECT_SET(Opacity, object, OB_EMPTY); +RNA_GP_MOD_OBJECT_SET(Thick, object, OB_EMPTY); # undef RNA_GP_MOD_OBJECT_SET @@ -1116,6 +1118,38 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Thickness Factor", "Factor to multiply the thickness with"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_FADING); + RNA_def_property_ui_text(prop, "Fading", "Fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Distance reference object */ + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Object", "Object used as distance reference"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_ThickGpencilModifier_object_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_start"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_end"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_end_factor"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3); + RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); @@ -1256,6 +1290,34 @@ static void rna_def_modifier_gpenciloffset(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Scale", "Values for changes in scale"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "random_offset", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "rnd_offset"); + RNA_def_property_ui_text(prop, "Random Offset", "Value for changes in location"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, NULL, "rnd_rot"); + RNA_def_property_ui_text(prop, "Random Rotation", "Value for changes in rotation"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "random_scale", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "rnd_scale"); + RNA_def_property_ui_text(prop, "Scale", "Value for changes in scale"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(prop, "Seed", "Random seed"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_uniform_random_scale", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OFFSET_UNIFORM_RANDOM_SCALE); + RNA_def_property_ui_text( + prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } static void rna_def_modifier_gpenciltint(BlenderRNA *brna) @@ -1625,6 +1687,38 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Hardness", "Factor of stroke hardness"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_FADING); + RNA_def_property_ui_text(prop, "Fading", "Fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Distance reference object */ + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Object", "Object used as distance reference"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_OpacityGpencilModifier_object_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_start"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_end"); + RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fading_end_factor"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3); + RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 0c0260c889c..245730919b0 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -400,6 +400,7 @@ typedef struct IntPropertyRNA { PropIntArraySetFuncEx setarray_ex; PropIntRangeFuncEx range_ex; + PropertyScaleType ui_scale_type; int softmin, softmax; int hardmin, hardmax; int step; @@ -423,6 +424,7 @@ typedef struct FloatPropertyRNA { PropFloatArraySetFuncEx setarray_ex; PropFloatRangeFuncEx range_ex; + PropertyScaleType ui_scale_type; float softmin, softmax; float hardmin, hardmax; float step; diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index e3fb443951f..674e5845ccb 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -2781,7 +2781,8 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) prop = RNA_def_property(srna, "double_threshold", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "double_threshold"); RNA_def_property_range(prop, 0, 1.0f); - RNA_def_property_ui_range(prop, 0, 1, 0.0001, 6); + RNA_def_property_ui_range(prop, 0, 1, 1.0, 6); + RNA_def_property_ui_scale_type(prop, PROP_SCALE_LOG); RNA_def_property_ui_text( prop, "Overlap Threshold", "Threshold for checking overlapping geometry"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); @@ -5530,6 +5531,7 @@ static void rna_def_modifier_remesh(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "voxel_size"); RNA_def_property_range(prop, 0.0001f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0001, 2, 0.1, 3); + RNA_def_property_ui_scale_type(prop, PROP_SCALE_LOG); RNA_def_property_ui_text(prop, "Voxel Size", "Size of the voxel in object space used for volume evaluation. Lower " diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index c9520c939f4..f8fa2aab5e7 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -402,7 +402,7 @@ static void rna_def_movieclip(BlenderRNA *brna) "Start Frame", "Global scene frame number at which this movie starts playing " "(affects all data associated with a clip)"); - RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); /* frame_offset */ prop = RNA_def_property(srna, "frame_offset", PROP_INT, PROP_NONE); @@ -412,7 +412,7 @@ static void rna_def_movieclip(BlenderRNA *brna) "Frame Offset", "Offset of footage first frame relative to its file name " "(affects only how footage is loading, does not change data associated with a clip)"); - RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); /* length */ prop = RNA_def_property(srna, "frame_duration", PROP_INT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b0254ce2ef3..a1b6885e7e2 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -65,6 +65,22 @@ const EnumPropertyItem rna_enum_node_socket_in_out_items[] = { {SOCK_IN, "IN", 0, "Input", ""}, {SOCK_OUT, "OUT", 0, "Output", ""}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem node_socket_data_type_items[] = { + {SOCK_FLOAT, "FLOAT", 0, "Float", ""}, + {SOCK_INT, "INT", 0, "Integer", ""}, + {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""}, + {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, + {SOCK_STRING, "STRING", 0, "String", ""}, + {SOCK_RGBA, "RGBA", 0, "Color", ""}, + {SOCK_OBJECT, "OBJECT", 0, "Object", ""}, + {SOCK_IMAGE, "IMAGE", 0, "Image", ""}, + {SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""}, + {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""}, + {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""}, + {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifndef RNA_RUNTIME static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = { {SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""}, @@ -78,7 +94,7 @@ static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = { static const EnumPropertyItem node_socket_type_items[] = { {SOCK_CUSTOM, "CUSTOM", 0, "Custom", ""}, {SOCK_FLOAT, "VALUE", 0, "Value", ""}, - {SOCK_INT, "INT", 0, "Int", ""}, + {SOCK_INT, "INT", 0, "Integer", ""}, {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""}, {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, {SOCK_STRING, "STRING", 0, "String", ""}, @@ -88,6 +104,8 @@ static const EnumPropertyItem node_socket_type_items[] = { {SOCK_IMAGE, "IMAGE", 0, "Image", ""}, {SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""}, {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""}, + {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""}, + {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -957,6 +975,32 @@ static void rna_NodeTree_get_from_context( RNA_parameter_list_free(&list); } +static bool rna_NodeTree_valid_socket_type(eNodeSocketDatatype socket_type, + bNodeTreeType *ntreetype) +{ + extern FunctionRNA rna_NodeTree_valid_socket_type_func; + + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + void *ret; + bool valid; + + RNA_pointer_create(NULL, ntreetype->rna_ext.srna, NULL, &ptr); /* dummy */ + func = &rna_NodeTree_valid_socket_type_func; + + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "type", &socket_type); + ntreetype->rna_ext.call(NULL, &ptr, func, &list); + + RNA_parameter_get_lookup(&list, "valid", &ret); + valid = *(bool *)ret; + + RNA_parameter_list_free(&list); + + return valid; +} + static void rna_NodeTree_unregister(Main *UNUSED(bmain), StructRNA *type) { bNodeTreeType *nt = RNA_struct_blender_type_get(type); @@ -985,7 +1029,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain, bNodeTreeType *nt, dummynt; bNodeTree dummyntree; PointerRNA dummyptr; - int have_function[3]; + int have_function[4]; /* setup dummy tree & tree type to store static properties in */ memset(&dummynt, 0, sizeof(bNodeTreeType)); @@ -1031,6 +1075,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain, nt->poll = (have_function[0]) ? rna_NodeTree_poll : NULL; nt->update = (have_function[1]) ? rna_NodeTree_update_reg : NULL; nt->get_from_context = (have_function[2]) ? rna_NodeTree_get_from_context : NULL; + nt->valid_socket_type = (have_function[3]) ? rna_NodeTree_valid_socket_type : NULL; ntreeTypeAdd(nt); @@ -1913,6 +1958,29 @@ static const EnumPropertyItem *itemf_function_check( return item_array; } +static bool switch_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, + SOCK_FLOAT, + SOCK_INT, + SOCK_BOOLEAN, + SOCK_VECTOR, + SOCK_STRING, + SOCK_RGBA, + SOCK_GEOMETRY, + SOCK_OBJECT, + SOCK_COLLECTION); +} + +static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + *r_free = true; + return itemf_function_check(node_socket_data_type_items, switch_type_supported); +} + static bool attribute_clamp_type_supported(const EnumPropertyItem *item) { return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR); @@ -1983,6 +2051,7 @@ static void rna_GeometryNodeAttributeRandomize_data_type_update(Main *bmain, static bool attribute_convert_type_supported(const EnumPropertyItem *item) { return ELEM(item->value, + CD_AUTO_FROM_NAME, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, @@ -1994,7 +2063,8 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeConvert_type_itemf( bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { *r_free = true; - return itemf_function_check(rna_enum_attribute_type_items, attribute_convert_type_supported); + return itemf_function_check(rna_enum_attribute_type_with_auto_items, + attribute_convert_type_supported); } static bool attribute_fill_type_supported(const EnumPropertyItem *item) @@ -2116,6 +2186,17 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeMapRange_type_itemf( return itemf_function_check(rna_enum_attribute_type_items, attribute_map_range_type_supported); } +static bool attribute_curve_map_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR); +} +static const EnumPropertyItem *rna_GeometryNodeAttributeCurveMap_type_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, attribute_curve_map_type_supported); +} + static StructRNA *rna_ShaderNode_register(Main *bmain, ReportList *reports, void *data, @@ -8953,9 +9034,9 @@ static void def_geo_attribute_convert(StructRNA *srna) RNA_def_struct_sdna_from(srna, "NodeAttributeConvert", "storage"); prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_with_auto_items); RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeConvert_type_itemf"); - RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_enum_default(prop, CD_AUTO_FROM_NAME); RNA_def_property_ui_text(prop, "Data Type", "The data type to save the result attribute with"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -9196,6 +9277,85 @@ static void def_geo_attribute_color_ramp(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_attribute_curve_map(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeAttributeCurveMap", "storage"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "data_type"); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeCurveMap_type_itemf"); + RNA_def_property_ui_text(prop, "Data Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "curve_vec", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Mapping", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "curve_rgb", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Mapping", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_attribute_vector_rotate(StructRNA *srna) +{ + static const EnumPropertyItem rotate_mode_items[] = { + {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS, + "AXIS_ANGLE", + 0, + "Axis Angle", + "Rotate a point using axis angle"}, + {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X, "X_AXIS", 0, "X Axis", "Rotate a point using X axis"}, + {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y, "Y_AXIS", 0, "Y Axis", "Rotate a point using Y axis"}, + {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z, "Z_AXIS", 0, "Z Axis", "Rotate a point using Z axis"}, + {GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ, + "EULER_XYZ", + 0, + "Euler", + "Rotate a point using XYZ order"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeAttributeVectorRotate", "storage"); + + prop = RNA_def_property(srna, "rotation_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, rotate_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Type of rotation"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); + + prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); + RNA_def_property_ui_text(prop, "Input Type Vector", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_center", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); + RNA_def_property_ui_text(prop, "Input Type Center", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); + RNA_def_property_ui_text(prop, "Input Type Axis", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_angle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text(prop, "Input Type Angle", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_rotation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); + RNA_def_property_ui_text(prop, "Input Type Rotation", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_point_rotate(StructRNA *srna) { static const EnumPropertyItem type_items[] = { @@ -9350,19 +9510,6 @@ static void def_geo_point_translate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_attribute_sample_texture(StructRNA *srna) -{ - PropertyRNA *prop; - - prop = RNA_def_property(srna, "texture", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "id"); - RNA_def_property_struct_type(prop, "Texture"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_ui_text(prop, "Texture", "Texture to sample values from"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations"); -} - static void def_geo_object_info(StructRNA *srna) { PropertyRNA *prop; @@ -9630,6 +9777,80 @@ static void def_geo_mesh_line(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_switch(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeSwitch", "storage"); + prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "input_type"); + RNA_def_property_enum_items(prop, node_socket_data_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeSwitch_type_itemf"); + RNA_def_property_ui_text(prop, "Input Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_curve_resample(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_SAMPLE_COUNT, + "COUNT", + 0, + "Count", + "Sample the specified number of points along each spline"}, + {GEO_NODE_CURVE_SAMPLE_LENGTH, + "LENGTH", + 0, + "Length", + "Calculate the number of samples by splitting each spline into segments with the specified " + "length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveResample", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to specify the amount of samples"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_attribute_transfer(StructRNA *srna) +{ + static EnumPropertyItem mapping_items[] = { + {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, + "NEAREST_FACE_INTERPOLATED", + 0, + "Nearest Face Interpolated", + "Transfer the attribute from the nearest face on a surface (loose points and edges are " + "ignored)"}, + {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST, + "NEAREST", + 0, + "Nearest", + "Transfer the element from the nearest element (using face and edge centers for the " + "distance computation)"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeTransfer", "storage"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO); + RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mapping_items); + RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) @@ -10402,6 +10623,76 @@ static void rna_def_node_socket_collection(BlenderRNA *brna, RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); } +static void rna_def_node_socket_texture(BlenderRNA *brna, + const char *identifier, + const char *interface_idname) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Texture Node Socket", "Texture socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "value"); + RNA_def_property_struct_type(prop, "Texture"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + + /* socket interface */ + srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); + RNA_def_struct_ui_text(srna, "Texture Node Socket Interface", "Texture socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "value"); + RNA_def_property_struct_type(prop, "Texture"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); +} + +static void rna_def_node_socket_material(BlenderRNA *brna, + const char *identifier, + const char *interface_idname) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Material Node Socket", "Material socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "value"); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + + /* socket interface */ + srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); + RNA_def_struct_ui_text(srna, "Material Node Socket Interface", "Material socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "value"); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); +} + static void rna_def_node_socket_standard_types(BlenderRNA *brna) { /* XXX Workaround: Registered functions are not exposed in python by bpy, @@ -10546,6 +10837,10 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna) rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry"); rna_def_node_socket_collection(brna, "NodeSocketCollection", "NodeSocketInterfaceCollection"); + + rna_def_node_socket_texture(brna, "NodeSocketTexture", "NodeSocketInterfaceTexture"); + + rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial"); } static void rna_def_internal_node(BlenderRNA *brna) @@ -11330,6 +11625,14 @@ static void rna_def_nodetree(BlenderRNA *brna) parm = RNA_def_pointer( func, "result_3", "ID", "From ID", "Original ID data-block selected from the context"); RNA_def_function_output(func, parm); + + /* Check for support of a socket type. */ + func = RNA_def_function(srna, "valid_socket_type", NULL); + RNA_def_function_ui_description(func, "Check if the socket type is valid for the node tree"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL); + parm = RNA_def_enum(func, "type", node_socket_type_items, 0, "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + RNA_def_function_return(func, RNA_def_boolean(func, "valid", false, "", "")); } static void rna_def_composite_nodetree(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 6b93a1c223c..3edfd5c44f4 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -84,7 +84,7 @@ const EnumPropertyItem rna_enum_object_mode_items[] = { {OB_MODE_PAINT_GPENCIL, "PAINT_GPENCIL", ICON_GREASEPENCIL, - "Draw", + "Draw Mode", "Paint Grease Pencil Strokes"}, {OB_MODE_WEIGHT_GPENCIL, "WEIGHT_GPENCIL", diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index ba65e42895c..b8bb4f58dcd 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -1359,12 +1359,27 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_dependency_update"); - prop = RNA_def_property(srna, "custom_shape_scale", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "custom_scale"); - RNA_def_property_range(prop, 0.0f, 1000.0f); + prop = RNA_def_property(srna, "custom_shape_scale_xyz", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "custom_scale_xyz"); + RNA_def_property_flag(prop, PROP_PROPORTIONAL); + RNA_def_property_float_array_default(prop, rna_default_scale_3d); RNA_def_property_ui_text(prop, "Custom Shape Scale", "Adjust the size of the custom shape"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + prop = RNA_def_property(srna, "custom_shape_translation", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "custom_translation"); + RNA_def_property_flag(prop, PROP_PROPORTIONAL); + RNA_def_property_ui_text( + prop, "Custom Shape Translation", "Adjust the location of the custom shape"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "custom_shape_rotation_euler", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, NULL, "custom_rotation_euler"); + RNA_def_property_ui_text( + prop, "Custom Shape Rotation", "Adjust the rotation of the custom shape"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + prop = RNA_def_property(srna, "use_custom_shape_bone_size", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "drawflag", PCHAN_DRAW_NO_CUSTOM_BONE_SIZE); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 4481555b931..81acedb76f2 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -786,7 +786,8 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_ShowMask_update"); prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_PIXEL); - RNA_def_property_ui_range(prop, 0.5, 40.0, 10, 2); + RNA_def_property_ui_range(prop, 0.5, 40.0, 0.1, 2); + RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); RNA_def_property_ui_text( prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (in pixels)"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 24d051fecc8..b2e399d41f5 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -505,7 +505,7 @@ static Sequence *sequence_get_by_transform(Editing *ed, StripTransform *transfor data.data = transform; /* irritating we need to search for our sequence! */ - SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, transform_seq_cmp_fn, &data); + SEQ_seqbase_recursive_apply(&ed->seqbase, transform_seq_cmp_fn, &data); return data.seq; } @@ -557,7 +557,7 @@ static Sequence *sequence_get_by_crop(Editing *ed, StripCrop *crop) data.data = crop; /* irritating we need to search for our sequence! */ - SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, crop_seq_cmp_fn, &data); + SEQ_seqbase_recursive_apply(&ed->seqbase, crop_seq_cmp_fn, &data); return data.seq; } @@ -951,7 +951,7 @@ static Sequence *sequence_get_by_proxy(Editing *ed, StripProxy *proxy) data.seq = NULL; data.data = proxy; - SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_fn, &data); + SEQ_seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_fn, &data); return data.seq; } @@ -990,18 +990,14 @@ static int colbalance_seq_cmp_fn(Sequence *seq, void *arg_pt) { SequenceSearchData *data = arg_pt; - if (seq->modifiers.first) { - SequenceModifierData *smd = seq->modifiers.first; + for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) { + if (smd->type == seqModifierType_ColorBalance) { + ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd; - for (smd = seq->modifiers.first; smd; smd = smd->next) { - if (smd->type == seqModifierType_ColorBalance) { - ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd; - - if (&cbmd->color_balance == data->data) { - data->seq = seq; - data->smd = smd; - return -1; /* done so bail out */ - } + if (&cbmd->color_balance == data->data) { + data->seq = seq; + data->smd = smd; + return -1; /* done so bail out */ } } } @@ -1020,7 +1016,7 @@ static Sequence *sequence_get_by_colorbalance(Editing *ed, data.data = cb; /* irritating we need to search for our sequence! */ - SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, colbalance_seq_cmp_fn, &data); + SEQ_seqbase_recursive_apply(&ed->seqbase, colbalance_seq_cmp_fn, &data); *r_smd = data.smd; @@ -1144,7 +1140,7 @@ static Sequence *sequence_get_by_modifier(Editing *ed, SequenceModifierData *smd data.data = smd; /* irritating we need to search for our sequence! */ - SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, modifier_seq_cmp_fn, &data); + SEQ_seqbase_recursive_apply(&ed->seqbase, modifier_seq_cmp_fn, &data); return data.seq; } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 2a513691762..aa07ff4006a 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3046,6 +3046,10 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) { sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; } + if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE && + !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + } } const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C), @@ -3091,6 +3095,11 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UN continue; } } + if (component_type == GEO_COMPONENT_TYPE_CURVE) { + if (!ELEM(item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + continue; + } + } if (item->value == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH) { RNA_enum_item_add(&item_array, &items_len, &mesh_vertex_domain_item); } @@ -7485,6 +7494,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna) ICON_POINTCLOUD_DATA, "Point Cloud", "Point cloud component containing only point data"}, + {GEO_COMPONENT_TYPE_CURVE, + "CURVE", + ICON_CURVE_DATA, + "Curve", + "Curve component containing spline and control point data"}, {GEO_COMPONENT_TYPE_INSTANCES, "INSTANCES", ICON_EMPTY_AXIS, diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 26887b51f81..bacd3943141 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6160,7 +6160,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) prop, "Python Scripts Directory", "Alternate script path, matching the default layout with subdirectories: " - "startup, add-ons and modules (requires restart)"); + "startup, addons, modules, and presets (requires restart)"); /* TODO, editing should reset sys.path! */ prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 54caaed9231..c19467945f9 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -79,6 +79,7 @@ set(SRC intern/MOD_mirror.c intern/MOD_multires.c intern/MOD_nodes.cc + intern/MOD_nodes_evaluator.cc intern/MOD_none.c intern/MOD_normal_edit.c intern/MOD_ocean.c @@ -118,6 +119,7 @@ set(SRC MOD_modifiertypes.h MOD_nodes.h intern/MOD_meshcache_util.h + intern/MOD_nodes_evaluator.hh intern/MOD_solidify_util.h intern/MOD_ui_common.h intern/MOD_util.h @@ -191,6 +193,18 @@ if(WITH_GMP) ) endif() +if(WITH_TBB) + add_definitions(-DWITH_TBB) + + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${TBB_LIBRARIES} + ) +endif() + if(WITH_OPENVDB) list(APPEND INC ../../../intern/openvdb diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 6769f14f88f..649d36e3d57 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -296,7 +296,6 @@ ModifierTypeInfo modifierType_Armature = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 0282f0d2934..93a9e76ffe4 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -1021,7 +1021,6 @@ ModifierTypeInfo modifierType_Array = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index a94411d897e..8fdd222402e 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -448,7 +448,6 @@ ModifierTypeInfo modifierType_Bevel = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, /* freeData */ freeData, diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 40d5386c2fa..9b8782737c3 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -635,7 +635,6 @@ ModifierTypeInfo modifierType_Boolean = { /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, - /* modifyVolume */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 0b1c661baed..c38e5126f6b 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -347,7 +347,6 @@ ModifierTypeInfo modifierType_Build = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index f905a38ae12..715bc26e5d3 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -591,7 +591,6 @@ ModifierTypeInfo modifierType_Cast = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index a25d65347c5..40d027f3044 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -311,7 +311,6 @@ ModifierTypeInfo modifierType_Cloth = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index e72e0279263..5dd57469914 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -317,7 +317,6 @@ ModifierTypeInfo modifierType_Collision = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 001c7d8d098..fef235b456b 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -852,7 +852,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index d5d53edfd54..20dbb299767 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -236,7 +236,6 @@ ModifierTypeInfo modifierType_Curve = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index 8b299a82f95..dbdc76f0edc 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -495,7 +495,6 @@ ModifierTypeInfo modifierType_DataTransfer = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 03db09a5aec..faad1175f3a 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -300,7 +300,6 @@ ModifierTypeInfo modifierType_Decimate = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index abe78943508..a7ac9f618af 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -507,7 +507,6 @@ ModifierTypeInfo modifierType_Displace = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 3e607e88cdd..8b1d541d45d 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_DynamicPaint = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index e02befd7efa..2874bebe13a 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -187,7 +187,6 @@ ModifierTypeInfo modifierType_EdgeSplit = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index c12019a325e..e1197439c7c 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -1255,7 +1255,6 @@ ModifierTypeInfo modifierType_Explode = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index 8a8d6f2305f..36d2ab2a11a 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -239,7 +239,6 @@ ModifierTypeInfo modifierType_Fluid = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 2fa05a319d5..ff581e92cdd 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -574,7 +574,6 @@ ModifierTypeInfo modifierType_Hook = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index bda0f9ba5a4..6efeec1970f 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -889,7 +889,6 @@ ModifierTypeInfo modifierType_LaplacianDeform = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index fc527304e76..78e0bf3fa8f 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -635,7 +635,6 @@ ModifierTypeInfo modifierType_LaplacianSmooth = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ init_data, /* requiredDataMask */ required_data_mask, diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index e3c42e39dda..29d1ecf6050 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -193,7 +193,6 @@ ModifierTypeInfo modifierType_Lattice = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 191d39d9fce..812bfe3b375 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -463,7 +463,6 @@ ModifierTypeInfo modifierType_Mask = { /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, - /* modifyVolume */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index cc007651733..778b5746471 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -20,6 +20,7 @@ #include <vector> +#include "BKE_geometry_set.hh" #include "BKE_lib_query.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_wrapper.h" @@ -204,7 +205,9 @@ static float compute_voxel_size(const ModifierEvalContext *ctx, } #endif -static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *input_volume) +static Volume *mesh_to_volume(ModifierData *md, + const ModifierEvalContext *ctx, + Volume *input_volume) { #ifdef WITH_OPENVDB using namespace blender; @@ -278,6 +281,17 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo #endif } +static void modifyGeometrySet(ModifierData *md, + const ModifierEvalContext *ctx, + GeometrySet *geometry_set) +{ + Volume *input_volume = geometry_set->get_volume_for_write(); + Volume *result_volume = mesh_to_volume(md, ctx, input_volume); + if (result_volume != input_volume) { + geometry_set->replace_volume(result_volume); + } +} + ModifierTypeInfo modifierType_MeshToVolume = { /* name */ "Mesh to Volume", /* structName */ "MeshToVolumeModifierData", @@ -295,8 +309,7 @@ ModifierTypeInfo modifierType_MeshToVolume = { /* deformMatricesEM */ nullptr, /* modifyMesh */ nullptr, /* modifyHair */ nullptr, - /* modifyGeometrySet */ nullptr, - /* modifyVolume */ modifyVolume, + /* modifyGeometrySet */ modifyGeometrySet, /* initData */ initData, /* requiredDataMask */ nullptr, diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 6d2e0f242d7..361454120ca 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -389,7 +389,6 @@ ModifierTypeInfo modifierType_MeshCache = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index a94dd6da477..8e1e03570b5 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -644,7 +644,6 @@ ModifierTypeInfo modifierType_MeshDeform = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 2c01857adb1..c2f9cd8c867 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -271,7 +271,6 @@ ModifierTypeInfo modifierType_MeshSequenceCache = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index b800ce7f803..6116cf8146a 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -239,7 +239,6 @@ ModifierTypeInfo modifierType_Mirror = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c index 1182c8db093..c3b34f3cd23 100644 --- a/source/blender/modifiers/intern/MOD_multires.c +++ b/source/blender/modifiers/intern/MOD_multires.c @@ -520,7 +520,6 @@ ModifierTypeInfo modifierType_Multires = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index d808052e5f5..37a21a3c14b 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -75,16 +75,14 @@ #include "MOD_modifiertypes.h" #include "MOD_nodes.h" +#include "MOD_nodes_evaluator.hh" #include "MOD_ui_common.h" #include "ED_spreadsheet.h" #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" -#include "NOD_geometry_exec.hh" #include "NOD_node_tree_multi_function.hh" -#include "NOD_type_callbacks.hh" -#include "NOD_type_conversions.hh" using blender::float3; using blender::FunctionRef; @@ -95,12 +93,8 @@ using blender::Span; using blender::StringRef; using blender::StringRefNull; using blender::Vector; -using blender::bke::PersistentCollectionHandle; -using blender::bke::PersistentDataHandleMap; -using blender::bke::PersistentObjectHandle; using blender::fn::GMutablePointer; using blender::fn::GPointer; -using blender::fn::GValueMap; using blender::nodes::GeoNodeExecParams; using namespace blender::fn::multi_function_types; using namespace blender::nodes::derived_node_tree_types; @@ -185,7 +179,7 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec if (object.type == OB_EMPTY && object.instance_collection != nullptr) { add_collection_relation(ctx, *object.instance_collection); } - else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) { + else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE)) { DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier"); DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask); } @@ -268,368 +262,6 @@ static bool logging_enabled(const ModifierEvalContext *ctx) return true; } -class GeometryNodesEvaluator { - public: - using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>; - - private: - blender::LinearAllocator<> allocator_; - Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_; - Vector<DInputSocket> group_outputs_; - blender::nodes::MultiFunctionByNode &mf_by_node_; - const blender::nodes::DataTypeConversions &conversions_; - const PersistentDataHandleMap &handle_map_; - const Object *self_object_; - const ModifierData *modifier_; - Depsgraph *depsgraph_; - LogSocketValueFn log_socket_value_fn_; - - public: - GeometryNodesEvaluator(const Map<DOutputSocket, GMutablePointer> &group_input_data, - Vector<DInputSocket> group_outputs, - blender::nodes::MultiFunctionByNode &mf_by_node, - const PersistentDataHandleMap &handle_map, - const Object *self_object, - const ModifierData *modifier, - Depsgraph *depsgraph, - LogSocketValueFn log_socket_value_fn) - : group_outputs_(std::move(group_outputs)), - mf_by_node_(mf_by_node), - conversions_(blender::nodes::get_implicit_type_conversions()), - handle_map_(handle_map), - self_object_(self_object), - modifier_(modifier), - depsgraph_(depsgraph), - log_socket_value_fn_(std::move(log_socket_value_fn)) - { - for (auto item : group_input_data.items()) { - this->log_socket_value(item.key, item.value); - this->forward_to_inputs(item.key, item.value); - } - } - - Vector<GMutablePointer> execute() - { - Vector<GMutablePointer> results; - for (const DInputSocket &group_output : group_outputs_) { - Vector<GMutablePointer> result = this->get_input_values(group_output); - this->log_socket_value(group_output, result); - results.append(result[0]); - } - for (GMutablePointer value : value_by_input_.values()) { - value.destruct(); - } - return results; - } - - private: - Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute) - { - Vector<DSocket> from_sockets; - socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); }); - - if (from_sockets.is_empty()) { - /* The input is not connected, use the value from the socket itself. */ - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); - return {get_unlinked_input_value(socket_to_compute, type)}; - } - - /* Multi-input sockets contain a vector of inputs. */ - if (socket_to_compute->is_multi_input_socket()) { - return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets); - } - - const DSocket from_socket = from_sockets[0]; - GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket); - return {value}; - } - - Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute, - const Span<DSocket> from_sockets) - { - Vector<GMutablePointer> values; - for (const int i : from_sockets.index_range()) { - const DSocket from_socket = from_sockets[i]; - const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket); - if (first_occurence == -1) { - values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket)); - } - else { - /* If the same from-socket occurs more than once, we make a copy of the first value. This - * can happen when a node linked to a multi-input-socket is muted. */ - GMutablePointer value = values[first_occurence]; - const CPPType *type = value.type(); - void *copy_buffer = allocator_.allocate(type->size(), type->alignment()); - type->copy_to_uninitialized(value.get(), copy_buffer); - values.append({type, copy_buffer}); - } - } - return values; - } - - GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute, - const DSocket from_socket) - { - if (from_socket->is_output()) { - const DOutputSocket from_output_socket{from_socket}; - const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute, - from_output_socket); - std::optional<GMutablePointer> value = value_by_input_.pop_try(key); - if (value.has_value()) { - /* This input has been computed before, return it directly. */ - return {*value}; - } - - /* Compute the socket now. */ - this->compute_output_and_forward(from_output_socket); - return {value_by_input_.pop(key)}; - } - - /* Get value from an unlinked input socket. */ - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); - const DInputSocket from_input_socket{from_socket}; - return {get_unlinked_input_value(from_input_socket, type)}; - } - - void compute_output_and_forward(const DOutputSocket socket_to_compute) - { - const DNode node{socket_to_compute.context(), &socket_to_compute->node()}; - - if (!socket_to_compute->is_available()) { - /* If the output is not available, use a default value. */ - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - type.copy_to_uninitialized(type.default_value(), buffer); - this->forward_to_inputs(socket_to_compute, {type, buffer}); - return; - } - - /* Prepare inputs required to execute the node. */ - GValueMap<StringRef> node_inputs_map{allocator_}; - for (const InputSocketRef *input_socket : node->inputs()) { - if (input_socket->is_available()) { - DInputSocket dsocket{node.context(), input_socket}; - Vector<GMutablePointer> values = this->get_input_values(dsocket); - this->log_socket_value(dsocket, values); - for (int i = 0; i < values.size(); ++i) { - /* Values from Multi Input Sockets are stored in input map with the format - * <identifier>[<index>]. */ - blender::StringRefNull key = allocator_.copy_string( - input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : "")); - node_inputs_map.add_new_direct(key, std::move(values[i])); - } - } - } - - /* Execute the node. */ - GValueMap<StringRef> node_outputs_map{allocator_}; - GeoNodeExecParams params{ - node, node_inputs_map, node_outputs_map, handle_map_, self_object_, modifier_, depsgraph_}; - this->execute_node(node, params); - - /* Forward computed outputs to linked input sockets. */ - for (const OutputSocketRef *output_socket : node->outputs()) { - if (output_socket->is_available()) { - const DOutputSocket dsocket{node.context(), output_socket}; - GMutablePointer value = node_outputs_map.extract(output_socket->identifier()); - this->log_socket_value(dsocket, value); - this->forward_to_inputs(dsocket, value); - } - } - } - - void log_socket_value(const DSocket socket, Span<GPointer> values) - { - if (log_socket_value_fn_) { - log_socket_value_fn_(socket, values); - } - } - - void log_socket_value(const DSocket socket, Span<GMutablePointer> values) - { - this->log_socket_value(socket, values.cast<GPointer>()); - } - - void log_socket_value(const DSocket socket, GPointer value) - { - this->log_socket_value(socket, Span<GPointer>(&value, 1)); - } - - void execute_node(const DNode node, GeoNodeExecParams params) - { - const bNode &bnode = params.node(); - - /* Use the geometry-node-execute callback if it exists. */ - if (bnode.typeinfo->geometry_node_execute != nullptr) { - bnode.typeinfo->geometry_node_execute(params); - return; - } - - /* Use the multi-function implementation if it exists. */ - const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr); - if (multi_function != nullptr) { - this->execute_multi_function_node(node, params, *multi_function); - return; - } - - /* Just output default values if no implementation exists. */ - this->execute_unknown_node(node, params); - } - - void execute_multi_function_node(const DNode node, - GeoNodeExecParams params, - const MultiFunction &fn) - { - MFContextBuilder fn_context; - MFParamsBuilder fn_params{fn, 1}; - Vector<GMutablePointer> input_data; - for (const InputSocketRef *socket_ref : node->inputs()) { - if (socket_ref->is_available()) { - GMutablePointer data = params.extract_input(socket_ref->identifier()); - fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1)); - input_data.append(data); - } - } - Vector<GMutablePointer> output_data; - for (const OutputSocketRef *socket_ref : node->outputs()) { - if (socket_ref->is_available()) { - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo()); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1)); - output_data.append(GMutablePointer(type, buffer)); - } - } - fn.call(IndexRange(1), fn_params, fn_context); - for (GMutablePointer value : input_data) { - value.destruct(); - } - int output_index = 0; - for (const int i : node->outputs().index_range()) { - if (node->output(i).is_available()) { - GMutablePointer value = output_data[output_index]; - params.set_output_by_move(node->output(i).identifier(), value); - value.destruct(); - output_index++; - } - } - } - - void execute_unknown_node(const DNode node, GeoNodeExecParams params) - { - for (const OutputSocketRef *socket : node->outputs()) { - if (socket->is_available()) { - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); - params.set_output_by_copy(socket->identifier(), {type, type.default_value()}); - } - } - } - - void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward) - { - /* For all sockets that are linked with the from_socket push the value to their node. */ - Vector<DInputSocket> to_sockets_all; - - auto handle_target_socket_fn = [&](DInputSocket to_socket) { - to_sockets_all.append_non_duplicates(to_socket); - }; - auto handle_skipped_socket_fn = [&, this](DSocket socket) { - this->log_socket_value(socket, value_to_forward); - }; - - from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn); - - const CPPType &from_type = *value_to_forward.type(); - Vector<DInputSocket> to_sockets_same_type; - for (const DInputSocket &to_socket : to_sockets_all) { - const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo()); - const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); - if (from_type == to_type) { - to_sockets_same_type.append(to_socket); - } - else { - void *buffer = allocator_.allocate(to_type.size(), to_type.alignment()); - if (conversions_.is_convertible(from_type, to_type)) { - conversions_.convert_to_uninitialized( - from_type, to_type, value_to_forward.get(), buffer); - } - else { - to_type.copy_to_uninitialized(to_type.default_value(), buffer); - } - add_value_to_input_socket(key, GMutablePointer{to_type, buffer}); - } - } - - if (to_sockets_same_type.size() == 0) { - /* This value is not further used, so destruct it. */ - value_to_forward.destruct(); - } - else if (to_sockets_same_type.size() == 1) { - /* This value is only used on one input socket, no need to copy it. */ - const DInputSocket to_socket = to_sockets_same_type[0]; - const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); - - add_value_to_input_socket(key, value_to_forward); - } - else { - /* Multiple inputs use the value, make a copy for every input except for one. */ - const DInputSocket first_to_socket = to_sockets_same_type[0]; - Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1); - const CPPType &type = *value_to_forward.type(); - const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket, - from_socket); - add_value_to_input_socket(first_key, value_to_forward); - for (const DInputSocket &to_socket : other_to_sockets) { - const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - type.copy_to_uninitialized(value_to_forward.get(), buffer); - add_value_to_input_socket(key, GMutablePointer{type, buffer}); - } - } - } - - void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key, - GMutablePointer value) - { - value_by_input_.add_new(key, value); - } - - GMutablePointer get_unlinked_input_value(const DInputSocket &socket, - const CPPType &required_type) - { - bNodeSocket *bsocket = socket->bsocket(); - const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - - if (bsocket->type == SOCK_OBJECT) { - Object *object = socket->default_value<bNodeSocketValueObject>()->value; - PersistentObjectHandle object_handle = handle_map_.lookup(object); - new (buffer) PersistentObjectHandle(object_handle); - } - else if (bsocket->type == SOCK_COLLECTION) { - Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value; - PersistentCollectionHandle collection_handle = handle_map_.lookup(collection); - new (buffer) PersistentCollectionHandle(collection_handle); - } - else { - blender::nodes::socket_cpp_value_get(*bsocket, buffer); - } - - if (type == required_type) { - return {type, buffer}; - } - if (conversions_.is_convertible(type, required_type)) { - void *converted_buffer = allocator_.allocate(required_type.size(), - required_type.alignment()); - conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer); - type.destruct(buffer); - return {required_type, converted_buffer}; - } - void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment()); - required_type.copy_to_uninitialized(required_type.default_value(), default_buffer); - return {required_type, default_buffer}; - } -}; - /** * This code is responsible for creating the new property and also creating the group of * properties in the prop_ui_container group for the UI info, the mapping for which is @@ -649,9 +281,7 @@ struct SocketPropertyType { IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name); PropertyType (*rna_subtype_get)(const bNodeSocket &socket); bool (*is_correct_type)(const IDProperty &property); - void (*init_cpp_value)(const IDProperty &property, - const PersistentDataHandleMap &handles, - void *r_value); + void (*init_cpp_value)(const IDProperty &property, void *r_value); }; static IDProperty *socket_add_property(IDProperty *settings_prop_group, @@ -744,9 +374,7 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype; }, [](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); }, - [](const IDProperty &property, - const PersistentDataHandleMap &UNUSED(handles), - void *r_value) { + [](const IDProperty &property, void *r_value) { if (property.type == IDP_FLOAT) { *(float *)r_value = IDP_Float(&property); } @@ -787,9 +415,7 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype; }, [](const IDProperty &property) { return property.type == IDP_INT; }, - [](const IDProperty &property, - const PersistentDataHandleMap &UNUSED(handles), - void *r_value) { *(int *)r_value = IDP_Int(&property); }, + [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); }, }; return &int_type; } @@ -832,9 +458,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 3; }, - [](const IDProperty &property, - const PersistentDataHandleMap &UNUSED(handles), - void *r_value) { copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); }, + [](const IDProperty &property, void *r_value) { + copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); + }, }; return &vector_type; } @@ -864,9 +490,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso }, nullptr, [](const IDProperty &property) { return property.type == IDP_INT; }, - [](const IDProperty &property, - const PersistentDataHandleMap &UNUSED(handles), - void *r_value) { *(bool *)r_value = IDP_Int(&property) != 0; }, + [](const IDProperty &property, void *r_value) { + *(bool *)r_value = IDP_Int(&property) != 0; + }, }; return &boolean_type; } @@ -886,9 +512,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso }, nullptr, [](const IDProperty &property) { return property.type == IDP_STRING; }, - [](const IDProperty &property, - const PersistentDataHandleMap &UNUSED(handles), - void *r_value) { new (r_value) std::string(IDP_String(&property)); }, + [](const IDProperty &property, void *r_value) { + new (r_value) std::string(IDP_String(&property)); + }, }; return &string_type; } @@ -905,10 +531,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso nullptr, nullptr, [](const IDProperty &property) { return property.type == IDP_ID; }, - [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) { + [](const IDProperty &property, void *r_value) { ID *id = IDP_Id(&property); Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr; - new (r_value) PersistentObjectHandle(handles.lookup(object)); + *(Object **)r_value = object; }, }; return &object_type; @@ -926,10 +552,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso nullptr, nullptr, [](const IDProperty &property) { return property.type == IDP_ID; }, - [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) { + [](const IDProperty &property, void *r_value) { ID *id = IDP_Id(&property); Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr; - new (r_value) PersistentCollectionHandle(handles.lookup(collection)); + *(Collection **)r_value = collection; }, }; return &collection_type; @@ -1017,7 +643,6 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd) } static void initialize_group_input(NodesModifierData &nmd, - const PersistentDataHandleMap &handle_map, const bNodeSocket &socket, const CPPType &cpp_type, void *r_value) @@ -1041,22 +666,7 @@ static void initialize_group_input(NodesModifierData &nmd, blender::nodes::socket_cpp_value_get(socket, r_value); return; } - property_type->init_cpp_value(*property, handle_map, r_value); -} - -static void fill_data_handle_map(const NodesModifierSettings &settings, - const DerivedNodeTree &tree, - PersistentDataHandleMap &handle_map) -{ - Set<ID *> used_ids; - find_used_ids_from_settings(settings, used_ids); - find_used_ids_from_nodes(*tree.root_context().tree().btree(), used_ids); - - int current_handle = 0; - for (ID *id : used_ids) { - handle_map.add(current_handle, *id); - current_handle++; - } + property_type->init_cpp_value(*property, r_value); } static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> trees, @@ -1247,9 +857,6 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, blender::LinearAllocator<> &allocator = scope.linear_allocator(); blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope); - PersistentDataHandleMap handle_map; - fill_data_handle_map(nmd->settings, tree, handle_map); - Map<DOutputSocket, GMutablePointer> group_inputs; const DTreeContext *root_context = &tree.root_context(); @@ -1275,7 +882,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, for (const OutputSocketRef *socket : remaining_input_sockets) { const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); - initialize_group_input(*nmd, handle_map, *socket->bsocket(), cpp_type, value_in); + initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in); group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); } } @@ -1300,21 +907,19 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, log_ui_hints(socket, values, ctx->object, nmd); }; - GeometryNodesEvaluator evaluator{group_inputs, - group_outputs, - mf_by_node, - handle_map, - ctx->object, - (ModifierData *)nmd, - ctx->depsgraph, - log_socket_value}; - - Vector<GMutablePointer> results = evaluator.execute(); - BLI_assert(results.size() == 1); - GMutablePointer result = results[0]; - - GeometrySet output_geometry = std::move(*(GeometrySet *)result.get()); - return output_geometry; + blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params; + eval_params.input_values = group_inputs; + eval_params.output_sockets = group_outputs; + eval_params.mf_by_node = &mf_by_node; + eval_params.modifier_ = nmd; + eval_params.depsgraph = ctx->depsgraph; + eval_params.self_object = ctx->object; + eval_params.log_socket_value_fn = log_socket_value; + blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); + + BLI_assert(eval_params.r_output_values.size() == 1); + GMutablePointer result = eval_params.r_output_values[0]; + return result.relocate_out<GeometrySet>(); } /** @@ -1453,36 +1058,37 @@ static void draw_property_for_socket(uiLayout *layout, /* IDProperties can be removed with python, so there could be a situation where * there isn't a property for a socket or it doesn't have the correct type. */ - if (property != nullptr && property_type->is_correct_type(*property)) { + if (property == nullptr || !property_type->is_correct_type(*property)) { + return; + } - char socket_id_esc[sizeof(socket.identifier) * 2]; - BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + char socket_id_esc[sizeof(socket.identifier) * 2]; + BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); - char rna_path[sizeof(socket_id_esc) + 4]; - BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc); + char rna_path[sizeof(socket_id_esc) + 4]; + BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc); - /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough - * information about what type of ID to select for editing the values. This is because - * pointer IDProperties contain no information about their type. */ - switch (socket.type) { - case SOCK_OBJECT: { - uiItemPointerR( - layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA); - break; - } - case SOCK_COLLECTION: { - uiItemPointerR(layout, - md_ptr, - rna_path, - bmain_ptr, - "collections", - socket.name, - ICON_OUTLINER_COLLECTION); - break; - } - default: - uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough + * information about what type of ID to select for editing the values. This is because + * pointer IDProperties contain no information about their type. */ + switch (socket.type) { + case SOCK_OBJECT: { + uiItemPointerR( + layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA); + break; } + case SOCK_COLLECTION: { + uiItemPointerR(layout, + md_ptr, + rna_path, + bmain_ptr, + "collections", + socket.name, + ICON_OUTLINER_COLLECTION); + break; + } + default: + uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); } } @@ -1594,7 +1200,6 @@ ModifierTypeInfo modifierType_Nodes = { /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, /* modifyGeometrySet */ modifyGeometrySet, - /* modifyVolume */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc new file mode 100644 index 00000000000..2637c7db0fc --- /dev/null +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -0,0 +1,434 @@ +/* + * 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 "MOD_nodes_evaluator.hh" + +#include "NOD_geometry_exec.hh" +#include "NOD_type_conversions.hh" + +#include "DEG_depsgraph_query.h" + +#include "FN_generic_value_map.hh" +#include "FN_multi_function.hh" + +namespace blender::modifiers::geometry_nodes { + +using fn::CPPType; +using fn::GValueMap; +using nodes::GeoNodeExecParams; +using namespace fn::multi_function_types; + +class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider { + public: + LinearAllocator<> *allocator; + GValueMap<StringRef> *input_values; + GValueMap<StringRef> *output_values; + + bool can_get_input(StringRef identifier) const override + { + return input_values->contains(identifier); + } + + bool can_set_output(StringRef identifier) const override + { + return !output_values->contains(identifier); + } + + GMutablePointer extract_input(StringRef identifier) override + { + return this->input_values->extract(identifier); + } + + Vector<GMutablePointer> extract_multi_input(StringRef identifier) override + { + Vector<GMutablePointer> values; + int index = 0; + while (true) { + std::string sub_identifier = identifier; + if (index > 0) { + sub_identifier += "[" + std::to_string(index) + "]"; + } + if (!this->input_values->contains(sub_identifier)) { + break; + } + values.append(input_values->extract(sub_identifier)); + index++; + } + return values; + } + + GPointer get_input(StringRef identifier) const override + { + return this->input_values->lookup(identifier); + } + + GMutablePointer alloc_output_value(StringRef identifier, const CPPType &type) override + { + void *buffer = this->allocator->allocate(type.size(), type.alignment()); + GMutablePointer ptr{&type, buffer}; + this->output_values->add_new_direct(identifier, ptr); + return ptr; + } +}; + +class GeometryNodesEvaluator { + public: + using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>; + + private: + blender::LinearAllocator<> &allocator_; + Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_; + Vector<DInputSocket> group_outputs_; + blender::nodes::MultiFunctionByNode &mf_by_node_; + const blender::nodes::DataTypeConversions &conversions_; + const Object *self_object_; + const ModifierData *modifier_; + Depsgraph *depsgraph_; + LogSocketValueFn log_socket_value_fn_; + + public: + GeometryNodesEvaluator(GeometryNodesEvaluationParams ¶ms) + : allocator_(params.allocator), + group_outputs_(std::move(params.output_sockets)), + mf_by_node_(*params.mf_by_node), + conversions_(blender::nodes::get_implicit_type_conversions()), + self_object_(params.self_object), + modifier_(¶ms.modifier_->modifier), + depsgraph_(params.depsgraph), + log_socket_value_fn_(std::move(params.log_socket_value_fn)) + { + for (auto item : params.input_values.items()) { + this->log_socket_value(item.key, item.value); + this->forward_to_inputs(item.key, item.value); + } + } + + Vector<GMutablePointer> execute() + { + Vector<GMutablePointer> results; + for (const DInputSocket &group_output : group_outputs_) { + Vector<GMutablePointer> result = this->get_input_values(group_output); + this->log_socket_value(group_output, result); + results.append(result[0]); + } + for (GMutablePointer value : value_by_input_.values()) { + value.destruct(); + } + return results; + } + + private: + Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute) + { + Vector<DSocket> from_sockets; + socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); }); + + if (from_sockets.is_empty()) { + /* The input is not connected, use the value from the socket itself. */ + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); + return {get_unlinked_input_value(socket_to_compute, type)}; + } + + /* Multi-input sockets contain a vector of inputs. */ + if (socket_to_compute->is_multi_input_socket()) { + return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets); + } + + const DSocket from_socket = from_sockets[0]; + GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket); + return {value}; + } + + Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute, + const Span<DSocket> from_sockets) + { + Vector<GMutablePointer> values; + for (const int i : from_sockets.index_range()) { + const DSocket from_socket = from_sockets[i]; + const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket); + if (first_occurence == -1) { + values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket)); + } + else { + /* If the same from-socket occurs more than once, we make a copy of the first value. This + * can happen when a node linked to a multi-input-socket is muted. */ + GMutablePointer value = values[first_occurence]; + const CPPType *type = value.type(); + void *copy_buffer = allocator_.allocate(type->size(), type->alignment()); + type->copy_to_uninitialized(value.get(), copy_buffer); + values.append({type, copy_buffer}); + } + } + return values; + } + + GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute, + const DSocket from_socket) + { + if (from_socket->is_output()) { + const DOutputSocket from_output_socket{from_socket}; + const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute, + from_output_socket); + std::optional<GMutablePointer> value = value_by_input_.pop_try(key); + if (value.has_value()) { + /* This input has been computed before, return it directly. */ + return {*value}; + } + + /* Compute the socket now. */ + this->compute_output_and_forward(from_output_socket); + return {value_by_input_.pop(key)}; + } + + /* Get value from an unlinked input socket. */ + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); + const DInputSocket from_input_socket{from_socket}; + return {get_unlinked_input_value(from_input_socket, type)}; + } + + void compute_output_and_forward(const DOutputSocket socket_to_compute) + { + const DNode node{socket_to_compute.context(), &socket_to_compute->node()}; + + if (!socket_to_compute->is_available()) { + /* If the output is not available, use a default value. */ + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo()); + void *buffer = allocator_.allocate(type.size(), type.alignment()); + type.copy_to_uninitialized(type.default_value(), buffer); + this->forward_to_inputs(socket_to_compute, {type, buffer}); + return; + } + + /* Prepare inputs required to execute the node. */ + GValueMap<StringRef> node_inputs_map{allocator_}; + for (const InputSocketRef *input_socket : node->inputs()) { + if (input_socket->is_available()) { + DInputSocket dsocket{node.context(), input_socket}; + Vector<GMutablePointer> values = this->get_input_values(dsocket); + this->log_socket_value(dsocket, values); + for (int i = 0; i < values.size(); ++i) { + /* Values from Multi Input Sockets are stored in input map with the format + * <identifier>[<index>]. */ + blender::StringRefNull key = allocator_.copy_string( + input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : "")); + node_inputs_map.add_new_direct(key, std::move(values[i])); + } + } + } + + /* Execute the node. */ + GValueMap<StringRef> node_outputs_map{allocator_}; + NodeParamsProvider params_provider; + params_provider.dnode = node; + params_provider.self_object = self_object_; + params_provider.depsgraph = depsgraph_; + params_provider.allocator = &allocator_; + params_provider.input_values = &node_inputs_map; + params_provider.output_values = &node_outputs_map; + params_provider.modifier = modifier_; + this->execute_node(node, params_provider); + + /* Forward computed outputs to linked input sockets. */ + for (const OutputSocketRef *output_socket : node->outputs()) { + if (output_socket->is_available()) { + const DOutputSocket dsocket{node.context(), output_socket}; + GMutablePointer value = node_outputs_map.extract(output_socket->identifier()); + this->log_socket_value(dsocket, value); + this->forward_to_inputs(dsocket, value); + } + } + } + + void log_socket_value(const DSocket socket, Span<GPointer> values) + { + if (log_socket_value_fn_) { + log_socket_value_fn_(socket, values); + } + } + + void log_socket_value(const DSocket socket, Span<GMutablePointer> values) + { + this->log_socket_value(socket, values.cast<GPointer>()); + } + + void log_socket_value(const DSocket socket, GPointer value) + { + this->log_socket_value(socket, Span<GPointer>(&value, 1)); + } + + void execute_node(const DNode node, NodeParamsProvider ¶ms_provider) + { + const bNode &bnode = *params_provider.dnode->bnode(); + + /* Use the geometry-node-execute callback if it exists. */ + if (bnode.typeinfo->geometry_node_execute != nullptr) { + GeoNodeExecParams params{params_provider}; + bnode.typeinfo->geometry_node_execute(params); + return; + } + + /* Use the multi-function implementation if it exists. */ + const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr); + if (multi_function != nullptr) { + this->execute_multi_function_node(node, params_provider, *multi_function); + return; + } + + /* Just output default values if no implementation exists. */ + this->execute_unknown_node(node, params_provider); + } + + void execute_multi_function_node(const DNode node, + NodeParamsProvider ¶ms_provider, + const MultiFunction &fn) + { + MFContextBuilder fn_context; + MFParamsBuilder fn_params{fn, 1}; + Vector<GMutablePointer> input_data; + for (const InputSocketRef *socket_ref : node->inputs()) { + if (socket_ref->is_available()) { + GMutablePointer data = params_provider.extract_input(socket_ref->identifier()); + fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1)); + input_data.append(data); + } + } + Vector<GMutablePointer> output_data; + for (const OutputSocketRef *socket_ref : node->outputs()) { + if (socket_ref->is_available()) { + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo()); + GMutablePointer output_value = params_provider.alloc_output_value(socket_ref->identifier(), + type); + fn_params.add_uninitialized_single_output(GMutableSpan{type, output_value.get(), 1}); + output_data.append(output_value); + } + } + fn.call(IndexRange(1), fn_params, fn_context); + for (GMutablePointer value : input_data) { + value.destruct(); + } + } + + void execute_unknown_node(const DNode node, NodeParamsProvider ¶ms_provider) + { + for (const OutputSocketRef *socket : node->outputs()) { + if (socket->is_available()) { + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); + params_provider.output_values->add_new_by_copy(socket->identifier(), + {type, type.default_value()}); + } + } + } + + void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward) + { + /* For all sockets that are linked with the from_socket push the value to their node. */ + Vector<DInputSocket> to_sockets_all; + + auto handle_target_socket_fn = [&](DInputSocket to_socket) { + to_sockets_all.append_non_duplicates(to_socket); + }; + auto handle_skipped_socket_fn = [&, this](DSocket socket) { + this->log_socket_value(socket, value_to_forward); + }; + + from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn); + + const CPPType &from_type = *value_to_forward.type(); + Vector<DInputSocket> to_sockets_same_type; + for (const DInputSocket &to_socket : to_sockets_all) { + const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo()); + const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); + if (from_type == to_type) { + to_sockets_same_type.append(to_socket); + } + else { + void *buffer = allocator_.allocate(to_type.size(), to_type.alignment()); + if (conversions_.is_convertible(from_type, to_type)) { + conversions_.convert_to_uninitialized( + from_type, to_type, value_to_forward.get(), buffer); + } + else { + to_type.copy_to_uninitialized(to_type.default_value(), buffer); + } + add_value_to_input_socket(key, GMutablePointer{to_type, buffer}); + } + } + + if (to_sockets_same_type.size() == 0) { + /* This value is not further used, so destruct it. */ + value_to_forward.destruct(); + } + else if (to_sockets_same_type.size() == 1) { + /* This value is only used on one input socket, no need to copy it. */ + const DInputSocket to_socket = to_sockets_same_type[0]; + const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); + + add_value_to_input_socket(key, value_to_forward); + } + else { + /* Multiple inputs use the value, make a copy for every input except for one. */ + const DInputSocket first_to_socket = to_sockets_same_type[0]; + Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1); + const CPPType &type = *value_to_forward.type(); + const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket, + from_socket); + add_value_to_input_socket(first_key, value_to_forward); + for (const DInputSocket &to_socket : other_to_sockets) { + const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket); + void *buffer = allocator_.allocate(type.size(), type.alignment()); + type.copy_to_uninitialized(value_to_forward.get(), buffer); + add_value_to_input_socket(key, GMutablePointer{type, buffer}); + } + } + } + + void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key, + GMutablePointer value) + { + value_by_input_.add_new(key, value); + } + + GMutablePointer get_unlinked_input_value(const DInputSocket &socket, + const CPPType &required_type) + { + bNodeSocket *bsocket = socket->bsocket(); + const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); + void *buffer = allocator_.allocate(type.size(), type.alignment()); + blender::nodes::socket_cpp_value_get(*bsocket, buffer); + + if (type == required_type) { + return {type, buffer}; + } + if (conversions_.is_convertible(type, required_type)) { + void *converted_buffer = allocator_.allocate(required_type.size(), + required_type.alignment()); + conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer); + type.destruct(buffer); + return {required_type, converted_buffer}; + } + void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment()); + required_type.copy_to_uninitialized(required_type.default_value(), default_buffer); + return {required_type, default_buffer}; + } +}; + +void evaluate_geometry_nodes(GeometryNodesEvaluationParams ¶ms) +{ + GeometryNodesEvaluator evaluator{params}; + params.r_output_values = evaluator.execute(); +} + +} // namespace blender::modifiers::geometry_nodes diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh new file mode 100644 index 00000000000..84249e4244e --- /dev/null +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -0,0 +1,52 @@ +/* + * 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 + +#include "BLI_map.hh" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_tree_multi_function.hh" + +#include "FN_generic_pointer.hh" + +#include "DNA_modifier_types.h" + +namespace blender::modifiers::geometry_nodes { + +using namespace nodes::derived_node_tree_types; +using fn::GMutablePointer; +using fn::GPointer; + +using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>; + +struct GeometryNodesEvaluationParams { + blender::LinearAllocator<> allocator; + + Map<DOutputSocket, GMutablePointer> input_values; + Vector<DInputSocket> output_sockets; + nodes::MultiFunctionByNode *mf_by_node; + const NodesModifierData *modifier_; + Depsgraph *depsgraph; + Object *self_object; + LogSocketValueFn log_socket_value_fn; + + Vector<GMutablePointer> r_output_values; +}; + +void evaluate_geometry_nodes(GeometryNodesEvaluationParams ¶ms); + +} // namespace blender::modifiers::geometry_nodes diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c index 4daa527577b..a01f63be791 100644 --- a/source/blender/modifiers/intern/MOD_none.c +++ b/source/blender/modifiers/intern/MOD_none.c @@ -60,7 +60,6 @@ ModifierTypeInfo modifierType_None = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ NULL, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index ec10b18665e..4bfd6aba4b2 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -805,7 +805,6 @@ ModifierTypeInfo modifierType_NormalEdit = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 9c940745920..f7ac59f9e4b 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -737,7 +737,6 @@ ModifierTypeInfo modifierType_Ocean = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index e7f1fa9943e..60c5667472e 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -679,7 +679,6 @@ ModifierTypeInfo modifierType_ParticleInstance = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index 4c1179af431..38cce5e6a50 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -337,7 +337,6 @@ ModifierTypeInfo modifierType_ParticleSystem = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index 175435fcd44..88851f91337 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -301,7 +301,6 @@ ModifierTypeInfo modifierType_Remesh = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 84360caa345..b236e0896b7 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -1258,7 +1258,6 @@ ModifierTypeInfo modifierType_Screw = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c index 81a0ee72496..b517bc102f8 100644 --- a/source/blender/modifiers/intern/MOD_shapekey.c +++ b/source/blender/modifiers/intern/MOD_shapekey.c @@ -141,7 +141,6 @@ ModifierTypeInfo modifierType_ShapeKey = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ NULL, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index 93626309727..a12724ec23c 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -293,7 +293,6 @@ ModifierTypeInfo modifierType_Shrinkwrap = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index 951eacdecda..db01dec4d19 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -21,10 +21,9 @@ * \ingroup modifiers */ -#include "BLI_utildefines.h" - #include "BLI_math.h" - +#include "BLI_task.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" #include "DNA_defaults.h" @@ -57,6 +56,21 @@ #define BEND_EPS 0.000001f +ALIGN_STRUCT struct DeformUserData { + bool invert_vgroup; + char mode; + char deform_axis; + int lock_axis; + int vgroup; + int limit_axis; + float weight; + float smd_factor; + float smd_limit[2]; + float (*vertexCos)[3]; + const SpaceTransform *transf; + const MDeformVert *dvert; +}; + /* Re-maps the indices for X Y Z by shifting them up and wrapping, such that * X = Y, Y = Z, Z = X (for X axis), and X = Z, Y = X, Z = Y (for Y axis). This * exists because the deformations (excluding bend) are based on the Z axis. @@ -205,6 +219,88 @@ static void simpleDeform_bend(const float factor, } } +static void simple_helper(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + const struct DeformUserData *curr_deform_data = userdata; + float weight = BKE_defvert_array_find_weight_safe( + curr_deform_data->dvert, iter, curr_deform_data->vgroup); + const uint *axis_map = axis_map_table[(curr_deform_data->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? + curr_deform_data->deform_axis : + 2]; + const float base_limit[2] = {0.0f, 0.0f}; + + if (curr_deform_data->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight != 0.0f) { + float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; + + if (curr_deform_data->transf) { + BLI_space_transform_apply(curr_deform_data->transf, curr_deform_data->vertexCos[iter]); + } + + copy_v3_v3(co, curr_deform_data->vertexCos[iter]); + + /* Apply axis limits, and axis mappings */ + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) { + axis_limit(0, base_limit, co, dcut); + } + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) { + axis_limit(1, base_limit, co, dcut); + } + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) { + axis_limit(2, base_limit, co, dcut); + } + axis_limit(curr_deform_data->limit_axis, curr_deform_data->smd_limit, co, dcut); + + /* apply the deform to a mapped copy of the vertex, and then re-map it back. */ + float co_remap[3]; + float dcut_remap[3]; + copy_v3_v3_map(co_remap, co, axis_map); + copy_v3_v3_map(dcut_remap, dcut, axis_map); + switch (curr_deform_data->mode) { + case MOD_SIMPLEDEFORM_MODE_TWIST: + simpleDeform_twist(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_BEND: + simpleDeform_bend(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_TAPER: + simpleDeform_taper(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_STRETCH: + simpleDeform_stretch(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + default: + return; /* No simple-deform mode? */ + } + copy_v3_v3_unmap(co, co_remap, axis_map); + + /* Use vertex weight has coef of linear interpolation */ + interp_v3_v3v3( + curr_deform_data->vertexCos[iter], curr_deform_data->vertexCos[iter], co, weight); + + if (curr_deform_data->transf) { + BLI_space_transform_invert(curr_deform_data->transf, curr_deform_data->vertexCos[iter]); + } + } +} + /* simple deform modifier */ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, const ModifierEvalContext *UNUSED(ctx), @@ -213,14 +309,9 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, float (*vertexCos)[3], int numVerts) { - const float base_limit[2] = {0.0f, 0.0f}; int i; float smd_limit[2], smd_factor; SpaceTransform *transf = NULL, tmp_transf; - void (*simpleDeform_callback)(const float factor, - const int axis, - const float dcut[3], - float co[3]) = NULL; /* Mode callback */ int vgroup; MDeformVert *dvert; @@ -302,23 +393,6 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]); } - switch (smd->mode) { - case MOD_SIMPLEDEFORM_MODE_TWIST: - simpleDeform_callback = simpleDeform_twist; - break; - case MOD_SIMPLEDEFORM_MODE_BEND: - simpleDeform_callback = simpleDeform_bend; - break; - case MOD_SIMPLEDEFORM_MODE_TAPER: - simpleDeform_callback = simpleDeform_taper; - break; - case MOD_SIMPLEDEFORM_MODE_STRETCH: - simpleDeform_callback = simpleDeform_stretch; - break; - default: - return; /* No simple-deform mode? */ - } - if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) { if (fabsf(smd_factor) < BEND_EPS) { return; @@ -327,53 +401,26 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, MOD_get_vgroup(ob, mesh, smd->vgroup_name, &dvert, &vgroup); const bool invert_vgroup = (smd->flag & MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP) != 0; - const uint *axis_map = - axis_map_table[(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? deform_axis : 2]; - - for (i = 0; i < numVerts; i++) { - float weight = BKE_defvert_array_find_weight_safe(dvert, i, vgroup); - - if (invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight != 0.0f) { - float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; - if (transf) { - BLI_space_transform_apply(transf, vertexCos[i]); - } - - copy_v3_v3(co, vertexCos[i]); - - /* Apply axis limits, and axis mappings */ - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) { - axis_limit(0, base_limit, co, dcut); - } - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) { - axis_limit(1, base_limit, co, dcut); - } - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) { - axis_limit(2, base_limit, co, dcut); - } - axis_limit(limit_axis, smd_limit, co, dcut); - - /* apply the deform to a mapped copy of the vertex, and then re-map it back. */ - float co_remap[3]; - float dcut_remap[3]; - copy_v3_v3_map(co_remap, co, axis_map); - copy_v3_v3_map(dcut_remap, dcut, axis_map); - simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */ - copy_v3_v3_unmap(co, co_remap, axis_map); - - /* Use vertex weight has coef of linear interpolation */ - interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight); - - if (transf) { - BLI_space_transform_invert(transf, vertexCos[i]); - } - } - } + /* Build our data. */ + const struct DeformUserData deform_pool_data = { + .mode = smd->mode, + .smd_factor = smd_factor, + .deform_axis = deform_axis, + .transf = transf, + .vertexCos = vertexCos, + .invert_vgroup = invert_vgroup, + .lock_axis = lock_axis, + .vgroup = vgroup, + .smd_limit[0] = smd_limit[0], + .smd_limit[1] = smd_limit[1], + .dvert = dvert, + .limit_axis = limit_axis, + }; + /* Do deformation. */ + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, numVerts, (void *)&deform_pool_data, simple_helper, &settings); } /* SimpleDeform */ @@ -555,7 +602,6 @@ ModifierTypeInfo modifierType_SimpleDeform = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 5e412185cea..e2d18cf1790 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -2044,7 +2044,6 @@ ModifierTypeInfo modifierType_Skin = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c index c5011ed15c1..97027e2ecff 100644 --- a/source/blender/modifiers/intern/MOD_smooth.c +++ b/source/blender/modifiers/intern/MOD_smooth.c @@ -286,7 +286,6 @@ ModifierTypeInfo modifierType_Smooth = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 9a657c44fca..d7d2f948955 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -120,7 +120,6 @@ ModifierTypeInfo modifierType_Softbody = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ NULL, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 8e519a72df1..736dd08a713 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -276,7 +276,6 @@ ModifierTypeInfo modifierType_Solidify = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index c3611488f5f..4dc45ad0324 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -509,7 +509,6 @@ ModifierTypeInfo modifierType_Subsurf = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 7416a4baf38..d2c011a21d3 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -241,7 +241,6 @@ ModifierTypeInfo modifierType_Surface = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index 99011c5e351..0cc68c2c4a3 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1646,7 +1646,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index 04d24ac0883..ef633494c7b 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -178,7 +178,6 @@ ModifierTypeInfo modifierType_Triangulate = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ NULL, // requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index 3162a33edc2..724d1370a47 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -385,7 +385,6 @@ ModifierTypeInfo modifierType_UVProject = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index 77b79167c2f..5742144b6dd 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -342,7 +342,6 @@ ModifierTypeInfo modifierType_UVWarp = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index e7c4004853e..af4b31d6bfc 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -18,6 +18,7 @@ * \ingroup modifiers */ +#include "BKE_geometry_set.hh" #include "BKE_lib_query.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" @@ -284,7 +285,7 @@ struct DisplaceGridOp { #endif -static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume) +static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume) { #ifdef WITH_OPENVDB VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md); @@ -303,14 +304,22 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo BKE_volume_grid_type_operation(grid_type, displace_grid_op); } - return volume; #else - UNUSED_VARS(md, ctx); + UNUSED_VARS(md, volume, ctx); BKE_modifier_set_error(ctx->object, md, "Compiled without OpenVDB"); - return volume; #endif } +static void modifyGeometrySet(ModifierData *md, + const ModifierEvalContext *ctx, + GeometrySet *geometry_set) +{ + Volume *input_volume = geometry_set->get_volume_for_write(); + if (input_volume != nullptr) { + displace_volume(md, ctx, input_volume); + } +} + ModifierTypeInfo modifierType_VolumeDisplace = { /* name */ "Volume Displace", /* structName */ "VolumeDisplaceModifierData", @@ -328,8 +337,7 @@ ModifierTypeInfo modifierType_VolumeDisplace = { /* deformMatricesEM */ nullptr, /* modifyMesh */ nullptr, /* modifyHair */ nullptr, - /* modifyGeometrySet */ nullptr, - /* modifyVolume */ modifyVolume, + /* modifyGeometrySet */ modifyGeometrySet, /* initData */ initData, /* requiredDataMask */ nullptr, diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index 3bf5cf6ab5b..c0bf07b8eec 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -223,7 +223,6 @@ ModifierTypeInfo modifierType_VolumeToMesh = { /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, /* modifyGeometrySet */ nullptr, - /* modifyVolume */ nullptr, /* initData */ initData, /* requiredDataMask */ nullptr, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 9d3d5b0658c..3bebc52c503 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -539,7 +539,6 @@ ModifierTypeInfo modifierType_Warp = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 863656b85a5..c6bab89247e 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -492,7 +492,6 @@ ModifierTypeInfo modifierType_Wave = { /* modifyMesh */ NULL, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index 40265e37db9..2f2da7d6554 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -764,7 +764,6 @@ ModifierTypeInfo modifierType_WeightedNormal = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 915adccc745..b5f72c88800 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -428,7 +428,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 52cee7ce7e5..a71a2f3b480 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -514,7 +514,6 @@ ModifierTypeInfo modifierType_WeightVGMix = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index aac29cabf0f..cd03175f16c 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -768,7 +768,6 @@ ModifierTypeInfo modifierType_WeightVGProximity = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index fd1254fc948..1590f342666 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -2053,7 +2053,6 @@ ModifierTypeInfo modifierType_Weld = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index 3d8e74d2cf5..16bf1f7d763 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -196,7 +196,6 @@ ModifierTypeInfo modifierType_Wireframe = { /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, /* modifyGeometrySet */ NULL, - /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 8168f0dba73..a1d7e6c8522 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -145,6 +145,7 @@ set(SRC geometry/nodes/node_geo_attribute_combine_xyz.cc geometry/nodes/node_geo_attribute_compare.cc geometry/nodes/node_geo_attribute_convert.cc + geometry/nodes/node_geo_attribute_curve_map.cc geometry/nodes/node_geo_attribute_fill.cc geometry/nodes/node_geo_attribute_map_range.cc geometry/nodes/node_geo_attribute_math.cc @@ -154,11 +155,15 @@ set(SRC geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_sample_texture.cc geometry/nodes/node_geo_attribute_separate_xyz.cc + geometry/nodes/node_geo_attribute_transfer.cc geometry/nodes/node_geo_attribute_vector_math.cc + geometry/nodes/node_geo_attribute_vector_rotate.cc geometry/nodes/node_geo_boolean.cc geometry/nodes/node_geo_bounding_box.cc geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc + geometry/nodes/node_geo_curve_to_mesh.cc + geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc @@ -180,6 +185,7 @@ set(SRC geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_subdivide.cc geometry/nodes/node_geo_subdivision_surface.cc + geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc geometry/nodes/node_geo_volume_to_mesh.cc diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index e294bef2ea8..7ff05449c0b 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -92,6 +92,9 @@ class DNode { operator bool() const; uint64_t hash() const; + + DInputSocket input(int index) const; + DOutputSocket output(int index) const; }; /* A (nullable) reference to a socket and the context it is in. It is unique within an entire @@ -274,6 +277,16 @@ inline uint64_t DNode::hash() const return get_default_hash_2(context_, node_ref_); } +inline DInputSocket DNode::input(int index) const +{ + return {context_, &node_ref_->input(index)}; +} + +inline DOutputSocket DNode::output(int index) const +{ + return {context_, &node_ref_->output(index)}; +} + /* -------------------------------------------------------------------- * DSocket inline methods. */ diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index b84c80e916c..76c9ab8a502 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -35,6 +35,7 @@ void register_node_type_geo_attribute_color_ramp(void); void register_node_type_geo_attribute_combine_xyz(void); void register_node_type_geo_attribute_compare(void); void register_node_type_geo_attribute_convert(void); +void register_node_type_geo_attribute_curve_map(void); void register_node_type_geo_attribute_fill(void); void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); @@ -42,11 +43,15 @@ void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_proximity(void); void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_separate_xyz(void); +void register_node_type_geo_attribute_transfer(void); void register_node_type_geo_attribute_vector_math(void); +void register_node_type_geo_attribute_vector_rotate(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); +void register_node_type_geo_curve_to_mesh(void); +void register_node_type_geo_curve_resample(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); @@ -69,6 +74,7 @@ void register_node_type_geo_points_to_volume(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_subdivide(void); void register_node_type_geo_subdivision_surface(void); +void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); void register_node_type_geo_triangulate(void); void register_node_type_geo_volume_to_mesh(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 5d1a217db9b..f341cb28dce 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -22,7 +22,6 @@ #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_node_ui_storage.hh" -#include "BKE_persistent_data_handle.hh" #include "DNA_node_types.h" @@ -33,55 +32,79 @@ struct ModifierData; namespace blender::nodes { -using bke::BooleanReadAttribute; -using bke::BooleanWriteAttribute; -using bke::Color4fReadAttribute; -using bke::Color4fWriteAttribute; -using bke::Float2ReadAttribute; -using bke::Float2WriteAttribute; -using bke::Float3ReadAttribute; -using bke::Float3WriteAttribute; -using bke::FloatReadAttribute; -using bke::FloatWriteAttribute; using bke::geometry_set_realize_instances; -using bke::Int32ReadAttribute; -using bke::Int32WriteAttribute; -using bke::PersistentDataHandleMap; -using bke::PersistentObjectHandle; -using bke::ReadAttribute; -using bke::ReadAttributePtr; -using bke::WriteAttribute; -using bke::WriteAttributePtr; +using bke::OutputAttribute; +using bke::OutputAttribute_Typed; +using bke::ReadAttributeLookup; +using bke::WriteAttributeLookup; using fn::CPPType; using fn::GMutablePointer; +using fn::GMutableSpan; using fn::GPointer; +using fn::GSpan; using fn::GValueMap; +using fn::GVArray; +using fn::GVArray_GSpan; +using fn::GVArray_Span; +using fn::GVArray_Typed; +using fn::GVArrayPtr; +using fn::GVMutableArray; +using fn::GVMutableArray_GSpan; +using fn::GVMutableArray_Typed; +using fn::GVMutableArrayPtr; + +/** + * This class exists to separate the memory management details of the geometry nodes evaluator from + * the node execution functions and related utilities. + */ +class GeoNodeExecParamsProvider { + public: + DNode dnode; + const Object *self_object = nullptr; + const ModifierData *modifier = nullptr; + Depsgraph *depsgraph = nullptr; + + /** + * Returns true when the node is allowed to get/extract the input value. The identifier is + * expected to be valid. This may return false if the input value has been consumed already. + */ + virtual bool can_get_input(StringRef identifier) const = 0; + + /** + * Returns true when the node is allowed to set the output value. The identifier is expected to + * be valid. This may return false if the output value has been set already. + */ + virtual bool can_set_output(StringRef identifier) const = 0; + + /** + * Take ownership of an input value. The caller is responsible for destructing the value. It does + * not have to be freed, because the memory is managed by the geometry nodes evaluator. + */ + virtual GMutablePointer extract_input(StringRef identifier) = 0; + + /** + * Similar to #extract_input, but has to be used for multi-input sockets. + */ + virtual Vector<GMutablePointer> extract_multi_input(StringRef identifier) = 0; + + /** + * Get the input value for the identifier without taking ownership of it. + */ + virtual GPointer get_input(StringRef identifier) const = 0; + + /** + * Prepare a memory buffer for an output value of the node. The returned memory has to be + * initialized by the caller. The identifier and type are expected to be correct. + */ + virtual GMutablePointer alloc_output_value(StringRef identifier, const CPPType &type) = 0; +}; class GeoNodeExecParams { private: - const DNode node_; - GValueMap<StringRef> &input_values_; - GValueMap<StringRef> &output_values_; - const PersistentDataHandleMap &handle_map_; - const Object *self_object_; - const ModifierData *modifier_; - Depsgraph *depsgraph_; + GeoNodeExecParamsProvider *provider_; public: - GeoNodeExecParams(const DNode node, - GValueMap<StringRef> &input_values, - GValueMap<StringRef> &output_values, - const PersistentDataHandleMap &handle_map, - const Object *self_object, - const ModifierData *modifier, - Depsgraph *depsgraph) - : node_(node), - input_values_(input_values), - output_values_(output_values), - handle_map_(handle_map), - self_object_(self_object), - modifier_(modifier), - depsgraph_(depsgraph) + GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider) { } @@ -94,9 +117,9 @@ class GeoNodeExecParams { GMutablePointer extract_input(StringRef identifier) { #ifdef DEBUG - this->check_extract_input(identifier); + this->check_input_access(identifier); #endif - return input_values_.extract(identifier); + return provider_->extract_input(identifier); } /** @@ -107,9 +130,10 @@ class GeoNodeExecParams { template<typename T> T extract_input(StringRef identifier) { #ifdef DEBUG - this->check_extract_input(identifier, &CPPType::get<T>()); + this->check_input_access(identifier, &CPPType::get<T>()); #endif - return input_values_.extract<T>(identifier); + GMutablePointer gvalue = this->extract_input(identifier); + return gvalue.relocate_out<T>(); } /** @@ -119,18 +143,10 @@ class GeoNodeExecParams { */ template<typename T> Vector<T> extract_multi_input(StringRef identifier) { + Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier); Vector<T> values; - int index = 0; - while (true) { - std::string sub_identifier = identifier; - if (index > 0) { - sub_identifier += "[" + std::to_string(index) + "]"; - } - if (!input_values_.contains(sub_identifier)) { - break; - } - values.append(input_values_.extract<T, StringRef>(sub_identifier)); - index++; + for (GMutablePointer gvalue : gvalues) { + values.append(gvalue.relocate_out<T>()); } return values; } @@ -141,33 +157,11 @@ class GeoNodeExecParams { template<typename T> const T &get_input(StringRef identifier) const { #ifdef DEBUG - this->check_extract_input(identifier, &CPPType::get<T>()); -#endif - return input_values_.lookup<T>(identifier); - } - - /** - * Move-construct a new value based on the given value and store it for the given socket - * identifier. - */ - void set_output_by_move(StringRef identifier, GMutablePointer value) - { -#ifdef DEBUG - BLI_assert(value.type() != nullptr); - BLI_assert(value.get() != nullptr); - this->check_set_output(identifier, *value.type()); -#endif - output_values_.add_new_by_move(identifier, value); - } - - void set_output_by_copy(StringRef identifier, GPointer value) - { -#ifdef DEBUG - BLI_assert(value.type() != nullptr); - BLI_assert(value.get() != nullptr); - this->check_set_output(identifier, *value.type()); + this->check_input_access(identifier, &CPPType::get<T>()); #endif - output_values_.add_new_by_copy(identifier, value); + GPointer gvalue = provider_->get_input(identifier); + BLI_assert(gvalue.is_type<T>()); + return *(const T *)gvalue.get(); } /** @@ -175,10 +169,13 @@ class GeoNodeExecParams { */ template<typename T> void set_output(StringRef identifier, T &&value) { + using StoredT = std::decay_t<T>; + const CPPType &type = CPPType::get<std::decay_t<T>>(); #ifdef DEBUG - this->check_set_output(identifier, CPPType::get<std::decay_t<T>>()); + this->check_output_access(identifier, type); #endif - output_values_.add_new(identifier, std::forward<T>(value)); + GMutablePointer gvalue = provider_->alloc_output_value(identifier, type); + new (gvalue.get()) StoredT(std::forward<T>(value)); } /** @@ -186,22 +183,17 @@ class GeoNodeExecParams { */ const bNode &node() const { - return *node_->bnode(); - } - - const PersistentDataHandleMap &handle_map() const - { - return handle_map_; + return *provider_->dnode->bnode(); } const Object *self_object() const { - return self_object_; + return provider_->self_object; } Depsgraph *depsgraph() const { - return depsgraph_; + return provider_->depsgraph; } /** @@ -217,20 +209,21 @@ class GeoNodeExecParams { * \note This will add an error message if the string socket is active and * the input attribute does not exist. */ - ReadAttributePtr get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const CustomDataType type, - const void *default_value) const; + GVArrayPtr get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const CustomDataType type, + const void *default_value) const; template<typename T> - bke::TypedReadAttribute<T> get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const T &default_value) const + GVArray_Typed<T> get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const T &default_value) const { const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>()); - return this->get_input_attribute(name, component, domain, type, &default_value); + GVArrayPtr varray = this->get_input_attribute(name, component, domain, type, &default_value); + return GVArray_Typed<T>(std::move(varray)); } /** @@ -247,8 +240,8 @@ class GeoNodeExecParams { private: /* Utilities for detecting common errors at when using this class. */ - void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const; - void check_set_output(StringRef identifier, const CPPType &value_type) const; + void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const; + void check_output_access(StringRef identifier, const CPPType &value_type) const; /* Find the active socket socket with the input name (not the identifier). */ const bNodeSocket *find_available_socket(const StringRef name) const; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index bf6bf34325a..8e4839310d1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -263,53 +263,59 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") -DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") -DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") +DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") +DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") -DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") -DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") -DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") -DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") -DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") -DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "") -DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "") -DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "") +DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "") -DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "") -DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "") -DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "") -DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "") -DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "") -DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "") +DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") +DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") +DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") +DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "") -DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "") -DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") +DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "") -DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") -DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "") -DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "") -DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") +DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") +DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") +DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "") +DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "") +DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "") +DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "") +DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") +DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") +DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") +DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") +DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") +DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/NOD_type_conversions.hh b/source/blender/nodes/NOD_type_conversions.hh index 34225208fe6..ec4859f0657 100644 --- a/source/blender/nodes/NOD_type_conversions.hh +++ b/source/blender/nodes/NOD_type_conversions.hh @@ -72,6 +72,10 @@ class DataTypeConversions { const CPPType &to_type, const void *from_value, void *to_value) const; + + fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const; + + fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const; }; const DataTypeConversions &get_implicit_type_conversions(); diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c index 085b5c463b9..19815d01278 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.c @@ -205,6 +205,12 @@ static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode) } } +static bool composite_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, + bNodeTreeType *UNUSED(ntreetype)) +{ + return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA); +} + bNodeTreeType *ntreeType_Composite; void register_node_tree_type_cmp(void) @@ -227,6 +233,7 @@ void register_node_tree_type_cmp(void) tt->update = update; tt->get_from_context = composite_get_from_context; tt->node_add_init = composite_node_add_init; + tt->valid_socket_type = composite_node_tree_socket_type_valid; tt->rna_ext.srna = &RNA_CompositorNodeTree; diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 6d3b1d55005..f4cd00b88ed 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -84,6 +84,34 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); } +static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) +{ + /* Geometry, string, object, material, texture and collection sockets can only be connected to + * themselves. The other types can be converted between each other. */ + if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) && + ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) { + return true; + } + return (link->tosock->type == link->fromsock->type); +} + +static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, + bNodeTreeType *UNUSED(ntreetype)) +{ + return ELEM(socket_type, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_BOOLEAN, + SOCK_INT, + SOCK_STRING, + SOCK_OBJECT, + SOCK_GEOMETRY, + SOCK_COLLECTION, + SOCK_TEXTURE, + SOCK_MATERIAL); +} + void register_node_tree_type_geo(void) { bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>( @@ -97,6 +125,8 @@ void register_node_tree_type_geo(void) tt->update = geometry_node_tree_update; tt->get_from_context = geometry_node_tree_get_from_context; tt->foreach_nodeclass = foreach_nodeclass; + tt->valid_socket_type = geometry_node_tree_socket_type_valid; + tt->validate_link = geometry_node_tree_validate_link; ntreeTypeAdd(tt); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc index f10b81a33b4..720ca9731a8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc @@ -15,6 +15,7 @@ */ #include "BLI_math_rotation.h" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -50,91 +51,95 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, namespace blender::nodes { -static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors, - const FloatReadAttribute &factors, +static void align_rotations_auto_pivot(const VArray<float3> &vectors, + const VArray<float> &factors, const float3 local_main_axis, - MutableSpan<float3> rotations) + const MutableSpan<float3> rotations) { - for (const int i : IndexRange(vectors.size())) { - const float3 vector = vectors[i]; - if (is_zero_v3(vector)) { - continue; - } + parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { + for (const int i : range) { + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + continue; + } - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float3 old_axis; - mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + float old_rotation[3][3]; + eul_to_mat3(old_rotation, rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); - const float3 new_axis = vector.normalized(); - float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis); - if (is_zero_v3(rotation_axis)) { - /* The vectors are linearly dependent, so we fall back to another axis. */ - rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0)); + const float3 new_axis = vector.normalized(); + float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis); if (is_zero_v3(rotation_axis)) { - /* This is now guaranteed to not be zero. */ - rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0)); + /* The vectors are linearly dependent, so we fall back to another axis. */ + rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0)); + if (is_zero_v3(rotation_axis)) { + /* This is now guaranteed to not be zero. */ + rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0)); + } } - } - const float full_angle = angle_normalized_v3v3(old_axis, new_axis); - const float angle = factors[i] * full_angle; + const float full_angle = angle_normalized_v3v3(old_axis, new_axis); + const float angle = factors[i] * full_angle; - float rotation[3][3]; - axis_angle_to_mat3(rotation, rotation_axis, angle); + float rotation[3][3]; + axis_angle_to_mat3(rotation, rotation_axis, angle); - float new_rotation_matrix[3][3]; - mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); - float3 new_rotation; - mat3_to_eul(new_rotation, new_rotation_matrix); + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); - rotations[i] = new_rotation; - } + rotations[i] = new_rotation; + } + }); } -static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors, - const FloatReadAttribute &factors, +static void align_rotations_fixed_pivot(const VArray<float3> &vectors, + const VArray<float> &factors, const float3 local_main_axis, const float3 local_pivot_axis, - MutableSpan<float3> rotations) + const MutableSpan<float3> rotations) { if (local_main_axis == local_pivot_axis) { /* Can't compute any meaningful rotation angle in this case. */ return; } - for (const int i : IndexRange(vectors.size())) { - const float3 vector = vectors[i]; - if (is_zero_v3(vector)) { - continue; - } + parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) { + for (const int i : range) { + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + continue; + } - float old_rotation[3][3]; - eul_to_mat3(old_rotation, rotations[i]); - float3 old_axis; - mul_v3_m3v3(old_axis, old_rotation, local_main_axis); - float3 pivot_axis; - mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis); - - float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis); - if (full_angle > M_PI) { - /* Make sure the point is rotated as little as possible. */ - full_angle -= 2.0f * M_PI; - } - const float angle = factors[i] * full_angle; + float old_rotation[3][3]; + eul_to_mat3(old_rotation, rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + float3 pivot_axis; + mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis); + + float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis); + if (full_angle > M_PI) { + /* Make sure the point is rotated as little as possible. */ + full_angle -= 2.0f * M_PI; + } + const float angle = factors[i] * full_angle; - float rotation[3][3]; - axis_angle_to_mat3(rotation, pivot_axis, angle); + float rotation[3][3]; + axis_angle_to_mat3(rotation, pivot_axis, angle); - float new_rotation_matrix[3][3]; - mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); - float3 new_rotation; - mat3_to_eul(new_rotation, new_rotation_matrix); + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); - rotations[i] = new_rotation; - } + rotations[i] = new_rotation; + } + }); } static void align_rotations_on_component(GeometryComponent &component, @@ -144,30 +149,30 @@ static void align_rotations_on_component(GeometryComponent &component, const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *) node.storage; - OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output( - "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - if (!rotation_attribute) { + OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>( + "rotation", ATTR_DOMAIN_POINT, {0, 0, 0}); + if (!rotations) { return; } - MutableSpan<float3> rotations = rotation_attribute->get_span<float3>(); - FloatReadAttribute factors = params.get_input_attribute<float>( + GVArray_Typed<float> factors = params.get_input_attribute<float>( "Factor", component, ATTR_DOMAIN_POINT, 1.0f); - Float3ReadAttribute vectors = params.get_input_attribute<float3>( + GVArray_Typed<float3> vectors = params.get_input_attribute<float3>( "Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1}); float3 local_main_axis{0, 0, 0}; local_main_axis[storage.axis] = 1; if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) { - align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations); + align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span()); } else { float3 local_pivot_axis{0, 0, 0}; local_pivot_axis[storage.pivot_axis - 1] = 1; - align_rotations_fixed_pivot(vectors, factors, local_main_axis, local_pivot_axis, rotations); + align_rotations_fixed_pivot( + vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span()); } - rotation_attribute.apply_span_and_save(); + rotations.save(); } static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params) @@ -183,6 +188,9 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params) align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc index 95fa24c8bac..21538db5455 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -112,10 +112,13 @@ template<> inline Color4f clamp_value(const Color4f val, const Color4f min, cons } template<typename T> -static void clamp_attribute(Span<T> read_span, MutableSpan<T> span, const T min, const T max) +static void clamp_attribute(const VArray<T> &inputs, + const MutableSpan<T> outputs, + const T min, + const T max) { - for (const int i : span.index_range()) { - span[i] = clamp_value<T>(read_span[i], min, max); + for (const int i : IndexRange(outputs.size())) { + outputs[i] = clamp_value<T>(inputs[i], min, max); } } @@ -123,13 +126,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef source_name, StringRef result_name) { - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name); - if (source_attribute) { - return source_attribute->domain(); + std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); + if (source_info) { + return source_info->domain; } return ATTR_DOMAIN_POINT; } @@ -154,10 +157,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam const AttributeDomain domain = get_result_domain(component, attribute_name, result_name); const int operation = static_cast<int>(storage.operation); - ReadAttributePtr attribute_input = component.attribute_try_get_for_read( + GVArrayPtr attribute_input = component.attribute_try_get_for_read( attribute_name, domain, data_type); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( result_name, domain, data_type); if (!attribute_result) { @@ -169,8 +172,6 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam switch (data_type) { case CD_PROP_FLOAT3: { - Span<float3> read_span = attribute_input->get_span<float3>(); - MutableSpan<float3> span = attribute_result->get_span_for_write_only<float3>(); float3 min = params.get_input<float3>("Min"); float3 max = params.get_input<float3>("Max"); if (operation == NODE_CLAMP_RANGE) { @@ -184,38 +185,35 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.z, max.z); } } - clamp_attribute<float3>(read_span, span, min, max); + MutableSpan<float3> results = attribute_result.as_span<float3>(); + clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max); break; } case CD_PROP_FLOAT: { - Span<float> read_span = attribute_input->get_span<float>(); - MutableSpan<float> span = attribute_result->get_span_for_write_only<float>(); const float min = params.get_input<float>("Min_001"); const float max = params.get_input<float>("Max_001"); + MutableSpan<float> results = attribute_result.as_span<float>(); if (operation == NODE_CLAMP_RANGE && min > max) { - clamp_attribute<float>(read_span, span, max, min); + clamp_attribute<float>(attribute_input->typed<float>(), results, max, min); } else { - clamp_attribute<float>(read_span, span, min, max); + clamp_attribute<float>(attribute_input->typed<float>(), results, min, max); } break; } case CD_PROP_INT32: { - Span<int> read_span = attribute_input->get_span<int>(); - MutableSpan<int> span = attribute_result->get_span_for_write_only<int>(); const int min = params.get_input<int>("Min_002"); const int max = params.get_input<int>("Max_002"); + MutableSpan<int> results = attribute_result.as_span<int>(); if (operation == NODE_CLAMP_RANGE && min > max) { - clamp_attribute<int>(read_span, span, max, min); + clamp_attribute<int>(attribute_input->typed<int>(), results, max, min); } else { - clamp_attribute<int>(read_span, span, min, max); + clamp_attribute<int>(attribute_input->typed<int>(), results, min, max); } break; } case CD_PROP_COLOR: { - Span<Color4f> read_span = attribute_input->get_span<Color4f>(); - MutableSpan<Color4f> span = attribute_result->get_span_for_write_only<Color4f>(); Color4f min = params.get_input<Color4f>("Min_003"); Color4f max = params.get_input<Color4f>("Max_003"); if (operation == NODE_CLAMP_RANGE) { @@ -232,7 +230,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.a, max.a); } } - clamp_attribute<Color4f>(read_span, span, min, max); + MutableSpan<Color4f> results = attribute_result.as_span<Color4f>(); + clamp_attribute<Color4f>(attribute_input->typed<Color4f>(), results, min, max); break; } default: { @@ -241,7 +240,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam } } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_clamp_exec(GeoNodeExecParams params) @@ -256,6 +255,9 @@ static void geo_node_attribute_clamp_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index 2b913beb670..26ddb0da515 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "BKE_colorband.h" #include "UI_interface.h" @@ -47,15 +49,15 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the input attribute's domain if it exists. */ - ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name); - if (input_attribute) { - return input_attribute->domain(); + std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name); + if (source_info) { + return source_info->domain; } return ATTR_DOMAIN_POINT; @@ -71,27 +73,27 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon /* Always output a color attribute for now. We might want to allow users to customize. * Using the type of an existing attribute could work, but does not have a real benefit * currently. */ - const CustomDataType result_type = CD_PROP_COLOR; const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( - result_name, result_domain, result_type); + OutputAttribute_Typed<Color4f> attribute_result = + component.attribute_try_get_for_output_only<Color4f>(result_name, result_domain); if (!attribute_result) { return; } - FloatReadAttribute attribute_in = component.attribute_get_for_read<float>( + GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( input_name, result_domain, 0.0f); - Span<float> data_in = attribute_in.get_span(); - MutableSpan<Color4f> data_out = attribute_result->get_span_for_write_only<Color4f>(); + MutableSpan<Color4f> results = attribute_result.as_span(); ColorBand *color_ramp = &node_storage->color_ramp; - for (const int i : data_in.index_range()) { - BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]); - } + parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + for (const int i : range) { + BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]); + } + }); - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) @@ -106,6 +108,9 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); } + if (geometry_set.has<CurveComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); + } params.set_output("Geometry", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc index e9e07d34c17..d8c52d16f41 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc @@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -94,27 +94,25 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa } const AttributeDomain result_domain = get_result_domain(component, params, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( - result_name, result_domain, CD_PROP_FLOAT3); + OutputAttribute_Typed<float3> attribute_result = + component.attribute_try_get_for_output_only<float3>(result_name, result_domain); if (!attribute_result) { return; } - FloatReadAttribute attribute_x = params.get_input_attribute<float>( + GVArray_Typed<float> attribute_x = params.get_input_attribute<float>( "X", component, result_domain, 0.0f); - FloatReadAttribute attribute_y = params.get_input_attribute<float>( + GVArray_Typed<float> attribute_y = params.get_input_attribute<float>( "Y", component, result_domain, 0.0f); - FloatReadAttribute attribute_z = params.get_input_attribute<float>( + GVArray_Typed<float> attribute_z = params.get_input_attribute<float>( "Z", component, result_domain, 0.0f); - MutableSpan<float3> results = attribute_result->get_span_for_write_only<float3>(); - for (const int i : results.index_range()) { + for (const int i : IndexRange(attribute_result->size())) { const float x = attribute_x[i]; const float y = attribute_y[i]; const float z = attribute_z[i]; - const float3 result = float3(x, y, z); - results[i] = result; + attribute_result->set(i, {x, y, z}); } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params) @@ -129,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc index fe4045c39a6..a2ff1668a06 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -81,21 +81,18 @@ static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *n nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage)); } -static void do_math_operation(const FloatReadAttribute &input_a, - const FloatReadAttribute &input_b, +static void do_math_operation(const VArray<float> &input_a, + const VArray<float> &input_b, const FloatCompareOperation operation, MutableSpan<bool> span_result) { const int size = input_a.size(); - Span<float> span_a = input_a.get_span(); - Span<float> span_b = input_b.get_span(); - if (try_dispatch_float_math_fl_fl_to_bool( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { for (const int i : IndexRange(size)) { - const float a = span_a[i]; - const float b = span_b[i]; + const float a = input_a[i]; + const float b = input_b[i]; const bool out = math_function(a, b); span_result[i] = out; } @@ -107,8 +104,8 @@ static void do_math_operation(const FloatReadAttribute &input_a, BLI_assert(false); } -static void do_equal_operation_float(const FloatReadAttribute &input_a, - const FloatReadAttribute &input_b, +static void do_equal_operation_float(const VArray<float> &input_a, + const VArray<float> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -120,8 +117,8 @@ static void do_equal_operation_float(const FloatReadAttribute &input_a, } } -static void do_equal_operation_float3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, +static void do_equal_operation_float3(const VArray<float3> &input_a, + const VArray<float3> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -134,8 +131,8 @@ static void do_equal_operation_float3(const Float3ReadAttribute &input_a, } } -static void do_equal_operation_color4f(const Color4fReadAttribute &input_a, - const Color4fReadAttribute &input_b, +static void do_equal_operation_color4f(const VArray<Color4f> &input_a, + const VArray<Color4f> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -148,8 +145,8 @@ static void do_equal_operation_color4f(const Color4fReadAttribute &input_a, } } -static void do_equal_operation_bool(const BooleanReadAttribute &input_a, - const BooleanReadAttribute &input_b, +static void do_equal_operation_bool(const VArray<bool> &input_a, + const VArray<bool> &input_b, const float UNUSED(threshold), MutableSpan<bool> span_result) { @@ -161,8 +158,8 @@ static void do_equal_operation_bool(const BooleanReadAttribute &input_a, } } -static void do_not_equal_operation_float(const FloatReadAttribute &input_a, - const FloatReadAttribute &input_b, +static void do_not_equal_operation_float(const VArray<float> &input_a, + const VArray<float> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -174,8 +171,8 @@ static void do_not_equal_operation_float(const FloatReadAttribute &input_a, } } -static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, +static void do_not_equal_operation_float3(const VArray<float3> &input_a, + const VArray<float3> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -188,8 +185,8 @@ static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a, } } -static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a, - const Color4fReadAttribute &input_b, +static void do_not_equal_operation_color4f(const VArray<Color4f> &input_a, + const VArray<Color4f> &input_b, const float threshold, MutableSpan<bool> span_result) { @@ -202,8 +199,8 @@ static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a, } } -static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a, - const BooleanReadAttribute &input_b, +static void do_not_equal_operation_bool(const VArray<bool> &input_a, + const VArray<bool> &input_b, const float UNUSED(threshold), MutableSpan<bool> span_result) { @@ -237,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -254,20 +251,19 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx node_storage->operation); const std::string result_name = params.get_input<std::string>("Result"); - const CustomDataType result_type = CD_PROP_BOOL; const AttributeDomain result_domain = get_result_domain(component, params, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( - result_name, result_domain, result_type); + OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>( + result_name, result_domain); if (!attribute_result) { return; } const CustomDataType input_data_type = get_data_type(component, params, *node_storage); - ReadAttributePtr attribute_a = params.get_input_attribute( + GVArrayPtr attribute_a = params.get_input_attribute( "A", component, result_domain, input_data_type, nullptr); - ReadAttributePtr attribute_b = params.get_input_attribute( + GVArrayPtr attribute_b = params.get_input_attribute( "B", component, result_domain, input_data_type, nullptr); if (!attribute_a || !attribute_b) { @@ -275,7 +271,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx return; } - MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>(); + MutableSpan<bool> result_span = attribute_result.as_span(); /* Use specific types for correct equality operations, but for other operations we use implicit * conversions and float comparison. In other words, the comparison is not element-wise. */ @@ -283,38 +279,47 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx const float threshold = params.get_input<float>("Threshold"); if (operation == NODE_FLOAT_COMPARE_EQUAL) { if (input_data_type == CD_PROP_FLOAT) { - do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span); + do_equal_operation_float( + attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span); } else if (input_data_type == CD_PROP_FLOAT3) { - do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span); + do_equal_operation_float3( + attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span); + do_equal_operation_color4f( + attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span); } else if (input_data_type == CD_PROP_BOOL) { - do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span); + do_equal_operation_bool( + attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span); } } else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) { if (input_data_type == CD_PROP_FLOAT) { - do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span); + do_not_equal_operation_float( + attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span); } else if (input_data_type == CD_PROP_FLOAT3) { - do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span); + do_not_equal_operation_float3( + attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span); + do_not_equal_operation_color4f( + attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span); } else if (input_data_type == CD_PROP_BOOL) { - do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span); + do_not_equal_operation_bool( + attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span); } } } else { - do_math_operation(*attribute_a, *attribute_b, operation, result_span); + do_math_operation( + attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span); } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_compare_exec(GeoNodeExecParams params) @@ -329,6 +334,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc index 19c3aaa9c85..7b40456b180 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc @@ -35,8 +35,10 @@ static void geo_node_attribute_convert_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "domain", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE); } static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node) @@ -44,26 +46,27 @@ static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert), __func__); - data->data_type = CD_PROP_FLOAT; + data->data_type = CD_AUTO_FROM_NAME; data->domain = ATTR_DOMAIN_AUTO; node->storage = data; } namespace blender::nodes { -static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef source_name, - StringRef result_name) +static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component, + const StringRef source_name, + const StringRef result_name) { - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return *result_info; } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name); - if (source_attribute) { - return source_attribute->domain(); + std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); + if (source_info) { + return *source_info; } - return ATTR_DOMAIN_POINT; + /* The node won't do anything in this case, but we still have to return a value. */ + return AttributeMetaData{ATTR_DOMAIN_POINT, CD_PROP_BOOL}; } static bool conversion_can_be_skipped(const GeometryComponent &component, @@ -75,14 +78,14 @@ static bool conversion_can_be_skipped(const GeometryComponent &component, if (source_name != result_name) { return false; } - ReadAttributePtr read_attribute = component.attribute_try_get_for_read(source_name); - if (!read_attribute) { + std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name); + if (!info) { return false; } - if (read_attribute->domain() != result_domain) { + if (info->domain != result_domain) { return false; } - if (read_attribute->cpp_type() != *bke::custom_data_type_to_cpp_type(result_type)) { + if (info->data_type != result_type) { return false; } return true; @@ -92,19 +95,20 @@ static void attribute_convert_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms, const StringRef source_name, const StringRef result_name, - const CustomDataType result_type, + const CustomDataType data_type, const AttributeDomain domain) { - const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? - get_result_domain( - component, source_name, result_name) : - domain; + const AttributeMetaData auto_info = get_result_domain_and_type( + component, source_name, result_name); + const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? auto_info.domain : domain; + const CustomDataType result_type = (data_type == CD_AUTO_FROM_NAME) ? auto_info.data_type : + data_type; if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) { return; } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + GVArrayPtr source_attribute = component.attribute_try_get_for_read( source_name, result_domain, result_type); if (!source_attribute) { params.error_message_add(NodeWarningType::Error, @@ -112,25 +116,22 @@ static void attribute_convert_calc(GeometryComponent &component, return; } - OutputAttributePtr result_attribute = component.attribute_try_get_for_output( + OutputAttribute result_attribute = component.attribute_try_get_for_output_only( result_name, result_domain, result_type); if (!result_attribute) { return; } - fn::GSpan source_span = source_attribute->get_span(); - fn::GMutableSpan result_span = result_attribute->get_span_for_write_only(); - if (source_span.is_empty() || result_span.is_empty()) { - return; - } + GVArray_GSpan source_span{*source_attribute}; + GMutableSpan result_span = result_attribute.as_span(); + BLI_assert(source_span.size() == result_span.size()); const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type); BLI_assert(cpp_type != nullptr); cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size()); - - result_attribute.apply_span_and_save(); + result_attribute.save(); } static void geo_node_attribute_convert_exec(GeoNodeExecParams params) @@ -166,6 +167,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params) data_type, domain); } + if (geometry_set.has<CurveComponent>()) { + attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(), + params, + source_name, + result_name, + data_type, + domain); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc new file mode 100644 index 00000000000..2fc86269797 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc @@ -0,0 +1,231 @@ +/* + * 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_blenlib.h" +#include "BLI_task.hh" + +#include "BKE_colortools.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_curve_map_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Attribute")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_curve_map_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_curve_map_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + bNode *node = (bNode *)ptr->data; + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; + switch (data->data_type) { + case CD_PROP_FLOAT: + uiTemplateCurveMapping(layout, ptr, "curve_vec", 0, false, false, false, false); + break; + case CD_PROP_FLOAT3: + uiTemplateCurveMapping(layout, ptr, "curve_vec", 'v', false, false, false, false); + break; + case CD_PROP_COLOR: + uiTemplateCurveMapping(layout, ptr, "curve_rgb", 'c', false, false, false, false); + break; + } +} + +static void geo_node_attribute_curve_map_free_storage(bNode *node) +{ + if (node->storage) { + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; + BKE_curvemapping_free(data->curve_vec); + BKE_curvemapping_free(data->curve_rgb); + MEM_freeN(node->storage); + } +} + +static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntree), + bNode *dest_node, + const bNode *src_node) +{ + dest_node->storage = MEM_dupallocN(src_node->storage); + NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage; + NodeAttributeCurveMap *dest_data = (NodeAttributeCurveMap *)dest_node->storage; + dest_data->curve_vec = BKE_curvemapping_copy(src_data->curve_vec); + dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb); +} + +static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap), + __func__); + + data->data_type = CD_PROP_FLOAT; + data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f); + data->curve_vec->cur = 3; + data->curve_rgb = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); + node->storage = data; +} + +static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + /* Set the active curve when data type is changed. */ + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; + if (data->data_type == CD_PROP_FLOAT) { + data->curve_vec->cur = 3; + } + else if (data->data_type == CD_PROP_FLOAT3) { + data->curve_vec->cur = 0; + } +} + +namespace blender::nodes { + +static AttributeDomain get_result_domain(const GeometryComponent &component, + StringRef input_name, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute.domain; + } + + /* Otherwise use the input attribute's domain if it exists. */ + ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name); + if (input_attribute) { + return input_attribute.domain; + } + + return ATTR_DOMAIN_POINT; +} + +static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) +{ + const bNode &bnode = params.node(); + NodeAttributeCurveMap &node_storage = *(NodeAttributeCurveMap *)bnode.storage; + const std::string result_name = params.get_input<std::string>("Result"); + const std::string input_name = params.get_input<std::string>("Attribute"); + + const CustomDataType result_type = (CustomDataType)node_storage.data_type; + const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); + + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( + result_name, result_domain, result_type); + if (!attribute_result) { + return; + } + + switch (result_type) { + case CD_PROP_FLOAT: { + const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec; + GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( + input_name, result_domain, float(0.0f)); + MutableSpan<float> results = attribute_result.as_span<float>(); + parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + for (const int i : range) { + results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]); + } + }); + break; + } + case CD_PROP_FLOAT3: { + const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec; + GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>( + input_name, result_domain, float3(0.0f)); + MutableSpan<float3> results = attribute_result.as_span<float3>(); + parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + for (const int i : range) { + BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]); + } + }); + break; + } + case CD_PROP_COLOR: { + const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; + GVArray_Typed<Color4f> attribute_in = component.attribute_get_for_read<Color4f>( + input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f)); + MutableSpan<Color4f> results = attribute_result.as_span<Color4f>(); + parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + for (const int i : range) { + BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); + } + }); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + + attribute_result.save(); +} + +static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params) +{ + const bNode &bnode = params.node(); + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage; + BKE_curvemapping_init(data->curve_vec); + BKE_curvemapping_init(data->curve_rgb); + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); + } + if (geometry_set.has<PointCloudComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); + } + if (geometry_set.has<CurveComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_curve_map() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_curve_map_in, geo_node_attribute_curve_map_out); + node_type_update(&ntype, geo_node_attribute_curve_map_update); + node_type_init(&ntype, geo_node_attribute_curve_map_init); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_storage(&ntype, + "NodeAttributeCurveMap", + geo_node_attribute_curve_map_free_storage, + geo_node_attribute_curve_map_copy_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec; + ntype.draw_buttons = geo_node_attribute_curve_map_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc index 7b4483a31a1..60522fd0f72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -68,13 +68,12 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node namespace blender::nodes { -static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef attribute_name) +static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name); + if (result_info) { + return result_info->domain; } return ATTR_DOMAIN_POINT; } @@ -93,7 +92,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams get_result_domain(component, attribute_name) : domain; - OutputAttributePtr attribute = component.attribute_try_get_for_output( + OutputAttribute attribute = component.attribute_try_get_for_output_only( attribute_name, result_domain, data_type); if (!attribute) { return; @@ -102,38 +101,34 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams switch (data_type) { case CD_PROP_FLOAT: { const float value = params.get_input<float>("Value_001"); - MutableSpan<float> attribute_span = attribute->get_span_for_write_only<float>(); - attribute_span.fill(value); + attribute->fill(&value); break; } case CD_PROP_FLOAT3: { const float3 value = params.get_input<float3>("Value"); - MutableSpan<float3> attribute_span = attribute->get_span_for_write_only<float3>(); - attribute_span.fill(value); + attribute->fill(&value); break; } case CD_PROP_COLOR: { const Color4f value = params.get_input<Color4f>("Value_002"); - MutableSpan<Color4f> attribute_span = attribute->get_span_for_write_only<Color4f>(); - attribute_span.fill(value); + attribute->fill(&value); break; } case CD_PROP_BOOL: { const bool value = params.get_input<bool>("Value_003"); - MutableSpan<bool> attribute_span = attribute->get_span_for_write_only<bool>(); - attribute_span.fill(value); + attribute->fill(&value); break; } case CD_PROP_INT32: { const int value = params.get_input<int>("Value_004"); - MutableSpan<int> attribute_span = attribute->get_span_for_write_only<int>(); - attribute_span.fill(value); + attribute->fill(&value); + break; } default: break; } - attribute.apply_span_and_save(); + attribute.save(); } static void geo_node_attribute_fill_exec(GeoNodeExecParams params) @@ -148,6 +143,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc index fdbdadf90b6..40fe675bd6c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc @@ -15,6 +15,7 @@ */ #include "BLI_math_base_safe.h" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -192,8 +193,8 @@ static float map_smootherstep(const float value, return min_to + factor_mapped * (max_to - min_to); } -static void map_range_float(FloatReadAttribute attribute_input, - FloatWriteAttribute attribute_result, +static void map_range_float(const VArray<float> &attribute_input, + MutableSpan<float> results, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); @@ -204,33 +205,40 @@ static void map_range_float(FloatReadAttribute attribute_input, const float min_to = params.get_input<float>("To Min"); const float max_to = params.get_input<float>("To Max"); - Span<float> span = attribute_input.get_span(); - MutableSpan<float> result_span = attribute_result.get_span(); + VArray_Span<float> span{attribute_input}; switch (interpolation_type) { case NODE_MAP_RANGE_LINEAR: { - for (int i : span.index_range()) { - result_span[i] = map_linear(span[i], min_from, max_from, min_to, max_to); - } + parallel_for(span.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + results[i] = map_linear(span[i], min_from, max_from, min_to, max_to); + } + }); break; } case NODE_MAP_RANGE_STEPPED: { const float steps = params.get_input<float>("Steps"); - for (int i : span.index_range()) { - result_span[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps); + } + }); break; } case NODE_MAP_RANGE_SMOOTHSTEP: { - for (int i : span.index_range()) { - result_span[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to); + } + }); break; } case NODE_MAP_RANGE_SMOOTHERSTEP: { - for (int i : span.index_range()) { - result_span[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to); + } + }); break; } } @@ -241,14 +249,16 @@ static void map_range_float(FloatReadAttribute attribute_input, const float clamp_min = min_to < max_to ? min_to : max_to; const float clamp_max = min_to < max_to ? max_to : min_to; - for (int i : result_span.index_range()) { - result_span[i] = std::clamp(result_span[i], clamp_min, clamp_max); - } + parallel_for(results.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + results[i] = std::clamp(results[i], clamp_min, clamp_max); + } + }); } } -static void map_range_float3(Float3ReadAttribute attribute_input, - Float3WriteAttribute attribute_result, +static void map_range_float3(const VArray<float3> &attribute_input, + const MutableSpan<float3> results, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); @@ -259,44 +269,51 @@ static void map_range_float3(Float3ReadAttribute attribute_input, const float3 min_to = params.get_input<float3>("To Min_001"); const float3 max_to = params.get_input<float3>("To Max_001"); - Span<float3> span = attribute_input.get_span(); - MutableSpan<float3> result_span = attribute_result.get_span(); + VArray_Span<float3> span{attribute_input}; switch (interpolation_type) { case NODE_MAP_RANGE_LINEAR: { - for (int i : span.index_range()) { - result_span[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - result_span[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - result_span[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); + results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); + results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); + } + }); break; } case NODE_MAP_RANGE_STEPPED: { const float3 steps = params.get_input<float3>("Steps_001"); - for (int i : span.index_range()) { - result_span[i].x = map_stepped( - span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x); - result_span[i].y = map_stepped( - span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y); - result_span[i].z = map_stepped( - span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i].x = map_stepped( + span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x); + results[i].y = map_stepped( + span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y); + results[i].z = map_stepped( + span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z); + } + }); break; } case NODE_MAP_RANGE_SMOOTHSTEP: { - for (int i : span.index_range()) { - result_span[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - result_span[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - result_span[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); + results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); + results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); + } + }); break; } case NODE_MAP_RANGE_SMOOTHERSTEP: { - for (int i : span.index_range()) { - result_span[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); - result_span[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); - result_span[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); - } + parallel_for(span.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x); + results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y); + results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z); + } + }); break; } } @@ -313,8 +330,8 @@ static void map_range_float3(Float3ReadAttribute attribute_input, clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z; clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z; - for (int i : result_span.index_range()) { - clamp_v3_v3v3(result_span[i], clamp_min, clamp_max); + for (int i : results.index_range()) { + clamp_v3_v3v3(results[i], clamp_min, clamp_max); } } } @@ -323,13 +340,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef source_name, StringRef result_name) { - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name); - if (source_attribute) { - return source_attribute->domain(); + std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name); + if (source_info) { + return source_info->domain; } return ATTR_DOMAIN_POINT; } @@ -349,8 +366,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP const AttributeDomain domain = get_result_domain(component, input_name, result_name); - ReadAttributePtr attribute_input = component.attribute_try_get_for_read( - input_name, domain, data_type); + GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type); if (!attribute_input) { params.error_message_add(NodeWarningType::Error, @@ -358,7 +374,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP return; } - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( result_name, domain, data_type); if (!attribute_result) { params.error_message_add(NodeWarningType::Error, @@ -369,18 +385,19 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP switch (data_type) { case CD_PROP_FLOAT: { - map_range_float(*attribute_input, *attribute_result, params); + map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params); break; } case CD_PROP_FLOAT3: { - map_range_float3(*attribute_input, *attribute_result, params); + map_range_float3( + attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params); break; } default: BLI_assert_unreachable(); } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_map_range_exec(GeoNodeExecParams params) @@ -393,6 +410,9 @@ static void geo_node_attribute_map_range_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc index 5ee31e78be2..ce0ca31cc2b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "UI_interface.h" #include "UI_resources.h" @@ -149,46 +151,52 @@ static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node operation_use_input_c(operation)); } -static void do_math_operation(Span<float> span_a, - Span<float> span_b, - Span<float> span_c, +static void do_math_operation(const VArray<float> &span_a, + const VArray<float> &span_b, + const VArray<float> &span_c, MutableSpan<float> span_result, const NodeMathOperation operation) { bool success = try_dispatch_float_math_fl_fl_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(span_result.size())) { - span_result[i] = math_function(span_a[i], span_b[i], span_c[i]); - } + parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) { + for (const int i : range) { + span_result[i] = math_function(span_a[i], span_b[i], span_c[i]); + } + }); }); BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation(Span<float> span_a, - Span<float> span_b, +static void do_math_operation(const VArray<float> &span_a, + const VArray<float> &span_b, MutableSpan<float> span_result, const NodeMathOperation operation) { bool success = try_dispatch_float_math_fl_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(span_result.size())) { - span_result[i] = math_function(span_a[i], span_b[i]); - } + parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + span_result[i] = math_function(span_a[i], span_b[i]); + } + }); }); BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation(Span<float> span_input, +static void do_math_operation(const VArray<float> &span_input, MutableSpan<float> span_result, const NodeMathOperation operation) { bool success = try_dispatch_float_math_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(span_result.size())) { - span_result[i] = math_function(span_input[i]); - } + parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + span_result[i] = math_function(span_input[i]); + } + }); }); BLI_assert(success); UNUSED_VARS_NDEBUG(success); @@ -200,9 +208,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -224,56 +232,39 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP const std::string result_name = params.get_input<std::string>("Result"); /* The result type of this node is always float. */ - const CustomDataType result_type = CD_PROP_FLOAT; const AttributeDomain result_domain = get_result_domain( component, params, operation, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( - result_name, result_domain, result_type); + OutputAttribute_Typed<float> attribute_result = + component.attribute_try_get_for_output_only<float>(result_name, result_domain); if (!attribute_result) { return; } - ReadAttributePtr attribute_a = params.get_input_attribute( - "A", component, result_domain, result_type, nullptr); - if (!attribute_a) { - return; - } + GVArray_Typed<float> attribute_a = params.get_input_attribute<float>( + "A", component, result_domain, 0.0f); - /* Note that passing the data with `get_span<float>()` works + MutableSpan<float> result_span = attribute_result.as_span(); + + /* Note that passing the data with `get_internal_span<float>()` works * because the attributes were accessed with #CD_PROP_FLOAT. */ if (operation_use_input_b(operation)) { - ReadAttributePtr attribute_b = params.get_input_attribute( - "B", component, result_domain, result_type, nullptr); - if (!attribute_b) { - return; - } + GVArray_Typed<float> attribute_b = params.get_input_attribute<float>( + "B", component, result_domain, 0.0f); if (operation_use_input_c(operation)) { - ReadAttributePtr attribute_c = params.get_input_attribute( - "C", component, result_domain, result_type, nullptr); - if (!attribute_c) { - return; - } - do_math_operation(attribute_a->get_span<float>(), - attribute_b->get_span<float>(), - attribute_c->get_span<float>(), - attribute_result->get_span_for_write_only<float>(), - operation); + GVArray_Typed<float> attribute_c = params.get_input_attribute<float>( + "C", component, result_domain, 0.0f); + do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation); } else { - do_math_operation(attribute_a->get_span<float>(), - attribute_b->get_span<float>(), - attribute_result->get_span_for_write_only<float>(), - operation); + do_math_operation(attribute_a, attribute_b, result_span, operation); } } else { - do_math_operation(attribute_a->get_span<float>(), - attribute_result->get_span_for_write_only<float>(), - operation); + do_math_operation(attribute_a, result_span, operation); } - attribute_result.apply_span_and_save(); + attribute_result.save(); } static void geo_node_attribute_math_exec(GeoNodeExecParams params) @@ -288,6 +279,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index 9d8cd3dfa82..3e5326edbf6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "BKE_material.h" #include "DNA_material_types.h" @@ -58,72 +60,87 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), namespace blender::nodes { static void do_mix_operation_float(const int blend_mode, - const FloatReadAttribute &factors, - const FloatReadAttribute &inputs_a, - const FloatReadAttribute &inputs_b, - FloatWriteAttribute results) + const VArray<float> &factors, + const VArray<float> &inputs_a, + const VArray<float> &inputs_b, + VMutableArray<float> &results) { const int size = results.size(); - for (const int i : IndexRange(size)) { - const float factor = factors[i]; - float3 a{inputs_a[i]}; - const float3 b{inputs_b[i]}; - ramp_blend(blend_mode, a, factor, b); - const float result = a.x; - results.set(i, result); - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float factor = factors[i]; + float3 a{inputs_a[i]}; + const float3 b{inputs_b[i]}; + ramp_blend(blend_mode, a, factor, b); + const float result = a.x; + results.set(i, result); + } + }); } static void do_mix_operation_float3(const int blend_mode, - const FloatReadAttribute &factors, - const Float3ReadAttribute &inputs_a, - const Float3ReadAttribute &inputs_b, - Float3WriteAttribute results) + const VArray<float> &factors, + const VArray<float3> &inputs_a, + const VArray<float3> &inputs_b, + VMutableArray<float3> &results) { const int size = results.size(); - for (const int i : IndexRange(size)) { - const float factor = factors[i]; - float3 a = inputs_a[i]; - const float3 b = inputs_b[i]; - ramp_blend(blend_mode, a, factor, b); - results.set(i, a); - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float factor = factors[i]; + float3 a = inputs_a[i]; + const float3 b = inputs_b[i]; + ramp_blend(blend_mode, a, factor, b); + results.set(i, a); + } + }); } static void do_mix_operation_color4f(const int blend_mode, - const FloatReadAttribute &factors, - const Color4fReadAttribute &inputs_a, - const Color4fReadAttribute &inputs_b, - Color4fWriteAttribute results) + const VArray<float> &factors, + const VArray<Color4f> &inputs_a, + const VArray<Color4f> &inputs_b, + VMutableArray<Color4f> &results) { const int size = results.size(); - for (const int i : IndexRange(size)) { - const float factor = factors[i]; - Color4f a = inputs_a[i]; - const Color4f b = inputs_b[i]; - ramp_blend(blend_mode, a, factor, b); - results.set(i, a); - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float factor = factors[i]; + Color4f a = inputs_a[i]; + const Color4f b = inputs_b[i]; + ramp_blend(blend_mode, a, factor, b); + results.set(i, a); + } + }); } static void do_mix_operation(const CustomDataType result_type, int blend_mode, - const FloatReadAttribute &attribute_factor, - const ReadAttribute &attribute_a, - const ReadAttribute &attribute_b, - WriteAttribute &attribute_result) + const VArray<float> &attribute_factor, + const GVArray &attribute_a, + const GVArray &attribute_b, + GVMutableArray &attribute_result) { if (result_type == CD_PROP_FLOAT) { - do_mix_operation_float( - blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result); + do_mix_operation_float(blend_mode, + attribute_factor, + attribute_a.typed<float>(), + attribute_b.typed<float>(), + attribute_result.typed<float>()); } else if (result_type == CD_PROP_FLOAT3) { - do_mix_operation_float3( - blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result); + do_mix_operation_float3(blend_mode, + attribute_factor, + attribute_a.typed<float3>(), + attribute_b.typed<float3>(), + attribute_result.typed<float3>()); } else if (result_type == CD_PROP_COLOR) { - do_mix_operation_color4f( - blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result); + do_mix_operation_color4f(blend_mode, + attribute_factor, + attribute_a.typed<Color4f>(), + attribute_b.typed<Color4f>(), + attribute_result.typed<Color4f>()); } } @@ -132,9 +149,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -158,17 +175,17 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa const AttributeDomain result_domain = get_result_domain(component, params, result_name); - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( result_name, result_domain, result_type); if (!attribute_result) { return; } - FloatReadAttribute attribute_factor = params.get_input_attribute<float>( + GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>( "Factor", component, result_domain, 0.5f); - ReadAttributePtr attribute_a = params.get_input_attribute( + GVArrayPtr attribute_a = params.get_input_attribute( "A", component, result_domain, result_type, nullptr); - ReadAttributePtr attribute_b = params.get_input_attribute( + GVArrayPtr attribute_b = params.get_input_attribute( "B", component, result_domain, result_type, nullptr); do_mix_operation(result_type, @@ -192,6 +209,9 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc index f09a9bf056e..9c22b7fa87f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc @@ -62,7 +62,7 @@ namespace blender::nodes { static void proximity_calc(MutableSpan<float> distance_span, MutableSpan<float3> location_span, - Span<float3> positions, + const VArray<float3> &positions, BVHTreeFromMesh &tree_data_mesh, BVHTreeFromPointCloud &tree_data_pointcloud, const bool bvh_mesh_success, @@ -169,19 +169,18 @@ static void attribute_calc_proximity(GeometryComponent &component, const AttributeDomain result_domain = ATTR_DOMAIN_POINT; const std::string distance_attribute_name = params.get_input<std::string>("Distance"); - OutputAttributePtr distance_attribute = component.attribute_try_get_for_output( - distance_attribute_name, result_domain, CD_PROP_FLOAT); + OutputAttribute_Typed<float> distance_attribute = + component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain); const std::string location_attribute_name = params.get_input<std::string>("Position"); - OutputAttributePtr location_attribute = component.attribute_try_get_for_output( - location_attribute_name, result_domain, CD_PROP_FLOAT3); - - ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position"); - BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3); + OutputAttribute_Typed<float3> location_attribute = + component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain); + ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position"); if (!position_attribute || (!distance_attribute && !location_attribute)) { return; } + BLI_assert(position_attribute.varray->type().is<float3>()); const bNode &node = params.node(); const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *) @@ -204,18 +203,15 @@ static void attribute_calc_proximity(GeometryComponent &component, tree_data_pointcloud); } - Span<float3> position_span = position_attribute->get_span<float3>(); - - MutableSpan<float> distance_span = distance_attribute ? - distance_attribute->get_span_for_write_only<float>() : - MutableSpan<float>(); - MutableSpan<float3> location_span = location_attribute ? - location_attribute->get_span_for_write_only<float3>() : - MutableSpan<float3>(); + GVArray_Typed<float3> positions{*position_attribute.varray}; + MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() : + MutableSpan<float>(); + MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() : + MutableSpan<float3>(); proximity_calc(distance_span, location_span, - position_span, + positions, tree_data_mesh, tree_data_pointcloud, bvh_mesh_success, @@ -231,10 +227,10 @@ static void attribute_calc_proximity(GeometryComponent &component, } if (distance_attribute) { - distance_attribute.apply_span_and_save(); + distance_attribute.save(); } if (location_attribute) { - location_attribute.apply_span_and_save(); + location_attribute.save(); } } @@ -257,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params) attribute_calc_proximity( geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params); } + if (geometry_set.has<CurveComponent>()) { + attribute_calc_proximity( + geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index 28263287a10..286411b7d28 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -16,6 +16,7 @@ #include "BLI_hash.h" #include "BLI_rand.hh" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -125,28 +126,36 @@ static void randomize_attribute(MutableSpan<T> span, /* The operations could be templated too, but it doesn't make the code much shorter. */ switch (operation) { case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE: - for (const int i : span.index_range()) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const T random_value = random_value_in_range<T>(ids[i], seed, min, max); + span[i] = random_value; + } + }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD: - for (const int i : span.index_range()) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] + random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const T random_value = random_value_in_range<T>(ids[i], seed, min, max); + span[i] = span[i] + random_value; + } + }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT: - for (const int i : span.index_range()) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] - random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const T random_value = random_value_in_range<T>(ids[i], seed, min, max); + span[i] = span[i] - random_value; + } + }); break; case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY: - for (const int i : span.index_range()) { - const T random_value = random_value_in_range<T>(ids[i], seed, min, max); - span[i] = span[i] * random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const T random_value = random_value_in_range<T>(ids[i], seed, min, max); + span[i] = span[i] * random_value; + } + }); break; default: BLI_assert(false); @@ -161,10 +170,12 @@ static void randomize_attribute_bool(MutableSpan<bool> span, { BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE); UNUSED_VARS_NDEBUG(operation); - for (const int i : span.index_range()) { - const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f; - span[i] = random_value; - } + parallel_for(span.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f; + span[i] = random_value; + } + }); } Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, @@ -173,15 +184,17 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo const int domain_size = component.attribute_domain_size(domain); /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */ - ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain); + GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain); Array<uint32_t> hashes(domain_size); if (hash_attribute) { BLI_assert(hashes.size() == hash_attribute->size()); - const CPPType &cpp_type = hash_attribute->cpp_type(); - fn::GSpan items = hash_attribute->get_span(); - for (const int i : hashes.index_range()) { - hashes[i] = cpp_type.hash(items[i]); - } + const CPPType &cpp_type = hash_attribute->type(); + GVArray_GSpan items{*hash_attribute}; + parallel_for(hashes.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + hashes[i] = cpp_type.hash(items[i]); + } + }); } else { /* If there is no "id" attribute for per-point variation, just create it here. */ @@ -196,12 +209,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo static AttributeDomain get_result_domain(const GeometryComponent &component, const GeoNodeExecParams ¶ms, - StringRef attribute_name) + const StringRef name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name); + if (result_info) { + return result_info->domain; } /* Otherwise use the input domain chosen in the interface. */ @@ -228,15 +241,13 @@ static void randomize_attribute_on_component(GeometryComponent &component, const AttributeDomain domain = get_result_domain(component, params, attribute_name); - OutputAttributePtr attribute = component.attribute_try_get_for_output( + OutputAttribute attribute = component.attribute_try_get_for_output( attribute_name, domain, data_type); if (!attribute) { return; } - fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ? - attribute->get_span_for_write_only() : - attribute->get_span(); + GMutableSpan span = attribute.as_span(); Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain); @@ -269,8 +280,8 @@ static void randomize_attribute_on_component(GeometryComponent &component, } } - attribute.apply_span_and_save(); -} // namespace blender::nodes + attribute.save(); +} static void geo_node_random_attribute_exec(GeoNodeExecParams params) { @@ -304,6 +315,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params) operation, seed); } + if (geometry_set.has<CurveComponent>()) { + randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(), + params, + attribute_name, + data_type, + operation, + seed); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index 837f0c3629a..e4f3230ebb9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params) remove_attribute( geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names); } + if (geometry_set.has<CurveComponent>()) { + remove_attribute( + geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index d0b2595b5b9..aa558314b9e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -15,6 +15,7 @@ */ #include "BLI_compiler_attrs.h" +#include "BLI_task.hh" #include "DNA_texture_types.h" @@ -29,6 +30,7 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_TEXTURE, N_("Texture")}, {SOCK_STRING, N_("Mapping")}, {SOCK_STRING, N_("Result")}, {-1, ""}, @@ -39,29 +41,22 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = { {-1, ""}, }; -static void geo_node_attribute_sample_texture_layout(uiLayout *layout, - bContext *C, - PointerRNA *ptr) -{ - uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); -} - namespace blender::nodes { static AttributeDomain get_result_domain(const GeometryComponent &component, - StringRef result_attribute_name, - StringRef map_attribute_name) + const StringRef result_name, + const StringRef map_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name); - if (result_attribute) { - return result_attribute->domain(); + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } /* Otherwise use the name of the map attribute. */ - ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name); - if (map_attribute) { - return map_attribute->domain(); + std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name); + if (map_info) { + return map_info->domain; } /* The node won't execute in this case, but we still have to return a value. */ @@ -70,8 +65,7 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams ¶ms) { - const bNode &node = params.node(); - Tex *texture = reinterpret_cast<Tex *>(node.id); + Tex *texture = params.get_input<Tex *>("Texture"); if (texture == nullptr) { return; } @@ -85,25 +79,28 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec const AttributeDomain result_domain = get_result_domain( component, result_attribute_name, mapping_name); - OutputAttributePtr attribute_out = component.attribute_try_get_for_output( - result_attribute_name, result_domain, CD_PROP_COLOR); + OutputAttribute_Typed<Color4f> attribute_out = + component.attribute_try_get_for_output_only<Color4f>(result_attribute_name, result_domain); if (!attribute_out) { return; } - Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>( + GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>( mapping_name, result_domain, {0, 0, 0}); - MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>(); - for (const int i : IndexRange(mapping_attribute.size())) { - TexResult texture_result = {0}; - const float3 position = mapping_attribute[i]; - /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */ - const float3 remapped_position = position * 2.0f - float3(1.0f); - BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false); - colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta}; - } - attribute_out.apply_span_and_save(); + MutableSpan<Color4f> colors = attribute_out.as_span(); + parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { + for (const int i : range) { + TexResult texture_result = {0}; + const float3 position = mapping_attribute[i]; + /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */ + const float3 remapped_position = position * 2.0f - float3(1.0f); + BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false); + colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta}; + } + }); + + attribute_out.save(); } static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) @@ -118,6 +115,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } @@ -137,6 +137,5 @@ void register_node_type_geo_sample_texture() node_type_socket_templates( &ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec; - ntype.draw_buttons = geo_node_attribute_sample_texture_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc index 656dc51149e..137a72bb707 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc @@ -71,23 +71,23 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa static AttributeDomain get_result_domain(const GeometryComponent &component, const GeoNodeExecParams ¶ms, - StringRef result_name_x, - StringRef result_name_y, - StringRef result_name_z) + const StringRef name_x, + const StringRef name_y, + const StringRef name_z) { /* Use the highest priority domain from any existing attribute outputs. */ Vector<AttributeDomain, 3> output_domains; - ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x); - ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y); - ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z); - if (attribute_x) { - output_domains.append(attribute_x->domain()); + std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x); + std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y); + std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z); + if (info_x) { + output_domains.append(info_x->domain); } - if (attribute_y) { - output_domains.append(attribute_y->domain()); + if (info_y) { + output_domains.append(info_y->domain); } - if (attribute_z) { - output_domains.append(attribute_z->domain()); + if (info_z) { + output_domains.append(info_z->domain); } if (output_domains.size() > 0) { return bke::attribute_domain_highest_priority(output_domains); @@ -107,37 +107,32 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa } /* The node is only for float3 to float conversions. */ - const CustomDataType input_type = CD_PROP_FLOAT3; - const CustomDataType result_type = CD_PROP_FLOAT; const AttributeDomain result_domain = get_result_domain( component, params, result_name_x, result_name_y, result_name_z); - ReadAttributePtr attribute_input = params.get_input_attribute( - "Vector", component, result_domain, input_type, nullptr); - if (!attribute_input) { - return; - } - const Span<float3> input_span = attribute_input->get_span<float3>(); + GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>( + "Vector", component, result_domain, {0, 0, 0}); + VArray_Span<float3> input_span{*attribute_input}; - OutputAttributePtr attribute_result_x = component.attribute_try_get_for_output( - result_name_x, result_domain, result_type); - OutputAttributePtr attribute_result_y = component.attribute_try_get_for_output( - result_name_y, result_domain, result_type); - OutputAttributePtr attribute_result_z = component.attribute_try_get_for_output( - result_name_z, result_domain, result_type); + OutputAttribute_Typed<float> attribute_result_x = + component.attribute_try_get_for_output_only<float>(result_name_x, result_domain); + OutputAttribute_Typed<float> attribute_result_y = + component.attribute_try_get_for_output_only<float>(result_name_y, result_domain); + OutputAttribute_Typed<float> attribute_result_z = + component.attribute_try_get_for_output_only<float>(result_name_z, result_domain); /* Only extract the components for the outputs with a given attribute. */ if (attribute_result_x) { - extract_input(0, input_span, attribute_result_x->get_span_for_write_only<float>()); - attribute_result_x.apply_span_and_save(); + extract_input(0, input_span, attribute_result_x.as_span()); + attribute_result_x.save(); } if (attribute_result_y) { - extract_input(1, input_span, attribute_result_y->get_span_for_write_only<float>()); - attribute_result_y.apply_span_and_save(); + extract_input(1, input_span, attribute_result_y.as_span()); + attribute_result_y.save(); } if (attribute_result_z) { - extract_input(2, input_span, attribute_result_z->get_span_for_write_only<float>()); - attribute_result_z.apply_span_and_save(); + extract_input(2, input_span, attribute_result_z.as_span()); + attribute_result_z.save(); } } @@ -153,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params) if (geometry_set.has<PointCloudComponent>()) { separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc new file mode 100644 index 00000000000..4b677dc5c82 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -0,0 +1,597 @@ +/* + * 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_kdopbvh.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_transfer_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_GEOMETRY, N_("Source Geometry")}, + {SOCK_STRING, N_("Source")}, + {SOCK_STRING, N_("Destination")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_transfer_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_transfer_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE); + uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); +} + +namespace blender::nodes { + +static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN( + sizeof(NodeGeometryAttributeTransfer), __func__); + data->domain = ATTR_DOMAIN_AUTO; + node->storage = data; +} + +static void get_result_domain_and_data_type(const GeometrySet &src_geometry, + const GeometryComponent &dst_component, + const StringRef attribute_name, + CustomDataType *r_data_type, + AttributeDomain *r_domain) +{ + Vector<CustomDataType> data_types; + Vector<AttributeDomain> domains; + + const PointCloudComponent *pointcloud_component = + src_geometry.get_component_for_read<PointCloudComponent>(); + if (pointcloud_component != nullptr) { + std::optional<AttributeMetaData> meta_data = pointcloud_component->attribute_get_meta_data( + attribute_name); + if (meta_data.has_value()) { + data_types.append(meta_data->data_type); + domains.append(meta_data->domain); + } + } + + const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>(); + if (mesh_component != nullptr) { + std::optional<AttributeMetaData> meta_data = mesh_component->attribute_get_meta_data( + attribute_name); + if (meta_data.has_value()) { + data_types.append(meta_data->data_type); + domains.append(meta_data->domain); + } + } + + *r_data_type = bke::attribute_data_type_highest_complexity(data_types); + + if (dst_component.type() == GEO_COMPONENT_TYPE_POINT_CLOUD) { + *r_domain = ATTR_DOMAIN_POINT; + } + else { + *r_domain = bke::attribute_domain_highest_priority(domains); + } +} + +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}; +} + +static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, + const VArray<float3> &positions, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty()); + BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty()); + BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty()); + + for (const int i : positions.index_range()) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + if (!r_indices.is_empty()) { + r_indices[i] = nearest.index; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + if (!r_positions.is_empty()) { + r_positions[i] = nearest.co; + } + } +} + +static void get_closest_pointcloud_points(const PointCloud &pointcloud, + const VArray<float3> &positions, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq) +{ + BLI_assert(positions.size() == r_indices.size()); + BLI_assert(pointcloud.totpoint > 0); + + BVHTreeFromPointCloud tree_data; + BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); + + for (const int i : positions.index_range()) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + r_indices[i] = nearest.index; + r_distances_sq[i] = nearest.dist_sq; + } + + free_bvhtree_from_pointcloud(&tree_data); +} + +static void get_closest_mesh_points(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_point_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totvert > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2); + get_closest_in_bvhtree(tree_data, positions, r_point_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_edges(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_edge_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totedge > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_EDGES, 2); + get_closest_in_bvhtree(tree_data, positions, r_edge_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_looptri_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree(tree_data, positions, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_polygons(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_poly_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + + Array<int> looptri_indices(positions.size()); + get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions); + + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + for (const int i : positions.index_range()) { + const MLoopTri &looptri = looptris[looptri_indices[i]]; + r_poly_indices[i] = looptri.poly; + } +} + +/* The closest corner is defined to be the closest corner on the closest face. */ +static void get_closest_mesh_corners(const Mesh &mesh, + const VArray<float3> &positions, + const MutableSpan<int> r_corner_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totloop > 0); + Array<int> poly_indices(positions.size()); + get_closest_mesh_polygons(mesh, positions, poly_indices, {}, {}); + + for (const int i : positions.index_range()) { + const float3 position = positions[i]; + const int poly_index = poly_indices[i]; + const MPoly &poly = mesh.mpoly[poly_index]; + + /* Find the closest vertex in the polygon. */ + float min_distance_sq = FLT_MAX; + const MVert *closest_mvert; + int closest_loop_index = 0; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int vertex_index = loop.v; + const MVert &mvert = mesh.mvert[vertex_index]; + const float distance_sq = float3::distance_squared(position, mvert.co); + if (distance_sq < min_distance_sq) { + min_distance_sq = distance_sq; + closest_loop_index = loop_index; + closest_mvert = &mvert; + } + } + if (!r_corner_indices.is_empty()) { + r_corner_indices[i] = closest_loop_index; + } + if (!r_positions.is_empty()) { + r_positions[i] = closest_mvert->co; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = min_distance_sq; + } + } +} + +static void get_barycentric_coords(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> positions, + const MutableSpan<float3> r_bary_coords) +{ + BLI_assert(r_bary_coords.size() == positions.size()); + BLI_assert(r_bary_coords.size() == looptri_indices.size()); + + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : r_bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + + 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; + + interp_weights_tri_v3(r_bary_coords[i], + mesh.mvert[v0_index].co, + mesh.mvert[v1_index].co, + mesh.mvert[v2_index].co, + positions[i]); + } +} + +static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry, + GeometryComponent &dst_component, + const VArray<float3> &dst_positions, + const AttributeDomain dst_domain, + const CustomDataType data_type, + const StringRef src_name, + const StringRef dst_name) +{ + const int tot_samples = dst_positions.size(); + const MeshComponent *component = src_geometry.get_component_for_read<MeshComponent>(); + if (component == nullptr) { + return; + } + const Mesh *mesh = component->get_for_read(); + if (mesh == nullptr) { + return; + } + if (mesh->totpoly == 0) { + return; + } + ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type); + if (!src_attribute) { + return; + } + + /* Find closest points on the mesh surface. */ + Array<int> looptri_indices(tot_samples); + Array<float3> positions(tot_samples); + get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions); + + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + dst_name, dst_domain, data_type); + if (!dst_attribute) { + return; + } + GMutableSpan dst_span = dst_attribute.as_span(); + Array<float3> bary_coords; + + /* Compute barycentric coordinates only when they are needed. */ + if (src_attribute.domain != ATTR_DOMAIN_FACE) { + bary_coords.reinitialize(tot_samples); + get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords); + } + /* Interpolate the source attribute on the surface. */ + switch (src_attribute.domain) { + case ATTR_DOMAIN_POINT: { + bke::mesh_surface_sample::sample_point_attribute( + *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span); + break; + } + case ATTR_DOMAIN_FACE: { + bke::mesh_surface_sample::sample_face_attribute( + *mesh, looptri_indices, *src_attribute.varray, dst_span); + break; + } + case ATTR_DOMAIN_CORNER: { + bke::mesh_surface_sample::sample_corner_attribute( + *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span); + break; + } + case ATTR_DOMAIN_EDGE: { + /* Not yet supported. */ + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + dst_attribute.save(); +} + +static void transfer_attribute_nearest(const GeometrySet &src_geometry, + GeometryComponent &dst_component, + const VArray<float3> &dst_positions, + const AttributeDomain dst_domain, + const CustomDataType data_type, + const StringRef src_name, + const StringRef dst_name) +{ + const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); + + /* Get pointcloud data from geometry. */ + const PointCloudComponent *pointcloud_component = + src_geometry.get_component_for_read<PointCloudComponent>(); + const PointCloud *pointcloud = pointcloud_component ? pointcloud_component->get_for_read() : + nullptr; + + /* Get mesh data from geometry. */ + const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>(); + const Mesh *mesh = mesh_component ? mesh_component->get_for_read() : nullptr; + + const int tot_samples = dst_positions.size(); + + Array<int> pointcloud_indices; + Array<float> pointcloud_distances_sq; + bool use_pointcloud = false; + + /* Depending on where what domain the source attribute lives, these indices are either vertex, + * corner, edge or polygon indices. */ + Array<int> mesh_indices; + Array<float> mesh_distances_sq; + bool use_mesh = false; + + /* If there is a pointcloud, find the closest points. */ + if (pointcloud != nullptr && pointcloud->totpoint > 0) { + if (pointcloud_component->attribute_exists(src_name)) { + use_pointcloud = true; + pointcloud_indices.reinitialize(tot_samples); + pointcloud_distances_sq.reinitialize(tot_samples); + get_closest_pointcloud_points( + *pointcloud, dst_positions, pointcloud_indices, pointcloud_distances_sq); + } + } + + /* If there is a mesh, find the closest mesh elements. */ + if (mesh != nullptr) { + ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name); + if (src_attribute) { + switch (src_attribute.domain) { + case ATTR_DOMAIN_POINT: { + if (mesh->totvert > 0) { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_points(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + } + break; + } + case ATTR_DOMAIN_EDGE: { + if (mesh->totedge > 0) { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_edges(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + } + break; + } + case ATTR_DOMAIN_FACE: { + if (mesh->totpoly > 0) { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_polygons(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + } + break; + } + case ATTR_DOMAIN_CORNER: { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + break; + } + default: { + break; + } + } + } + } + + if (!use_pointcloud && !use_mesh) { + return; + } + + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + dst_name, dst_domain, data_type); + if (!dst_attribute) { + return; + } + + /* Create a buffer for intermediate values. */ + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + + if (use_mesh && use_pointcloud) { + /* When there is a mesh and a pointcloud, we still have to check whether a pointcloud point or + * a mesh element is closer to every point. */ + ReadAttributeLookup pointcloud_src_attribute = + pointcloud_component->attribute_try_get_for_read(src_name, data_type); + ReadAttributeLookup mesh_src_attribute = mesh_component->attribute_try_get_for_read(src_name, + data_type); + for (const int i : IndexRange(tot_samples)) { + if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) { + /* Pointcloud point is closer. */ + const int index = pointcloud_indices[i]; + pointcloud_src_attribute.varray->get(index, buffer); + dst_attribute->set_by_relocate(i, buffer); + } + else { + /* Mesh element is closer. */ + const int index = mesh_indices[i]; + mesh_src_attribute.varray->get(index, buffer); + dst_attribute->set_by_relocate(i, buffer); + } + } + } + else if (use_pointcloud) { + /* The source geometry only has a pointcloud. */ + ReadAttributeLookup src_attribute = pointcloud_component->attribute_try_get_for_read( + src_name, data_type); + for (const int i : IndexRange(tot_samples)) { + const int index = pointcloud_indices[i]; + src_attribute.varray->get(index, buffer); + dst_attribute->set_by_relocate(i, buffer); + } + } + else if (use_mesh) { + /* The source geometry only has a mesh. */ + ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name, + data_type); + for (const int i : IndexRange(tot_samples)) { + const int index = mesh_indices[i]; + src_attribute.varray->get(index, buffer); + dst_attribute->set_by_relocate(i, buffer); + } + } + + dst_attribute.save(); +} + +static void transfer_attribute(const GeoNodeExecParams ¶ms, + const GeometrySet &src_geometry, + GeometryComponent &dst_component, + const StringRef src_name, + const StringRef dst_name) +{ + const NodeGeometryAttributeTransfer &storage = + *(const NodeGeometryAttributeTransfer *)params.node().storage; + const GeometryNodeAttributeTransferMapMode mapping = (GeometryNodeAttributeTransferMapMode) + storage.mapping; + const AttributeDomain input_domain = (AttributeDomain)storage.domain; + + CustomDataType data_type; + AttributeDomain auto_domain; + get_result_domain_and_data_type(src_geometry, dst_component, src_name, &data_type, &auto_domain); + const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain : + input_domain; + + GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>( + "position", dst_domain, {0, 0, 0}); + + switch (mapping) { + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + transfer_attribute_nearest_face_interpolated( + src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + transfer_attribute_nearest( + src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); + break; + } + } +} + +static void geo_node_attribute_transfer_exec(GeoNodeExecParams params) +{ + GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry"); + const std::string src_attribute_name = params.extract_input<std::string>("Source"); + const std::string dst_attribute_name = params.extract_input<std::string>("Destination"); + + if (src_attribute_name.empty() || dst_attribute_name.empty()) { + params.set_output("Geometry", dst_geometry_set); + return; + } + + dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set); + src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set); + + if (dst_geometry_set.has<MeshComponent>()) { + transfer_attribute(params, + src_geometry_set, + dst_geometry_set.get_component_for_write<MeshComponent>(), + src_attribute_name, + dst_attribute_name); + } + if (dst_geometry_set.has<PointCloudComponent>()) { + transfer_attribute(params, + src_geometry_set, + dst_geometry_set.get_component_for_write<PointCloudComponent>(), + src_attribute_name, + dst_attribute_name); + } + + params.set_output("Geometry", dst_geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_transfer() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_transfer_in, geo_node_attribute_transfer_out); + node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init); + node_type_storage(&ntype, + "NodeGeometryAttributeTransfer", + node_free_standard_storage, + node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec; + ntype.draw_buttons = geo_node_attribute_transfer_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index 1ae095a27d2..8877af445f9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -15,6 +15,7 @@ */ #include "BLI_math_base_safe.h" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -168,196 +169,210 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod operation_use_input_c(operation)); } -static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, - Float3WriteAttribute result, +static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a, + const VArray<float3> &input_b, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float3> span_b = input_b.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float3> span_b{input_b}; + VMutableArray_Span<float3> span_result{result, false}; bool success = try_dispatch_float_math_fl3_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float3 out = math_function(a, b); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float3 b = span_b[i]; + const float3 out = math_function(a, b); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, - const Float3ReadAttribute &input_c, - Float3WriteAttribute result, +static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a, + const VArray<float3> &input_b, + const VArray<float3> &input_c, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float3> span_b = input_b.get_span(); - Span<float3> span_c = input_c.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float3> span_b{input_b}; + VArray_Span<float3> span_c{input_c}; + VMutableArray_Span<float3> span_result{result}; bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float3 c = span_c[i]; - const float3 out = math_function(a, b, c); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float3 b = span_b[i]; + const float3 c = span_c[i]; + const float3 out = math_function(a, b, c); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, - const FloatReadAttribute &input_c, - Float3WriteAttribute result, +static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a, + const VArray<float3> &input_b, + const VArray<float> &input_c, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float3> span_b = input_b.get_span(); - Span<float> span_c = input_c.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float3> span_b{input_b}; + VArray_Span<float> span_c{input_c}; + VMutableArray_Span<float3> span_result{result, false}; bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float c = span_c[i]; - const float3 out = math_function(a, b, c); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float3 b = span_b[i]; + const float c = span_c[i]; + const float3 out = math_function(a, b, c); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a, - const Float3ReadAttribute &input_b, - FloatWriteAttribute result, +static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a, + const VArray<float3> &input_b, + VMutableArray<float> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float3> span_b = input_b.get_span(); - MutableSpan<float> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float3> span_b{input_b}; + VMutableArray_Span<float> span_result{result, false}; bool success = try_dispatch_float_math_fl3_fl3_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float3 b = span_b[i]; - const float out = math_function(a, b); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float3 b = span_b[i]; + const float out = math_function(a, b); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a, - const FloatReadAttribute &input_b, - Float3WriteAttribute result, +static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a, + const VArray<float> &input_b, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - Span<float> span_b = input_b.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VArray_Span<float> span_b{input_b}; + VMutableArray_Span<float3> span_result{result, false}; bool success = try_dispatch_float_math_fl3_fl_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 a = span_a[i]; - const float b = span_b[i]; - const float3 out = math_function(a, b); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 a = span_a[i]; + const float b = span_b[i]; + const float3 out = math_function(a, b); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a, - Float3WriteAttribute result, +static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a, + VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - MutableSpan<float3> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VMutableArray_Span<float3> span_result{result, false}; bool success = try_dispatch_float_math_fl3_to_fl3( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 in = span_a[i]; - const float3 out = math_function(in); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 in = span_a[i]; + const float3 out = math_function(in); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); UNUSED_VARS_NDEBUG(success); } -static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a, - FloatWriteAttribute result, +static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a, + VMutableArray<float> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); - Span<float3> span_a = input_a.get_span(); - MutableSpan<float> span_result = result.get_span_for_write_only(); + VArray_Span<float3> span_a{input_a}; + VMutableArray_Span<float> span_result{result, false}; bool success = try_dispatch_float_math_fl3_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float3 in = span_a[i]; - const float out = math_function(in); - span_result[i] = out; - } + parallel_for(IndexRange(size), 512, [&](IndexRange range) { + for (const int i : range) { + const float3 in = span_a[i]; + const float out = math_function(in); + span_result[i] = out; + } + }); }); - result.apply_span(); + span_result.save(); /* The operation is not supported by this node currently. */ BLI_assert(success); @@ -370,9 +385,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name); if (result_attribute) { - return result_attribute->domain(); + return result_attribute.domain; } /* Otherwise use the highest priority domain from existing input attributes, or the default. */ @@ -406,13 +421,13 @@ static void attribute_vector_math_calc(GeometryComponent &component, const AttributeDomain result_domain = get_result_domain( component, params, operation, result_name); - ReadAttributePtr attribute_a = params.get_input_attribute( + GVArrayPtr attribute_a = params.get_input_attribute( "A", component, result_domain, read_type_a, nullptr); if (!attribute_a) { return; } - ReadAttributePtr attribute_b; - ReadAttributePtr attribute_c; + GVArrayPtr attribute_b; + GVArrayPtr attribute_c; if (use_input_b) { attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr); if (!attribute_b) { @@ -427,7 +442,7 @@ static void attribute_vector_math_calc(GeometryComponent &component, } /* Get result attribute first, in case it has to overwrite one of the existing attributes. */ - OutputAttributePtr attribute_result = component.attribute_try_get_for_output( + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( result_name, result_domain, result_type); if (!attribute_result) { return; @@ -445,17 +460,27 @@ static void attribute_vector_math_calc(GeometryComponent &component, case NODE_VECTOR_MATH_MODULO: case NODE_VECTOR_MATH_MINIMUM: case NODE_VECTOR_MATH_MAXIMUM: - do_math_operation_fl3_fl3_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation); + do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(), + attribute_b->typed<float3>(), + attribute_result->typed<float3>(), + operation); break; case NODE_VECTOR_MATH_DOT_PRODUCT: case NODE_VECTOR_MATH_DISTANCE: - do_math_operation_fl3_fl3_to_fl(*attribute_a, *attribute_b, *attribute_result, operation); + do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(), + attribute_b->typed<float3>(), + attribute_result->typed<float>(), + operation); break; case NODE_VECTOR_MATH_LENGTH: - do_math_operation_fl3_to_fl(*attribute_a, *attribute_result, operation); + do_math_operation_fl3_to_fl( + attribute_a->typed<float3>(), attribute_result->typed<float>(), operation); break; case NODE_VECTOR_MATH_SCALE: - do_math_operation_fl3_fl_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation); + do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(), + attribute_b->typed<float>(), + attribute_result->typed<float3>(), + operation); break; case NODE_VECTOR_MATH_NORMALIZE: case NODE_VECTOR_MATH_FLOOR: @@ -465,16 +490,23 @@ static void attribute_vector_math_calc(GeometryComponent &component, case NODE_VECTOR_MATH_SINE: case NODE_VECTOR_MATH_COSINE: case NODE_VECTOR_MATH_TANGENT: - do_math_operation_fl3_to_fl3(*attribute_a, *attribute_result, operation); + do_math_operation_fl3_to_fl3( + attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation); break; case NODE_VECTOR_MATH_WRAP: case NODE_VECTOR_MATH_FACEFORWARD: - do_math_operation_fl3_fl3_fl3_to_fl3( - *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation); + do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(), + attribute_b->typed<float3>(), + attribute_c->typed<float3>(), + attribute_result->typed<float3>(), + operation); break; case NODE_VECTOR_MATH_REFRACT: - do_math_operation_fl3_fl3_fl_to_fl3( - *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation); + do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(), + attribute_b->typed<float3>(), + attribute_c->typed<float>(), + attribute_result->typed<float3>(), + operation); break; } attribute_result.save(); @@ -493,6 +525,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params) attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); } + if (geometry_set.has<CurveComponent>()) { + attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc new file mode 100644 index 00000000000..4d568ab5c3a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc @@ -0,0 +1,352 @@ +/* + * 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 "node_geometry_util.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +static bNodeSocketTemplate geo_node_attribute_vector_rotate_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Vector")}, + {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + {SOCK_STRING, N_("Center")}, + {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ}, + {SOCK_STRING, N_("Axis")}, + {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_XYZ, PROP_NONE}, + {SOCK_STRING, N_("Angle")}, + {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE}, + {SOCK_STRING, N_("Rotation")}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {SOCK_BOOLEAN, N_("Invert")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_vector_rotate_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage; + const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode) + node_storage.mode; + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiLayout *column = uiLayoutColumn(layout, false); + + uiItemR(column, ptr, "rotation_mode", 0, "", ICON_NONE); + + uiItemR(column, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE); + uiItemR(column, ptr, "input_type_center", 0, IFACE_("Center"), ICON_NONE); + if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS) { + uiItemR(column, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE); + } + if (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { + uiItemR(column, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE); + } + if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { + uiItemR(column, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE); + } +} + +namespace blender::nodes { + +static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage; + const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode) + node_storage->mode; + + update_attribute_input_socket_availabilities( + *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); + update_attribute_input_socket_availabilities( + *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center); + update_attribute_input_socket_availabilities( + *node, + "Axis", + (GeometryNodeAttributeInputMode)node_storage->input_type_axis, + (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS)); + update_attribute_input_socket_availabilities( + *node, + "Angle", + (GeometryNodeAttributeInputMode)node_storage->input_type_angle, + (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); + update_attribute_input_socket_availabilities( + *node, + "Rotation", + (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, + (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); +} + +static float3 vector_rotate_around_axis(const float3 vector, + const float3 center, + const float3 axis, + const float angle) +{ + float3 result = vector - center; + float mat[3][3]; + axis_angle_to_mat3(mat, axis, angle); + mul_m3_v3(mat, result); + return result + center; +} + +static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN( + sizeof(NodeAttributeVectorRotate), __func__); + + node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS; + node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + node_storage->input_type_center = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + + node->storage = node_storage; +} + +static float3 vector_rotate_euler(const float3 vector, + const float3 center, + const float3 rotation, + const bool invert) +{ + float mat[3][3]; + float3 result = vector - center; + eul_to_mat3(mat, rotation); + if (invert) { + invert_m3(mat); + } + mul_m3_v3(mat, result); + return result + center; +} + +static void do_vector_rotate_around_axis(const VArray<float3> &vector, + const VArray<float3> ¢er, + const VArray<float3> &axis, + const VArray<float> &angle, + MutableSpan<float3> results, + const bool invert) +{ + VArray_Span<float3> span_vector{vector}; + VArray_Span<float3> span_center{center}; + VArray_Span<float3> span_axis{axis}; + VArray_Span<float> span_angle{angle}; + + parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + float angle = (invert) ? -span_angle[i] : span_angle[i]; + results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle); + } + }); +} + +static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector, + const VArray<float3> ¢er, + const float3 axis, + const VArray<float> &angle, + MutableSpan<float3> results, + const bool invert) +{ + VArray_Span<float3> span_vector{vector}; + VArray_Span<float3> span_center{center}; + VArray_Span<float> span_angle{angle}; + + parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + float angle = (invert) ? -span_angle[i] : span_angle[i]; + results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle); + } + }); +} + +static void do_vector_rotate_euler(const VArray<float3> &vector, + const VArray<float3> ¢er, + const VArray<float3> &rotation, + MutableSpan<float3> results, + const bool invert) +{ + VArray_Span<float3> span_vector{vector}; + VArray_Span<float3> span_center{center}; + VArray_Span<float3> span_rotation{rotation}; + + parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert); + } + }); +} + +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(result_name); + if (meta_data) { + return meta_data->domain; + } + + /* Otherwise use the highest priority domain from existing input attributes, or the default. */ + const AttributeDomain default_domain = ATTR_DOMAIN_POINT; + return params.get_highest_priority_input_domain({"Vector", "Center"}, component, default_domain); +} + +static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) +{ + const bNode &node = params.node(); + const NodeAttributeVectorRotate *node_storage = (const NodeAttributeVectorRotate *)node.storage; + const GeometryNodeAttributeVectorRotateMode mode = (GeometryNodeAttributeVectorRotateMode) + node_storage->mode; + const std::string result_name = params.get_input<std::string>("Result"); + const AttributeDomain result_domain = get_result_domain(component, params, result_name); + const bool invert = params.get_input<bool>("Invert"); + + GVArrayPtr attribute_vector = params.get_input_attribute( + "Vector", component, result_domain, CD_PROP_FLOAT3, nullptr); + if (!attribute_vector) { + return; + } + GVArrayPtr attribute_center = params.get_input_attribute( + "Center", component, result_domain, CD_PROP_FLOAT3, nullptr); + if (!attribute_center) { + return; + } + + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( + result_name, result_domain, CD_PROP_FLOAT3); + if (!attribute_result) { + return; + } + + if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { + GVArrayPtr attribute_rotation = params.get_input_attribute( + "Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr); + if (!attribute_rotation) { + return; + } + do_vector_rotate_euler(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + attribute_rotation->typed<float3>(), + attribute_result.as_span<float3>(), + invert); + attribute_result.save(); + return; + } + + GVArrayPtr attribute_angle = params.get_input_attribute( + "Angle", component, result_domain, CD_PROP_FLOAT, nullptr); + if (!attribute_angle) { + return; + } + + switch (mode) { + case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: { + GVArrayPtr attribute_axis = params.get_input_attribute( + "Axis", component, result_domain, CD_PROP_FLOAT3, nullptr); + if (!attribute_axis) { + return; + } + do_vector_rotate_around_axis(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + attribute_axis->typed<float3>(), + attribute_angle->typed<float>(), + attribute_result.as_span<float3>(), + invert); + } break; + case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X: + do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + float3(1.0f, 0.0f, 0.0f), + attribute_angle->typed<float>(), + attribute_result.as_span<float3>(), + invert); + break; + case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y: + do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + float3(0.0f, 1.0f, 0.0f), + attribute_angle->typed<float>(), + attribute_result.as_span<float3>(), + invert); + + break; + case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z: + do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + float3(0.0f, 0.0f, 1.0f), + attribute_angle->typed<float>(), + attribute_result.as_span<float3>(), + invert); + + break; + case GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: + /* Euler is handled before other modes to avoid processing the unavailable angle socket. */ + BLI_assert_unreachable(); + break; + } + attribute_result.save(); +} + +static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); + } + if (geometry_set.has<PointCloudComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); + } + if (geometry_set.has<CurveComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_vector_rotate() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, + GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, + "Attribute Vector Rotate", + NODE_CLASS_ATTRIBUTE, + 0); + node_type_socket_templates( + &ntype, geo_node_attribute_vector_rotate_in, geo_node_attribute_vector_rotate_out); + node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update); + node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init); + node_type_size(&ntype, 165, 100, 600); + node_type_storage( + &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec; + ntype.draw_buttons = geo_node_attribute_vector_rotate_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index 96455f080e7..b6fa4c0d48f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -39,15 +39,12 @@ static void compute_min_max_from_position_and_transform(const GeometryComponent float3 &r_min, float3 &r_max) { - ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position"); - if (!position_attribute) { - BLI_assert(component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0); - return; - } - Span<float3> positions = position_attribute->get_span<float3>(); + GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); for (const float4x4 &transform : transforms) { - for (const float3 &position : positions) { + for (const int i : positions.index_range()) { + const float3 position = positions[i]; const float3 transformed_position = transform * position; minmax_v3v3_v3(r_min, r_max, transformed_position); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index 0ad495aa4db..bd4de710dbb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -42,9 +42,7 @@ namespace blender::nodes { static void geo_node_collection_info_exec(GeoNodeExecParams params) { - bke::PersistentCollectionHandle collection_handle = - params.extract_input<bke::PersistentCollectionHandle>("Collection"); - Collection *collection = params.handle_map().lookup(collection_handle); + Collection *collection = params.get_input<Collection *>("Collection"); GeometrySet geometry_set_out; @@ -58,10 +56,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) const bool transform_space_relative = (node_storage->transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_COLLECTION; - instance.data.collection = collection; - InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); float transform_mat[4][4]; @@ -73,7 +67,9 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) mul_m4_m4_pre(transform_mat, self_object->imat); } - instances.add_instance(instance, transform_mat, -1); + + const int handle = instances.add_reference(*collection); + instances.add_instance(handle, transform_mat, -1); params.set_output("Geometry", geometry_set_out); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc new file mode 100644 index 00000000000..d7d31a4ef92 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -0,0 +1,208 @@ +/* + * 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_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::fn::GVArray_For_Span; +using blender::fn::GVArray_Typed; + +static bNodeSocketTemplate geo_node_curve_resample_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_INT, N_("Count"), 10, 0, 0, 0, 1, 100000}, + {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_resample_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN( + sizeof(NodeGeometryCurveResample), __func__); + + data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + node->storage = data; +} + +static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *length_socket = count_socket->next; + + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); +} + +namespace blender::nodes { + +struct SampleModeParam { + GeometryNodeCurveSampleMode mode; + std::optional<float> length; + std::optional<int> count; +}; + +template<typename T> +static void sample_span_to_output_spline(const Spline &input_spline, + Span<float> index_factors, + const VArray<T> &input_data, + MutableSpan<T> output_data) +{ + BLI_assert(input_data.size() == input_spline.evaluated_points_size()); + + parallel_for(output_data.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + const Spline::LookupResult interp = input_spline.lookup_data_from_index_factor( + index_factors[i]); + output_data[i] = blender::attribute_math::mix2(interp.factor, + input_data[interp.evaluated_index], + input_data[interp.next_evaluated_index]); + } + }); +} + +static SplinePtr resample_spline(const Spline &input_spline, const int count) +{ + std::unique_ptr<PolySpline> output_spline = std::make_unique<PolySpline>(); + output_spline->set_cyclic(input_spline.is_cyclic()); + output_spline->normal_mode = input_spline.normal_mode; + + if (input_spline.evaluated_edges_size() < 1) { + output_spline->resize(1); + output_spline->positions().first() = input_spline.positions().first(); + return output_spline; + } + + output_spline->resize(count); + + Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count); + + { + GVArray_For_Span positions(input_spline.evaluated_positions()); + GVArray_Typed<float3> positions_typed(positions); + sample_span_to_output_spline<float3>( + input_spline, uniform_samples, positions_typed, output_spline->positions()); + } + { + GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points( + GVArray_For_Span(input_spline.radii())); + GVArray_Typed<float> interpolated_data_typed{*interpolated_data}; + sample_span_to_output_spline<float>( + input_spline, uniform_samples, interpolated_data_typed, output_spline->radii()); + } + { + GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points( + GVArray_For_Span(input_spline.tilts())); + GVArray_Typed<float> interpolated_data_typed{*interpolated_data}; + sample_span_to_output_spline<float>( + input_spline, uniform_samples, interpolated_data_typed, output_spline->tilts()); + } + + return output_spline; +} + +static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, + const SampleModeParam &mode_param) +{ + std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + + for (const SplinePtr &spline : input_curve.splines()) { + if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + BLI_assert(mode_param.count); + output_curve->add_spline(resample_spline(*spline, *mode_param.count)); + } + else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + BLI_assert(mode_param.length); + const float length = spline->length(); + const int count = length / *mode_param.length; + output_curve->add_spline(resample_spline(*spline, count)); + } + } + + return output_curve; +} + +static void geo_node_resample_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const CurveEval &input_curve = *geometry_set.get_curve_for_read(); + NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + SampleModeParam mode_param; + mode_param.mode = mode; + if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + const int count = params.extract_input<int>("Count"); + if (count < 1) { + params.set_output("Geometry", GeometrySet()); + return; + } + mode_param.count.emplace(count); + } + else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + /* Don't allow asymptotic count increase for low resolution values. */ + const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f); + mode_param.length.emplace(resolution); + } + + std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param); + + params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_resample() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_resample_in, geo_node_curve_resample_out); + ntype.draw_buttons = geo_node_curve_resample_layout; + node_type_storage( + &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_curve_resample_init); + node_type_update(&ntype, geo_node_curve_resample_update); + ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc new file mode 100644 index 00000000000..f0effdc71f6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -0,0 +1,312 @@ +/* + * 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_float4x4.hh" +#include "BLI_timeit.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_GEOMETRY, N_("Profile Curve")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = { + {SOCK_GEOMETRY, N_("Mesh")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void vert_extrude_to_mesh_data(const Spline &spline, + const float3 profile_vert, + MutableSpan<MVert> r_verts, + MutableSpan<MEdge> r_edges, + int &vert_offset, + int &edge_offset) +{ + Span<float3> positions = spline.evaluated_positions(); + + for (const int i : IndexRange(positions.size() - 1)) { + MEdge &edge = r_edges[edge_offset++]; + edge.v1 = vert_offset + i; + edge.v2 = vert_offset + i + 1; + edge.flag = ME_LOOSEEDGE; + } + + if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { + MEdge &edge = r_edges[edge_offset++]; + edge.v1 = vert_offset; + edge.v2 = vert_offset + positions.size() - 1; + edge.flag = ME_LOOSEEDGE; + } + + for (const int i : positions.index_range()) { + MVert &vert = r_verts[vert_offset++]; + copy_v3_v3(vert.co, positions[i] + profile_vert); + } +} + +static void mark_edges_sharp(MutableSpan<MEdge> edges) +{ + for (MEdge &edge : edges) { + edge.flag |= ME_SHARP; + } +} + +static void spline_extrude_to_mesh_data(const Spline &spline, + const Spline &profile_spline, + MutableSpan<MVert> r_verts, + MutableSpan<MEdge> r_edges, + MutableSpan<MLoop> r_loops, + MutableSpan<MPoly> r_polys, + int &vert_offset, + int &edge_offset, + int &loop_offset, + int &poly_offset) +{ + const int spline_vert_len = spline.evaluated_points_size(); + const int spline_edge_len = spline.evaluated_edges_size(); + const int profile_vert_len = profile_spline.evaluated_points_size(); + const int profile_edge_len = profile_spline.evaluated_edges_size(); + if (spline_vert_len == 0) { + return; + } + + if (profile_vert_len == 1) { + vert_extrude_to_mesh_data(spline, + profile_spline.evaluated_positions()[0], + r_verts, + r_edges, + vert_offset, + edge_offset); + return; + } + + /* Add the edges running along the length of the curve, starting at each profile vertex. */ + const int spline_edges_start = edge_offset; + for (const int i_profile : IndexRange(profile_vert_len)) { + for (const int i_ring : IndexRange(spline_edge_len)) { + const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1; + + const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring; + + MEdge &edge = r_edges[edge_offset++]; + edge.v1 = ring_vert_offset + i_profile; + edge.v2 = next_ring_vert_offset + i_profile; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Add the edges running along each profile ring. */ + const int profile_edges_start = edge_offset; + for (const int i_ring : IndexRange(spline_vert_len)) { + const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + + for (const int i_profile : IndexRange(profile_edge_len)) { + const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; + + MEdge &edge = r_edges[edge_offset++]; + edge.v1 = ring_vert_offset + i_profile; + edge.v2 = ring_vert_offset + i_next_profile; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Calculate poly and corner indices. */ + for (const int i_ring : IndexRange(spline_edge_len)) { + const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1; + + const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring; + + const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring; + const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring; + + for (const int i_profile : IndexRange(profile_edge_len)) { + const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; + + const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile; + const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile; + + MPoly &poly = r_polys[poly_offset++]; + poly.loopstart = loop_offset; + poly.totloop = 4; + poly.flag = ME_SMOOTH; + + MLoop &loop_a = r_loops[loop_offset++]; + loop_a.v = ring_vert_offset + i_profile; + loop_a.e = ring_edge_start + i_profile; + MLoop &loop_b = r_loops[loop_offset++]; + loop_b.v = ring_vert_offset + i_next_profile; + loop_b.e = next_spline_edge_start + i_ring; + MLoop &loop_c = r_loops[loop_offset++]; + loop_c.v = next_ring_vert_offset + i_next_profile; + loop_c.e = next_ring_edge_offset + i_profile; + MLoop &loop_d = r_loops[loop_offset++]; + loop_d.v = next_ring_vert_offset + i_profile; + loop_d.e = spline_edge_start + i_ring; + } + } + + /* Calculate the positions of each profile ring profile along the spline. */ + Span<float3> positions = spline.evaluated_positions(); + Span<float3> tangents = spline.evaluated_tangents(); + Span<float3> normals = spline.evaluated_normals(); + Span<float3> profile_positions = profile_spline.evaluated_positions(); + + GVArray_Typed<float> radii{ + spline.interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(spline.radii()))}; + for (const int i_ring : IndexRange(spline_vert_len)) { + float4x4 point_matrix = float4x4::from_normalized_axis_data( + positions[i_ring], normals[i_ring], tangents[i_ring]); + + point_matrix.apply_scale(radii[i_ring]); + + for (const int i_profile : IndexRange(profile_vert_len)) { + MVert &vert = r_verts[vert_offset++]; + copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); + } + } + + /* Mark edge loops from sharp vector control points sharp. */ + if (profile_spline.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline); + Span<int> control_point_offsets = bezier_spline.control_point_offsets(); + for (const int i : IndexRange(bezier_spline.size())) { + if (bezier_spline.point_is_sharp(i)) { + mark_edges_sharp(r_edges.slice( + spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len)); + } + } + } +} + +static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve) +{ + int profile_vert_total = 0; + int profile_edge_total = 0; + for (const SplinePtr &profile_spline : profile_curve.splines()) { + profile_vert_total += profile_spline->evaluated_points_size(); + profile_edge_total += profile_spline->evaluated_edges_size(); + } + + int vert_total = 0; + int edge_total = 0; + int poly_total = 0; + for (const SplinePtr &spline : curve.splines()) { + const int spline_vert_len = spline->evaluated_points_size(); + const int spline_edge_len = spline->evaluated_edges_size(); + vert_total += spline_vert_len * profile_vert_total; + poly_total += spline_edge_len * profile_edge_total; + + /* Add the ring edges, with one ring for every curve vertex, and the edge loops + * that run along the length of the curve, starting on the first profile. */ + edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len; + } + const int corner_total = poly_total * 4; + + if (vert_total == 0) { + return nullptr; + } + + Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + mesh->flag |= ME_AUTOSMOOTH; + mesh->smoothresh = DEG2RADF(180.0f); + + int vert_offset = 0; + int edge_offset = 0; + int loop_offset = 0; + int poly_offset = 0; + for (const SplinePtr &spline : curve.splines()) { + for (const SplinePtr &profile_spline : profile_curve.splines()) { + spline_extrude_to_mesh_data(*spline, + *profile_spline, + verts, + edges, + loops, + polys, + vert_offset, + edge_offset, + loop_offset, + poly_offset); + } + } + + BKE_mesh_calc_normals(mesh); + + return mesh; +} + +static CurveEval get_curve_single_vert() +{ + CurveEval curve; + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->add_point(float3(0), 0, 0.0f); + curve.add_spline(std::move(spline)); + + return curve; +} + +static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) +{ + GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); + GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); + + curve_set = bke::geometry_set_realize_instances(curve_set); + profile_set = bke::geometry_set_realize_instances(profile_set); + + if (!curve_set.has_curve()) { + params.set_output("Mesh", GeometrySet()); + return; + } + + const CurveEval *profile_curve = profile_set.get_curve_for_read(); + + static const CurveEval vert_curve = get_curve_single_vert(); + + Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(), + (profile_curve == nullptr) ? vert_curve : *profile_curve); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_to_mesh() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 77d2b0df2a9..a6e78048ea6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -17,6 +17,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_pointcloud.h" +#include "BKE_spline.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -149,10 +150,10 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *> Vector<CustomDataType> data_types; Vector<AttributeDomain> domains; for (const GeometryComponent *component : components) { - ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name); + ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name); if (attribute) { - data_types.append(attribute->custom_data_type()); - domains.append(attribute->domain()); + data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type())); + domains.append(attribute.domain); } } @@ -164,7 +165,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, StringRef attribute_name, const CustomDataType data_type, const AttributeDomain domain, - fn::GMutableSpan dst_span) + GMutableSpan dst_span) { const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type); BLI_assert(cpp_type != nullptr); @@ -175,10 +176,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, if (domain_size == 0) { continue; } - ReadAttributePtr read_attribute = component->attribute_get_for_read( + GVArrayPtr read_attribute = component->attribute_get_for_read( attribute_name, domain, data_type, nullptr); - fn::GSpan src_span = read_attribute->get_span(); + GVArray_GSpan src_span{*read_attribute}; const void *src_buffer = src_span.data(); void *dst_buffer = dst_span[offset]; cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size); @@ -201,16 +202,14 @@ static void join_attributes(Span<const GeometryComponent *> src_components, AttributeDomain domain; determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain); - OutputAttributePtr write_attribute = result.attribute_try_get_for_output( + OutputAttribute write_attribute = result.attribute_try_get_for_output_only( attribute_name, domain, data_type); - if (!write_attribute || - &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) || - write_attribute->domain() != domain) { + if (!write_attribute) { continue; } - fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); + GMutableSpan dst_span = write_attribute.as_span(); fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span); - write_attribute.apply_span_and_save(); + write_attribute.save(); } } @@ -244,13 +243,30 @@ static void join_components(Span<const PointCloudComponent *> src_components, Ge static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result) { InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>(); - for (const InstancesComponent *component : src_components) { - const int size = component->instances_amount(); - Span<InstancedData> instanced_data = component->instanced_data(); - Span<float4x4> transforms = component->transforms(); - Span<int> ids = component->ids(); - for (const int i : IndexRange(size)) { - dst_component.add_instance(instanced_data[i], transforms[i], ids[i]); + + int tot_instances = 0; + for (const InstancesComponent *src_component : src_components) { + tot_instances += src_component->instances_amount(); + } + dst_component.reserve(tot_instances); + + for (const InstancesComponent *src_component : src_components) { + Span<InstanceReference> src_references = src_component->references(); + Array<int> handle_map(src_references.size()); + for (const int src_handle : src_references.index_range()) { + handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]); + } + + Span<float4x4> src_transforms = src_component->instance_transforms(); + Span<int> src_ids = src_component->instance_ids(); + Span<int> src_reference_handles = src_component->instance_reference_handles(); + + for (const int i : src_transforms.index_range()) { + const int src_handle = src_reference_handles[i]; + const int dst_handle = handle_map[src_handle]; + const float4x4 &transform = src_transforms[i]; + const int id = src_ids[i]; + dst_component.add_instance(dst_handle, transform, id); } } } @@ -263,6 +279,40 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet UNUSED_VARS(src_components, dst_component); } +static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result) +{ + Vector<CurveComponent *> src_components; + for (GeometrySet &geometry_set : src_geometry_sets) { + if (geometry_set.has_curve()) { + /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy + * in the case where the input spline has no other users, because the splines can be + * moved from the source curve rather than copied from a read-only source. Retrieving + * the curve for write will make a copy only when it has a user elsewhere. */ + CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); + src_components.append(&component); + } + } + + if (src_components.size() == 0) { + return; + } + if (src_components.size() == 1) { + result.add(*src_components[0]); + return; + } + + CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); + CurveEval *dst_curve = new CurveEval(); + for (CurveComponent *component : src_components) { + CurveEval *src_curve = component->get_for_write(); + for (SplinePtr &spline : src_curve->splines()) { + dst_curve->add_spline(std::move(spline)); + } + } + + dst_component.replace(dst_curve); +} + template<typename Component> static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result) { @@ -293,6 +343,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params) join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result); join_component_type<InstancesComponent>(geometry_sets, geometry_set_result); join_component_type<VolumeComponent>(geometry_sets, geometry_set_result); + join_curve_components(geometry_sets, geometry_set_result); params.set_output("Geometry", std::move(geometry_set_result)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 761d5d6c388..2806472286e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -194,9 +194,9 @@ static void calculate_uvs(Mesh *mesh, { MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output( - "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr); - MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>(); + OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); Array<float2> circle(verts_num); float angle = 0.0f; @@ -271,7 +271,7 @@ static void calculate_uvs(Mesh *mesh, } } - uv_attribute.apply_span_and_save(); + uv_attribute.save(); } Mesh *create_cylinder_or_cone_mesh(const float radius_top, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 14c57bc7135..5a4bab86421 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -44,9 +44,9 @@ static void calculate_uvs( { MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output( - "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr); - MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>(); + OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x; const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y; @@ -56,7 +56,7 @@ static void calculate_uvs( uvs[i].y = (co.y + size_y * 0.5f) * dy; } - uv_attribute.apply_span_and_save(); + uv_attribute.save(); } static Mesh *create_grid_mesh(const int verts_x, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index fd95cdc81f7..cc93e71a5dd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -224,9 +224,9 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r { MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output( - "uv_map", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr); - MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>(); + OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); int loop_index = 0; const float dy = 1.0f / rings; @@ -256,7 +256,7 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r uvs[loop_index++] = float2(segment / segments, 1.0f - dy); } - uv_attribute.apply_span_and_save(); + uv_attribute.save(); } static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings) diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index bd42b4c11d6..16c943b310c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -47,9 +47,7 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) const bool transform_space_relative = (node_storage->transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); - bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>( - "Object"); - Object *object = params.handle_map().lookup(object_handle); + Object *object = params.get_input<Object *>("Object"); float3 location = {0, 0, 0}; float3 rotation = {0, 0, 0}; @@ -73,14 +71,15 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) if (object != self_object) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + const int handle = instances.add_reference(*object); if (transform_space_relative) { - instances.add_instance(object, transform); + instances.add_instance(handle, transform); } else { float unit_transform[4][4]; unit_m4(unit_transform); - instances.add_instance(object, unit_transform); + instances.add_instance(handle, unit_transform); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 74cca8a2f3c..772bd8a1080 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -28,6 +28,7 @@ #include "BKE_geometry_set_instances.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" #include "BKE_pointcloud.h" #include "UI_interface.h" @@ -91,7 +92,7 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) static void sample_mesh_surface(const Mesh &mesh, const float4x4 &transform, const float base_density, - const FloatReadAttribute *density_factors, + const VArray<float> *density_factors, const int seed, Vector<float3> &r_positions, Vector<float3> &r_bary_coords, @@ -113,9 +114,9 @@ static void sample_mesh_surface(const Mesh &mesh, float looptri_density_factor = 1.0f; if (density_factors != nullptr) { - const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]); - const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]); - const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]); + const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop)); + const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop)); + const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop)); looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f; } const float area = area_tri_v3(v0_pos, v1_pos, v2_pos); @@ -203,7 +204,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( const Mesh &mesh, - const FloatReadAttribute &density_factors, + const VArray<float> &density_factors, Span<float3> bary_coords, Span<int> looptri_indices, MutableSpan<bool> elimination_mask) @@ -249,99 +250,27 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m } } -template<typename T> -BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh, - const Span<float3> bary_coords, - const Span<int> looptri_indices, - const Span<T> data_in, - MutableSpan<T> data_out) -{ - BLI_assert(data_in.size() == mesh.totvert); - 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; - } -} - -template<typename T> -BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh, - const Span<float3> bary_coords, - const Span<int> looptri_indices, - const Span<T> data_in, - MutableSpan<T> data_out) -{ - BLI_assert(data_in.size() == mesh.totloop); - 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; - } -} - -template<typename T> -BLI_NOINLINE static void interpolate_attribute_face(const Mesh &mesh, - const Span<int> looptri_indices, - const Span<T> data_in, - MutableSpan<T> data_out) -{ - BLI_assert(data_in.size() == mesh.totpoly); - 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]; - } -} - -template<typename T> BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, Span<float3> bary_coords, Span<int> looptri_indices, const AttributeDomain source_domain, - Span<T> source_span, - MutableSpan<T> output_span) + const GVArray &source_data, + GMutableSpan output_data) { switch (source_domain) { case ATTR_DOMAIN_POINT: { - interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, source_span, output_span); + bke::mesh_surface_sample::sample_point_attribute( + mesh, looptri_indices, bary_coords, source_data, output_data); break; } case ATTR_DOMAIN_CORNER: { - interpolate_attribute_corner<T>( - mesh, bary_coords, looptri_indices, source_span, output_span); + bke::mesh_surface_sample::sample_corner_attribute( + mesh, looptri_indices, bary_coords, source_data, output_data); break; } case ATTR_DOMAIN_FACE: { - interpolate_attribute_face<T>(mesh, looptri_indices, source_span, output_span); + bke::mesh_surface_sample::sample_face_attribute( + mesh, looptri_indices, source_data, output_data); break; } default: { @@ -363,13 +292,13 @@ BLI_NOINLINE static void interpolate_existing_attributes( StringRef attribute_name = entry.key; const CustomDataType output_data_type = entry.value.data_type; /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ - OutputAttributePtr attribute_out = component.attribute_try_get_for_output( + OutputAttribute attribute_out = component.attribute_try_get_for_output_only( attribute_name, ATTR_DOMAIN_POINT, output_data_type); if (!attribute_out) { continue; } - fn::GMutableSpan out_span = attribute_out->get_span_for_write_only(); + GMutableSpan out_span = attribute_out.as_span(); int i_instance = 0; for (const GeometryInstanceGroup &set_group : set_groups) { @@ -377,47 +306,41 @@ BLI_NOINLINE static void interpolate_existing_attributes( const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *source_component.get_for_read(); - /* Use a dummy read without specifying a domain or data type in order to - * get the existing attribute's domain. Interpolation is done manually based - * on the bary coords in #interpolate_attribute. */ - ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read( + std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data( attribute_name); - if (!dummy_attribute) { + if (!attribute_info) { i_instance += set_group.transforms.size(); continue; } - const AttributeDomain source_domain = dummy_attribute->domain(); - ReadAttributePtr source_attribute = source_component.attribute_get_for_read( + const AttributeDomain source_domain = attribute_info->domain; + GVArrayPtr source_attribute = source_component.attribute_get_for_read( attribute_name, source_domain, output_data_type, nullptr); if (!source_attribute) { i_instance += set_group.transforms.size(); continue; } - fn::GSpan source_span = source_attribute->get_span(); + + for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { + const int offset = instance_start_offsets[i_instance]; + Span<float3> bary_coords = bary_coords_array[i_instance]; + Span<int> looptri_indices = looptri_indices_array[i_instance]; + + GMutableSpan instance_span = out_span.slice(offset, bary_coords.size()); + interpolate_attribute( + mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span); + + i_instance++; + } attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) { using T = decltype(dummy); - for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { - const int offset = instance_start_offsets[i_instance]; - Span<float3> bary_coords = bary_coords_array[i_instance]; - Span<int> looptri_indices = looptri_indices_array[i_instance]; - - MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size()); - interpolate_attribute<T>(mesh, - bary_coords, - looptri_indices, - source_domain, - source_span.typed<T>(), - instance_span); - - i_instance++; - } + GVArray_Span<T> source_span{*source_attribute}; }); } - attribute_out.apply_span_and_save(); + attribute_out.save(); } } @@ -427,16 +350,16 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> Span<Vector<float3>> bary_coords_array, Span<Vector<int>> looptri_indices_array) { - OutputAttributePtr id_attribute = component.attribute_try_get_for_output( - "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); - OutputAttributePtr normal_attribute = component.attribute_try_get_for_output( - "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output( - "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( + "id", ATTR_DOMAIN_POINT); + OutputAttribute_Typed<float3> normal_attribute = + component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT); + OutputAttribute_Typed<float3> rotation_attribute = + component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT); - MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>(); - MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>(); - MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>(); + MutableSpan<int> result_ids = id_attribute.as_span(); + MutableSpan<float3> result_normals = normal_attribute.as_span(); + MutableSpan<float3> result_rotations = rotation_attribute.as_span(); int i_instance = 0; for (const GeometryInstanceGroup &set_group : sets) { @@ -480,9 +403,9 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> } } - id_attribute.apply_span_and_save(); - normal_attribute.apply_span_and_save(); - rotation_attribute.apply_span_and_save(); + id_attribute.save(); + normal_attribute.save(); + rotation_attribute.save(); } BLI_NOINLINE static void add_remaining_point_attributes( @@ -520,7 +443,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups, for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - const FloatReadAttribute density_factors = component.attribute_get_for_read<float>( + GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>( density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); const Mesh &mesh = *component.get_for_read(); for (const float4x4 &transform : set_group.transforms) { @@ -530,7 +453,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups, sample_mesh_surface(mesh, transform, density, - &density_factors, + &*density_factors, seed, positions, bary_coords, @@ -589,7 +512,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group const GeometrySet &set = set_group.geometry_set; const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *component.get_for_read(); - const FloatReadAttribute density_factors = component.attribute_get_for_read<float>( + const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>( density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 20022e8d29d..6f66f2145fa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -14,11 +14,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BKE_persistent_data_handle.hh" - #include "DNA_collection_types.h" #include "BLI_hash.h" +#include "BLI_task.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -65,128 +64,133 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection); } -static void get_instanced_data__object(const GeoNodeExecParams ¶ms, - MutableSpan<std::optional<InstancedData>> r_instances_data) +static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams ¶ms) { - bke::PersistentObjectHandle object_handle = params.get_input<bke::PersistentObjectHandle>( - "Object"); - Object *object = params.handle_map().lookup(object_handle); + Object *object = params.extract_input<Object *>("Object"); if (object == params.self_object()) { - object = nullptr; + return {}; } if (object != nullptr) { - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_OBJECT; - instance.data.object = object; - r_instances_data.fill(instance); + return {*object}; } + return {}; } -static void get_instanced_data__collection( - const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - MutableSpan<std::optional<InstancedData>> r_instances_data) +static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams ¶ms) { const bNode &node = params.node(); NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; - bke::PersistentCollectionHandle collection_handle = - params.get_input<bke::PersistentCollectionHandle>("Collection"); - Collection *collection = params.handle_map().lookup(collection_handle); + Collection *collection = params.get_input<Collection *>("Collection"); if (collection == nullptr) { - return; + return {}; } if (BLI_listbase_is_empty(&collection->children) && BLI_listbase_is_empty(&collection->gobject)) { params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty")); - return; + return {}; } - const bool use_whole_collection = (node_storage->flag & - GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0; - if (use_whole_collection) { - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_COLLECTION; - instance.data.collection = collection; - r_instances_data.fill(instance); + if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) { + return {*collection}; } - else { - Vector<InstancedData> possible_instances; - /* Direct child objects are instanced as objects. */ - LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - Object *object = cob->ob; - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_OBJECT; - instance.data.object = object; - possible_instances.append(instance); - } - /* Direct child collections are instanced as collections. */ - LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - Collection *child_collection = child->collection; - InstancedData instance; - instance.type = INSTANCE_DATA_TYPE_COLLECTION; - instance.data.collection = child_collection; - possible_instances.append(instance); - } - if (!possible_instances.is_empty()) { - const int seed = params.get_input<int>("Seed"); - Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT); - for (const int i : r_instances_data.index_range()) { - const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size(); - r_instances_data[i] = possible_instances[index]; - } - } + Vector<InstanceReference> references; + /* Direct child objects are instanced as objects. */ + LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { + references.append(*cob->ob); + } + /* Direct child collections are instanced as collections. */ + LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { + references.append(*child->collection); } + + return references; } -static Array<std::optional<InstancedData>> get_instanced_data(const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - const int amount) +static Vector<InstanceReference> get_instance_references(GeoNodeExecParams ¶ms) { const bNode &node = params.node(); NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType) node_storage->instance_type; - Array<std::optional<InstancedData>> instances_data(amount); switch (type) { case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: { - get_instanced_data__object(params, instances_data); - break; + return get_instance_references__object(params); } case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: { - get_instanced_data__collection(params, component, instances_data); - break; + return get_instance_references__collection(params); } } - return instances_data; + return {}; +} + +/** + * Add the instance references to the component as a separate step from actually creating the + * instances in order to avoid a map lookup for every transform. While this might add some + * unnecessary references if they are not chosen while adding transforms, in the common cases + * there are many more transforms than there are references, so that isn't likely. + */ +static Array<int> add_instance_references(InstancesComponent &instance_component, + Span<InstanceReference> possible_references) +{ + Array<int> possible_handles(possible_references.size()); + for (const int i : possible_references.index_range()) { + possible_handles[i] = instance_component.add_reference(possible_references[i]); + } + return possible_handles; } -static void add_instances_from_geometry_component(InstancesComponent &instances, - const GeometryComponent &src_geometry, - const GeoNodeExecParams ¶ms) +static void add_instances_from_component(InstancesComponent &instances, + const GeometryComponent &src_geometry, + Span<int> possible_handles, + const GeoNodeExecParams ¶ms) { const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_geometry.attribute_domain_size(domain); - Array<std::optional<InstancedData>> instances_data = get_instanced_data( - params, src_geometry, domain_size); - Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>( + GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>( "position", domain, {0, 0, 0}); - Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>( + GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>( "rotation", domain, {0, 0, 0}); - Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>( + GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>( "scale", domain, {1, 1, 1}); - Int32ReadAttribute ids = src_geometry.attribute_get_for_read<int>("id", domain, -1); - - for (const int i : IndexRange(domain_size)) { - if (instances_data[i].has_value()) { - const float4x4 matrix = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); - instances.add_instance(*instances_data[i], matrix, ids[i]); - } + GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1); + + /* The initial size of the component might be non-zero if there are two component types. */ + const int start_len = instances.instances_amount(); + instances.resize(start_len + domain_size); + MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size); + MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size); + MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size); + + /* Skip all of the randomness handling if there is only a single possible instance + * (anything except for collection mode with "Whole Collection" turned off). */ + if (possible_handles.size() == 1) { + const int handle = possible_handles.first(); + parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { + for (const int i : range) { + handles[i] = handle; + transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); + instance_ids[i] = id_attribute[i]; + } + }); + } + else { + const int seed = params.get_input<int>("Seed"); + Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT); + parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { + for (const int i : range) { + const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size(); + const int handle = possible_handles[index]; + handles[i] = handle; + transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); + instance_ids[i] = id_attribute[i]; + } + }); } } @@ -199,14 +203,32 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params) * rather than making the entire input geometry set real. */ geometry_set = geometry_set_realize_instances(geometry_set); + const Vector<InstanceReference> possible_references = get_instance_references(params); + if (possible_references.is_empty()) { + params.set_output("Geometry", std::move(geometry_set_out)); + return; + } + InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); + Array<int> possible_handles = add_instance_references(instances, possible_references); + if (geometry_set.has<MeshComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<MeshComponent>(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<MeshComponent>(), + possible_handles, + params); } if (geometry_set.has<PointCloudComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<PointCloudComponent>(), + possible_handles, + params); + } + if (geometry_set.has<CurveComponent>()) { + add_instances_from_component(instances, + *geometry_set.get_component_for_read<CurveComponent>(), + possible_handles, + params); } params.set_output("Geometry", std::move(geometry_set_out)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc index 2e7fce6ea30..73d489949ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc @@ -60,8 +60,8 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), namespace blender::nodes { static void point_rotate__axis_angle__object_space(const int domain_size, - const Float3ReadAttribute &axis, - const FloatReadAttribute &angles, + const VArray<float3> &axis, + const VArray<float> &angles, MutableSpan<float3> rotations) { for (const int i : IndexRange(domain_size)) { @@ -76,8 +76,8 @@ static void point_rotate__axis_angle__object_space(const int domain_size, } static void point_rotate__axis_angle__point_space(const int domain_size, - const Float3ReadAttribute &axis, - const FloatReadAttribute &angles, + const VArray<float3> &axis, + const VArray<float> &angles, MutableSpan<float3> rotations) { for (const int i : IndexRange(domain_size)) { @@ -92,7 +92,7 @@ static void point_rotate__axis_angle__point_space(const int domain_size, } static void point_rotate__euler__object_space(const int domain_size, - const Float3ReadAttribute &eulers, + const VArray<float3> &eulers, MutableSpan<float3> rotations) { for (const int i : IndexRange(domain_size)) { @@ -107,7 +107,7 @@ static void point_rotate__euler__object_space(const int domain_size, } static void point_rotate__euler__point_space(const int domain_size, - const Float3ReadAttribute &eulers, + const VArray<float3> &eulers, MutableSpan<float3> rotations) { for (const int i : IndexRange(domain_size)) { @@ -127,19 +127,19 @@ static void point_rotate_on_component(GeometryComponent &component, const bNode &node = params.node(); const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage; - OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output( - "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + OutputAttribute_Typed<float3> rotation_attribute = + component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0}); if (!rotation_attribute) { return; } - MutableSpan<float3> rotations = rotation_attribute->get_span<float3>(); + MutableSpan<float3> rotations = rotation_attribute.as_span(); const int domain_size = rotations.size(); if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) { - Float3ReadAttribute axis = params.get_input_attribute<float3>( + GVArray_Typed<float3> axis = params.get_input_attribute<float3>( "Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1}); - FloatReadAttribute angles = params.get_input_attribute<float>( + GVArray_Typed<float> angles = params.get_input_attribute<float>( "Angle", component, ATTR_DOMAIN_POINT, 0); if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) { @@ -150,7 +150,7 @@ static void point_rotate_on_component(GeometryComponent &component, } } else { - Float3ReadAttribute eulers = params.get_input_attribute<float3>( + GVArray_Typed<float3> eulers = params.get_input_attribute<float3>( "Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) { @@ -161,7 +161,7 @@ static void point_rotate_on_component(GeometryComponent &component, } } - rotation_attribute.apply_span_and_save(); + rotation_attribute.save(); } static void geo_node_point_rotate_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc index 113e2c620f6..2ef21fb085b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc @@ -50,7 +50,7 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co * for the factor. But for it's simpler to simply always use float3, since that is usually * expected anyway. */ static const float3 scale_default = float3(1.0f); - OutputAttributePtr scale_attribute = component.attribute_try_get_for_output( + OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output( "scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default); if (!scale_attribute) { return; @@ -63,27 +63,27 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT : CD_PROP_FLOAT3; - ReadAttributePtr attribute = params.get_input_attribute( + GVArrayPtr attribute = params.get_input_attribute( "Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr); if (!attribute) { return; } - MutableSpan<float3> scale_span = scale_attribute->get_span<float3>(); + MutableSpan<float3> scale_span = scale_attribute.as_span(); if (data_type == CD_PROP_FLOAT) { - Span<float> factors = attribute->get_span<float>(); + GVArray_Typed<float> factors{*attribute}; for (const int i : scale_span.index_range()) { scale_span[i] = scale_span[i] * factors[i]; } } else if (data_type == CD_PROP_FLOAT3) { - Span<float3> factors = attribute->get_span<float3>(); + GVArray_Typed<float3> factors{*attribute}; for (const int i : scale_span.index_range()) { scale_span[i] = scale_span[i] * factors[i]; } } - scale_attribute.apply_span_and_save(); + scale_attribute.save(); } static void geo_node_point_scale_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index 522dea4aa0e..6541d982629 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -58,27 +58,27 @@ static void copy_attributes_based_on_mask(const GeometryComponent &in_component, const bool invert) { for (const std::string &name : in_component.attribute_names()) { - ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name); - const CustomDataType data_type = attribute->custom_data_type(); + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name); + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type()); /* Only copy point attributes. Theoretically this could interpolate attributes on other * domains to the point domain, but that would conflict with attributes that are built-in * on other domains, which causes creating the attributes to fail. */ - if (attribute->domain() != ATTR_DOMAIN_POINT) { + if (attribute.domain != ATTR_DOMAIN_POINT) { continue; } - OutputAttributePtr result_attribute = result_component.attribute_try_get_for_output( + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( name, ATTR_DOMAIN_POINT, data_type); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); - Span<T> span = attribute->get_span<T>(); - MutableSpan<T> out_span = result_attribute->get_span_for_write_only<T>(); + GVArray_Span<T> span{*attribute.varray}; + MutableSpan<T> out_span = result_attribute.as_span<T>(); copy_data_based_on_mask(span, masks, invert, out_span); }); - result_attribute.apply_span_and_save(); + result_attribute.save(); } } @@ -107,9 +107,9 @@ static void separate_points_from_component(const GeometryComponent &in_component return; } - const BooleanReadAttribute mask_attribute = in_component.attribute_get_for_read<bool>( + const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>( mask_name, ATTR_DOMAIN_POINT, false); - Span<bool> masks = mask_attribute.get_span(); + VArray_Span<bool> masks{mask_attribute}; const int total = masks.count(!invert); if (total == 0) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc index 8c7387f7d9b..44203228899 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc @@ -42,24 +42,19 @@ namespace blender::nodes { static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component) { - OutputAttributePtr position_attribute = component.attribute_try_get_for_output( - "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + OutputAttribute_Typed<float3> position_attribute = + component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0}); if (!position_attribute) { return; } - ReadAttributePtr attribute = params.get_input_attribute( - "Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr); - if (!attribute) { - return; - } + GVArray_Typed<float3> attribute = params.get_input_attribute<float3>( + "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); - Span<float3> data = attribute->get_span<float3>(); - MutableSpan<float3> scale_span = position_attribute->get_span<float3>(); - for (const int i : scale_span.index_range()) { - scale_span[i] = scale_span[i] + data[i]; + for (const int i : IndexRange(attribute.size())) { + position_attribute->set(i, position_attribute->get(i) + attribute[i]); } - position_attribute.apply_span_and_save(); + position_attribute.save(); } static void geo_node_point_translate_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index a9eb136597e..e85f6aaee54 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -147,13 +147,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams ¶ms, Vector<float3> &r_positions, Vector<float> &r_radii) { - Float3ReadAttribute positions = component.attribute_get_for_read<float3>( + GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - FloatReadAttribute radii = params.get_input_attribute<float>( + GVArray_Typed<float> radii = params.get_input_attribute<float>( "Radius", component, ATTR_DOMAIN_POINT, 0.0f); - r_positions.extend(positions.get_span()); - r_radii.extend(radii.get_span()); + for (const int i : IndexRange(positions.size())) { + r_positions.append(positions[i]); + r_radii.append(radii[i]); + } } static void convert_to_grid_index_space(const float voxel_size, @@ -184,6 +186,10 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_ gather_point_data_from_component( params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii); } + if (geometry_set_in.has<CurveComponent>()) { + gather_point_data_from_component( + params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii); + } const float max_radius = *std::max_element(radii.begin(), radii.end()); const float voxel_size = compute_voxel_size(params, positions, max_radius); diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc new file mode 100644 index 00000000000..f27c2163188 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -0,0 +1,163 @@ +/* + * 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 "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +static bNodeSocketTemplate geo_node_switch_in[] = { + {SOCK_BOOLEAN, N_("Switch")}, + + {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_BOOLEAN, N_("A")}, + {SOCK_BOOLEAN, N_("B")}, + {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_STRING, N_("A")}, + {SOCK_STRING, N_("B")}, + {SOCK_GEOMETRY, N_("A")}, + {SOCK_GEOMETRY, N_("B")}, + {SOCK_OBJECT, N_("A")}, + {SOCK_OBJECT, N_("B")}, + {SOCK_COLLECTION, N_("A")}, + {SOCK_COLLECTION, N_("B")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_switch_out[] = { + {SOCK_FLOAT, N_("Output")}, + {SOCK_INT, N_("Output")}, + {SOCK_BOOLEAN, N_("Output")}, + {SOCK_VECTOR, N_("Output")}, + {SOCK_RGBA, N_("Output")}, + {SOCK_STRING, N_("Output")}, + {SOCK_GEOMETRY, N_("Output")}, + {SOCK_OBJECT, N_("Output")}, + {SOCK_COLLECTION, N_("Output")}, + {-1, ""}, +}; + +static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE); +} + +static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); + data->input_type = SOCK_FLOAT; + node->storage = data; +} + +namespace blender::nodes { + +template<typename T> +void output_input(GeoNodeExecParams ¶ms, + const bool input, + const StringRef input_suffix, + const StringRef output_identifier) +{ + if (input) { + params.set_output(output_identifier, params.extract_input<T>("B" + input_suffix)); + } + else { + params.set_output(output_identifier, params.extract_input<T>("A" + input_suffix)); + } +} + +static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeSwitch *node_storage = (NodeSwitch *)node->storage; + int index = 0; + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + nodeSetSocketAvailability( + socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type); + index++; + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + nodeSetSocketAvailability(socket, + socket->type == (eNodeSocketDatatype)node_storage->input_type); + } +} + +static void geo_node_switch_exec(GeoNodeExecParams params) +{ + const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage; + const bool input = params.extract_input<bool>("Switch"); + switch ((eNodeSocketDatatype)storage.input_type) { + case SOCK_FLOAT: { + output_input<float>(params, input, "", "Output"); + break; + } + case SOCK_INT: { + output_input<int>(params, input, "_001", "Output_001"); + break; + } + case SOCK_BOOLEAN: { + output_input<bool>(params, input, "_002", "Output_002"); + break; + } + case SOCK_VECTOR: { + output_input<float3>(params, input, "_003", "Output_003"); + break; + } + case SOCK_RGBA: { + output_input<Color4f>(params, input, "_004", "Output_004"); + break; + } + case SOCK_STRING: { + output_input<std::string>(params, input, "_005", "Output_005"); + break; + } + case SOCK_GEOMETRY: { + output_input<GeometrySet>(params, input, "_006", "Output_006"); + break; + } + case SOCK_OBJECT: { + output_input<Object *>(params, input, "_007", "Output_007"); + break; + } + case SOCK_COLLECTION: { + output_input<Collection *>(params, input, "_008", "Output_008"); + break; + } + default: + BLI_assert_unreachable(); + break; + } +} + +} // namespace blender::nodes + +void register_node_type_geo_switch() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); + node_type_init(&ntype, geo_node_switch_init); + node_type_update(&ntype, blender::nodes::geo_node_switch_update); + node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec; + ntype.draw_buttons = geo_node_switch_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index d54982d16c2..ce52dc90668 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -24,6 +24,7 @@ #include "DNA_volume_types.h" #include "BKE_mesh.h" +#include "BKE_spline.hh" #include "BKE_volume.h" #include "DEG_depsgraph_query.h" @@ -100,7 +101,7 @@ static void transform_instances(InstancesComponent &instances, const float3 rotation, const float3 scale) { - MutableSpan<float4x4> transforms = instances.transforms(); + MutableSpan<float4x4> transforms = instances.instance_transforms(); /* Use only translation if rotation and scale don't apply. */ if (use_translate(rotation, scale)) { @@ -152,6 +153,21 @@ static void transform_volume(Volume *volume, #endif } +static void transform_curve(CurveEval &curve, + const float3 translation, + const float3 rotation, + const float3 scale) +{ + + if (use_translate(rotation, scale)) { + curve.translate(translation); + } + else { + const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); + curve.transform(matrix); + } +} + static void geo_node_transform_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -179,6 +195,11 @@ static void geo_node_transform_exec(GeoNodeExecParams params) transform_volume(volume, translation, rotation, scale, params); } + if (geometry_set.has_curve()) { + CurveEval *curve = geometry_set.get_curve_for_write(); + transform_curve(*curve, translation, rotation, scale); + } + params.set_output("Geometry", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index a4fb99a988e..73a702c753a 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -22,6 +22,7 @@ #include "NOD_geometry_exec.hh" #include "NOD_type_callbacks.hh" +#include "NOD_type_conversions.hh" #include "node_geometry_util.hh" @@ -29,22 +30,22 @@ namespace blender::nodes { void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const { - bNodeTree *btree_cow = node_->btree(); + bNodeTree *btree_cow = provider_->dnode->btree(); BLI_assert(btree_cow != nullptr); if (btree_cow == nullptr) { return; } bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow); - const NodeTreeEvaluationContext context(*self_object_, *modifier_); + const NodeTreeEvaluationContext context(*provider_->self_object, *provider_->modifier); BKE_nodetree_error_message_add( - *btree_original, context, *node_->bnode(), type, std::move(message)); + *btree_original, context, *provider_->dnode->bnode(), type, std::move(message)); } const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { - for (const InputSocketRef *socket : node_->inputs()) { + for (const InputSocketRef *socket : provider_->dnode->inputs()) { if (socket->is_available() && socket->name() == name) { return socket->bsocket(); } @@ -53,22 +54,29 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name return nullptr; } -ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const CustomDataType type, - const void *default_value) const +GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const CustomDataType type, + const void *default_value) const { const bNodeSocket *found_socket = this->find_available_socket(name); BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ + const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type); + const int64_t domain_size = component.attribute_domain_size(domain); + + if (default_value == nullptr) { + default_value = cpp_type->default_value(); + } + if (found_socket == nullptr) { - return component.attribute_get_constant_for_read(domain, type, default_value); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); } if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); /* Try getting the attribute without the default value. */ - ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type); + GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, type); if (attribute) { return attribute; } @@ -80,25 +88,29 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, this->error_message_add(NodeWarningType::Error, TIP_("No attribute with name \"") + name + "\""); } - return component.attribute_get_constant_for_read(domain, type, default_value); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); } + const DataTypeConversions &conversions = get_implicit_type_conversions(); if (found_socket->type == SOCK_FLOAT) { const float value = this->get_input<float>(found_socket->identifier); - return component.attribute_get_constant_for_read_converted( - domain, CD_PROP_FLOAT, type, &value); + BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); + conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_VECTOR) { const float3 value = this->get_input<float3>(found_socket->identifier); - return component.attribute_get_constant_for_read_converted( - domain, CD_PROP_FLOAT3, type, &value); + BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); + conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_RGBA) { const Color4f value = this->get_input<Color4f>(found_socket->identifier); - return component.attribute_get_constant_for_read_converted( - domain, CD_PROP_COLOR, type, &value); + BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); + conversions.convert_to_uninitialized(CPPType::get<Color4f>(), *cpp_type, &value, buffer); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); } BLI_assert(false); - return component.attribute_get_constant_for_read(domain, type, default_value); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); } CustomDataType GeoNodeExecParams::get_input_attribute_data_type( @@ -114,11 +126,11 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type( if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); - ReadAttributePtr attribute = component.attribute_try_get_for_read(name); - if (!attribute) { - return default_type; + std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name); + if (info) { + return info->data_type; } - return attribute->custom_data_type(); + return default_type; } if (found_socket->type == SOCK_FLOAT) { return CD_PROP_FLOAT; @@ -157,9 +169,9 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); - ReadAttributePtr attribute = component.attribute_try_get_for_read(name); - if (attribute) { - input_domains.append(attribute->domain()); + std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name); + if (info) { + input_domains.append(info->domain); } } } @@ -171,11 +183,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( return default_domain; } -void GeoNodeExecParams::check_extract_input(StringRef identifier, - const CPPType *requested_type) const +void GeoNodeExecParams::check_input_access(StringRef identifier, + const CPPType *requested_type) const { bNodeSocket *found_socket = nullptr; - for (const InputSocketRef *socket : node_->inputs()) { + for (const InputSocketRef *socket : provider_->dnode->inputs()) { if (socket->identifier() == identifier) { found_socket = socket->bsocket(); break; @@ -185,39 +197,39 @@ void GeoNodeExecParams::check_extract_input(StringRef identifier, if (found_socket == nullptr) { std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n"; std::cout << "Possible identifiers are: "; - for (const InputSocketRef *socket : node_->inputs()) { + for (const InputSocketRef *socket : provider_->dnode->inputs()) { if (socket->is_available()) { std::cout << "'" << socket->identifier() << "', "; } } std::cout << "\n"; - BLI_assert(false); + BLI_assert_unreachable(); } else if (found_socket->flag & SOCK_UNAVAIL) { std::cout << "The socket corresponding to the identifier '" << identifier << "' is disabled.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } - else if (!input_values_.contains(identifier)) { + else if (!provider_->can_get_input(identifier)) { std::cout << "The identifier '" << identifier << "' is valid, but there is no value for it anymore.\n"; std::cout << "Most likely it has been extracted before.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } else if (requested_type != nullptr) { const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo); if (*requested_type != expected_type) { std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } } } -void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const +void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const { bNodeSocket *found_socket = nullptr; - for (const OutputSocketRef *socket : node_->outputs()) { + for (const OutputSocketRef *socket : provider_->dnode->outputs()) { if (socket->identifier() == identifier) { found_socket = socket->bsocket(); break; @@ -227,29 +239,29 @@ void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &va if (found_socket == nullptr) { std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n"; std::cout << "Possible identifiers are: "; - for (const OutputSocketRef *socket : node_->outputs()) { + for (const OutputSocketRef *socket : provider_->dnode->outputs()) { if (socket->is_available()) { std::cout << "'" << socket->identifier() << "', "; } } std::cout << "\n"; - BLI_assert(false); + BLI_assert_unreachable(); } else if (found_socket->flag & SOCK_UNAVAIL) { std::cout << "The socket corresponding to the identifier '" << identifier << "' is disabled.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } - else if (output_values_.contains(identifier)) { + else if (!provider_->can_set_output(identifier)) { std::cout << "The identifier '" << identifier << "' has been set already.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } else { const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo); if (value_type != expected_type) { std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; - BLI_assert(false); + BLI_assert_unreachable(); } } } diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index b583523da98..ce2848b52a0 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -35,7 +35,6 @@ #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_node.h" -#include "BKE_persistent_data_handle.hh" #include "DNA_collection_types.h" @@ -294,6 +293,21 @@ void node_socket_init_default_value(bNodeSocket *sock) sock->default_value = dval; break; + } + case SOCK_TEXTURE: { + bNodeSocketValueTexture *dval = (bNodeSocketValueTexture *)MEM_callocN( + sizeof(bNodeSocketValueTexture), "node socket value texture"); + dval->value = nullptr; + + sock->default_value = dval; + break; + } + case SOCK_MATERIAL: { + bNodeSocketValueMaterial *dval = (bNodeSocketValueMaterial *)MEM_callocN( + sizeof(bNodeSocketValueMaterial), "node socket value material"); + dval->value = nullptr; + + sock->default_value = dval; break; } } @@ -375,6 +389,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) id_us_plus(&toval->value->id); break; } + case SOCK_TEXTURE: { + bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to->default_value; + bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from->default_value; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } } to->flag |= (from->flag & SOCK_HIDE_VALUE); @@ -633,63 +654,17 @@ static bNodeSocketType *make_socket_type_string() return socktype; } -class ObjectSocketMultiFunction : public blender::fn::MultiFunction { - private: - Object *object_; - - public: - ObjectSocketMultiFunction(Object *object) : object_(object) - { - static blender::fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static blender::fn::MFSignature create_signature() - { - blender::fn::MFSignatureBuilder signature{"Object Socket"}; - signature.depends_on_context(); - signature.single_output<blender::bke::PersistentObjectHandle>("Object"); - return signature.build(); - } - - void call(blender::IndexMask mask, - blender::fn::MFParams params, - blender::fn::MFContext context) const override - { - blender::MutableSpan output = - params.uninitialized_single_output<blender::bke::PersistentObjectHandle>(0, "Object"); - - /* Try to get a handle map, so that the object can be converted to a handle. */ - const blender::bke::PersistentDataHandleMap *handle_map = - context.get_global_context<blender::bke::PersistentDataHandleMap>( - "PersistentDataHandleMap"); - - if (handle_map == nullptr) { - /* Return empty handles when there is no handle map. */ - output.fill_indices(mask, blender::bke::PersistentObjectHandle()); - return; - } - - blender::bke::PersistentObjectHandle handle = handle_map->lookup(object_); - for (int64_t i : mask) { - output[i] = handle; - } - } -}; - -MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle); -MAKE_CPP_TYPE(PersistentCollectionHandle, blender::bke::PersistentCollectionHandle); +MAKE_CPP_TYPE(Object, Object *) +MAKE_CPP_TYPE(Collection, Collection *) +MAKE_CPP_TYPE(Texture, Tex *) +MAKE_CPP_TYPE(Material, Material *) static bNodeSocketType *make_socket_type_object() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); - socktype->get_cpp_type = []() { - /* Objects are not passed along as raw pointers, but as handles. */ - return &blender::fn::CPPType::get<blender::bke::PersistentObjectHandle>(); - }; - socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) { - Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value; - builder.construct_generator_fn<ObjectSocketMultiFunction>(object); + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); }; + socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value; }; return socktype; } @@ -707,9 +682,29 @@ static bNodeSocketType *make_socket_type_geometry() static bNodeSocketType *make_socket_type_collection() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE); - socktype->get_cpp_type = []() { - /* Objects are not passed along as raw pointers, but as handles. */ - return &blender::fn::CPPType::get<blender::bke::PersistentCollectionHandle>(); + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); }; + socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value; + }; + return socktype; +} + +static bNodeSocketType *make_socket_type_texture() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE); + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); }; + socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value; + }; + return socktype; +} + +static bNodeSocketType *make_socket_type_material() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); }; + socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value; }; return socktype; } @@ -755,5 +750,9 @@ void register_standard_node_socket_types(void) nodeRegisterSocketType(make_socket_type_collection()); + nodeRegisterSocketType(make_socket_type_texture()); + + nodeRegisterSocketType(make_socket_type_material()); + nodeRegisterSocketType(make_socket_type_virtual()); } diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index e42572b9cb7..5c0bc0b5ebc 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -240,7 +240,10 @@ void InputSocketRef::foreach_logical_origin(FunctionRef<void(const OutputSocketR } const OutputSocketRef &origin = link->from(); const NodeRef &origin_node = origin.node(); - if (origin_node.is_reroute_node()) { + if (!origin.is_available()) { + /* Non available sockets are ignored. */ + } + else if (origin_node.is_reroute_node()) { const InputSocketRef &reroute_input = origin_node.input(0); const OutputSocketRef &reroute_output = origin_node.output(0); skipped_fn.call_safe(reroute_input); @@ -281,7 +284,10 @@ void OutputSocketRef::foreach_logical_target( } const InputSocketRef &target = link->to(); const NodeRef &target_node = target.node(); - if (target_node.is_reroute_node()) { + if (!target.is_available()) { + /* Non available sockets are ignored. */ + } + else if (target_node.is_reroute_node()) { const OutputSocketRef &reroute_output = target_node.output(0); skipped_fn.call_safe(target); skipped_fn.call_safe(reroute_output); diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index f87e63d195d..1aec280fd2b 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -459,6 +459,22 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype return -1; } } + case SOCK_TEXTURE: { + switch (from) { + case SOCK_TEXTURE: + return 1; + default: + return -1; + } + } + case SOCK_MATERIAL: { + switch (from) { + case SOCK_MATERIAL: + return 1; + default: + return -1; + } + } default: return -1; } diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc index 1c1b7c7feb5..63f7b8a9ee8 100644 --- a/source/blender/nodes/intern/type_conversions.cc +++ b/source/blender/nodes/intern/type_conversions.cc @@ -24,6 +24,9 @@ namespace blender::nodes { +using fn::GVArrayPtr; +using fn::GVMutableArray; +using fn::GVMutableArrayPtr; using fn::MFDataType; template<typename From, typename To, To (*ConversionF)(const From &)> @@ -227,6 +230,11 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type, const void *from_value, void *to_value) const { + if (from_type == to_type) { + from_type.copy_to_uninitialized(from_value, to_value); + return; + } + const ConversionFunctions *functions = this->get_conversion_functions( MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type)); BLI_assert(functions != nullptr); @@ -234,4 +242,108 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type, functions->convert_single_to_uninitialized(from_value, to_value); } +class GVArray_For_ConvertedGVArray : public GVArray { + private: + GVArrayPtr varray_; + const CPPType &from_type_; + ConversionFunctions old_to_new_conversions_; + + public: + GVArray_For_ConvertedGVArray(GVArrayPtr varray, + const CPPType &to_type, + const DataTypeConversions &conversions) + : GVArray(to_type, varray->size()), varray_(std::move(varray)), from_type_(varray_->type()) + { + old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type); + } + + private: + void get_impl(const int64_t index, void *r_value) const override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + varray_->get(index, buffer); + old_to_new_conversions_.convert_single_to_initialized(buffer, r_value); + from_type_.destruct(buffer); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + varray_->get(index, buffer); + old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value); + from_type_.destruct(buffer); + } +}; + +class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArray { + private: + GVMutableArrayPtr varray_; + const CPPType &from_type_; + ConversionFunctions old_to_new_conversions_; + ConversionFunctions new_to_old_conversions_; + + public: + GVMutableArray_For_ConvertedGVMutableArray(GVMutableArrayPtr varray, + const CPPType &to_type, + const DataTypeConversions &conversions) + : GVMutableArray(to_type, varray->size()), + varray_(std::move(varray)), + from_type_(varray_->type()) + { + old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type); + new_to_old_conversions_ = *conversions.get_conversion_functions(to_type, from_type_); + } + + private: + void get_impl(const int64_t index, void *r_value) const override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + varray_->get(index, buffer); + old_to_new_conversions_.convert_single_to_initialized(buffer, r_value); + from_type_.destruct(buffer); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + varray_->get(index, buffer); + old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value); + from_type_.destruct(buffer); + } + + void set_by_move_impl(const int64_t index, void *value) override + { + BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); + new_to_old_conversions_.convert_single_to_uninitialized(value, buffer); + varray_->set_by_relocate(index, buffer); + } +}; + +fn::GVArrayPtr DataTypeConversions::try_convert(fn::GVArrayPtr varray, + const CPPType &to_type) const +{ + const CPPType &from_type = varray->type(); + if (from_type == to_type) { + return varray; + } + if (!this->is_convertible(from_type, to_type)) { + return {}; + } + return std::make_unique<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this); +} + +fn::GVMutableArrayPtr DataTypeConversions::try_convert(fn::GVMutableArrayPtr varray, + const CPPType &to_type) const +{ + const CPPType &from_type = varray->type(); + if (from_type == to_type) { + return varray; + } + if (!this->is_convertible(from_type, to_type)) { + return {}; + } + return std::make_unique<GVMutableArray_For_ConvertedGVMutableArray>( + std::move(varray), to_type, *this); +} + } // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index 83f476884e6..5ec982c4e7f 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -184,6 +184,12 @@ static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) return true; } +static bool shader_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, + bNodeTreeType *UNUSED(ntreetype)) +{ + return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER); +} + bNodeTreeType *ntreeType_Shader; void register_node_tree_type_sh(void) @@ -205,6 +211,7 @@ void register_node_tree_type_sh(void) tt->poll = shader_tree_poll; tt->get_from_context = shader_get_from_context; tt->validate_link = shader_validate_link; + tt->valid_socket_type = shader_node_tree_socket_type_valid; tt->rna_ext.srna = &RNA_ShaderNodeTree; diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 48027dc847b..2ae722e3cd8 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -152,6 +152,12 @@ static void update(bNodeTree *ntree) } } +static bool texture_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, + bNodeTreeType *UNUSED(ntreetype)) +{ + return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA); +} + bNodeTreeType *ntreeType_Texture; void register_node_tree_type_tex(void) @@ -171,6 +177,7 @@ void register_node_tree_type_tex(void) tt->local_sync = local_sync; tt->local_merge = local_merge; tt->get_from_context = texture_get_from_context; + tt->valid_socket_type = texture_node_tree_socket_type_valid; tt->rna_ext.srna = &RNA_TextureNodeTree; diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index ebe6ed79578..f1a8d450ea5 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1087,7 +1087,7 @@ PyDoc_STRVAR( "3.0.\n"); static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw) { - static const char *kwlist[] = {"object", "depsgraph", "deform", "cage", "face_normals", NULL}; + static const char *kwlist[] = {"object", "depsgraph", "cage", "face_normals", NULL}; PyObject *py_object; PyObject *py_depsgraph; Object *ob, *ob_eval; @@ -1095,7 +1095,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject struct Scene *scene_eval; Mesh *me_eval; BMesh *bm; - bool use_deform = true; bool use_cage = false; bool use_fnorm = true; const CustomData_MeshMasks data_masks = CD_MASK_BMESH; @@ -1104,13 +1103,11 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject if (!PyArg_ParseTupleAndKeywords(args, kw, - "OO|O&O&O&:from_object", + "OO|O&O&:from_object", (char **)kwlist, &py_object, &py_depsgraph, PyC_ParseBool, - &use_deform, - PyC_ParseBool, &use_cage, PyC_ParseBool, &use_fnorm) || @@ -1125,13 +1122,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject return NULL; } - if (use_deform == false) { - PyErr_WarnEx(PyExc_FutureWarning, - "from_object(...): the deform parameter is deprecated, assumed to be True, and " - "will be removed in version 3.0", - 1); - } - const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER; scene_eval = DEG_get_evaluated_scene(depsgraph); ob_eval = DEG_get_evaluated_object(depsgraph, ob); diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index fc7054f675a..9b6ca7fcec5 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -42,6 +42,18 @@ extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id); extern PyObject *pyrna_id_CreatePyObject(ID *id); extern bool pyrna_id_CheckPyObject(PyObject *obj); +/* Currently there is no need to expose this publicly. */ +static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed); +static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed); +static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed); + +static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group); +static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group); +static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group); + +static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type); +static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value); + /* -------------------------------------------------------------------- */ /** \name Python from ID-Property (Internal Conversions) * @@ -756,13 +768,7 @@ static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self) { - BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type); - iter->group = self; - Py_INCREF(self); - iter->mode = IDPROP_ITER_KEYS; - iter->cur = self->prop->data.group.first; - PyObject_GC_Track(iter); - return (PyObject *)iter; + return BPy_IDGroup_ViewKeys_CreatePyObject(self); } /* for simple, non nested types this is the same as BPy_IDGroup_WrapData */ @@ -875,6 +881,370 @@ PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) /** \} */ /* -------------------------------------------------------------------- */ +/** \name ID-Property Group Iterator Type + * \{ */ + +static PyObject *BPy_IDGroup_Iter_repr(BPy_IDGroup_Iter *self) +{ + if (self->group == NULL) { + return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name); + } + return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name); +} + +static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self) +{ + if (self->group != NULL) { + PyObject_GC_UnTrack(self); + } + Py_CLEAR(self->group); + PyObject_GC_Del(self); +} + +static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg) +{ + Py_VISIT(self->group); + return 0; +} + +static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self) +{ + Py_CLEAR(self->group); + return 0; +} + +static bool BPy_Group_Iter_same_size_or_raise_error(BPy_IDGroup_Iter *self) +{ + if (self->len_init == self->group->prop->len) { + return true; + } + PyErr_SetString(PyExc_RuntimeError, "IDPropertyGroup changed size during iteration"); + return false; +} + +static PyObject *BPy_Group_IterKeys_next(BPy_IDGroup_Iter *self) +{ + if (self->cur != NULL) { + /* When `cur` is set, `group` cannot be NULL. */ + if (!BPy_Group_Iter_same_size_or_raise_error(self)) { + return NULL; + } + IDProperty *cur = self->cur; + self->cur = self->reversed ? self->cur->prev : self->cur->next; + return PyUnicode_FromString(cur->name); + } + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +static PyObject *BPy_Group_IterValues_next(BPy_IDGroup_Iter *self) +{ + if (self->cur != NULL) { + /* When `cur` is set, `group` cannot be NULL. */ + if (!BPy_Group_Iter_same_size_or_raise_error(self)) { + return NULL; + } + IDProperty *cur = self->cur; + self->cur = self->reversed ? self->cur->prev : self->cur->next; + return BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop); + } + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +static PyObject *BPy_Group_IterItems_next(BPy_IDGroup_Iter *self) +{ + if (self->cur != NULL) { + /* When `cur` is set, `group` cannot be NULL. */ + if (!BPy_Group_Iter_same_size_or_raise_error(self)) { + return NULL; + } + IDProperty *cur = self->cur; + self->cur = self->reversed ? self->cur->prev : self->cur->next; + PyObject *ret = PyTuple_New(2); + PyTuple_SET_ITEMS(ret, + PyUnicode_FromString(cur->name), + BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop)); + return ret; + } + PyErr_SetNone(PyExc_StopIteration); + return NULL; +} + +PyTypeObject BPy_IDGroup_IterKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)}; +PyTypeObject BPy_IDGroup_IterValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)}; +PyTypeObject BPy_IDGroup_IterItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)}; + +/* ID Property Group Iterator. */ +static void IDGroup_Iter_init_type(void) +{ +#define SHARED_MEMBER_SET(member, value) \ + { \ + k_ty->member = v_ty->member = i_ty->member = value; \ + } \ + ((void)0) + + PyTypeObject *k_ty = &BPy_IDGroup_IterKeys_Type; + PyTypeObject *v_ty = &BPy_IDGroup_IterValues_Type; + PyTypeObject *i_ty = &BPy_IDGroup_IterItems_Type; + + /* Unique members. */ + k_ty->tp_name = "IDPropertyGroupIterKeys"; + v_ty->tp_name = "IDPropertyGroupIterValues"; + i_ty->tp_name = "IDPropertyGroupIterItems"; + + k_ty->tp_iternext = (iternextfunc)BPy_Group_IterKeys_next; + v_ty->tp_iternext = (iternextfunc)BPy_Group_IterValues_next; + i_ty->tp_iternext = (iternextfunc)BPy_Group_IterItems_next; + + /* Shared members. */ + SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_Iter)); + SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_Iter_dealloc); + SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_Iter_repr); + SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC); + SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_Iter_traverse); + SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_Iter_clear); + SHARED_MEMBER_SET(tp_iter, PyObject_SelfIter); + +#undef SHARED_MEMBER_SET +} + +static PyObject *IDGroup_Iter_New_WithType(BPy_IDProperty *group, + const bool reversed, + PyTypeObject *type) +{ + BLI_assert(group ? group->prop->type == IDP_GROUP : true); + BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, type); + iter->reversed = reversed; + iter->group = group; + if (group != NULL) { + Py_INCREF(group); + PyObject_GC_Track(iter); + iter->cur = (reversed ? group->prop->data.group.last : group->prop->data.group.first); + iter->len_init = group->prop->len; + } + else { + iter->cur = NULL; + iter->len_init = 0; + } + return (PyObject *)iter; +} + +static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed) +{ + return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterKeys_Type); +} + +static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed) +{ + return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterValues_Type); +} + +static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed) +{ + return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterItems_Type); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ID-Property Group View Types (Keys/Values/Items) + * + * This view types is a thin wrapper on keys/values/items, this matches Python's `dict_view` type. + * The is returned by `property.keys()` and is separate from the iterator that loops over keys. + * + * There are some less common features this type could support (matching Python's `dict_view`) + * + * TODO: + * - Efficient contains checks for values and items which currently convert to a list first. + * - Missing `dict_views.isdisjoint`. + * - Missing `tp_as_number` (`nb_subtract`, `nb_and`, `nb_xor`, `nb_or`). + * \{ */ + +static PyObject *BPy_IDGroup_View_repr(BPy_IDGroup_View *self) +{ + if (self->group == NULL) { + return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name); + } + return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name); +} + +static void BPy_IDGroup_View_dealloc(BPy_IDGroup_View *self) +{ + if (self->group != NULL) { + PyObject_GC_UnTrack(self); + } + Py_CLEAR(self->group); + PyObject_GC_Del(self); +} + +static int BPy_IDGroup_View_traverse(BPy_IDGroup_View *self, visitproc visit, void *arg) +{ + Py_VISIT(self->group); + return 0; +} + +static int BPy_IDGroup_View_clear(BPy_IDGroup_View *self) +{ + Py_CLEAR(self->group); + return 0; +} + +/* View Specific API's (Key/Value/Items). */ + +static PyObject *BPy_Group_ViewKeys_iter(BPy_IDGroup_View *self) +{ + return BPy_IDGroup_IterKeys_CreatePyObject(self->group, self->reversed); +} + +static PyObject *BPy_Group_ViewValues_iter(BPy_IDGroup_View *self) +{ + return BPy_IDGroup_IterValues_CreatePyObject(self->group, self->reversed); +} + +static PyObject *BPy_Group_ViewItems_iter(BPy_IDGroup_View *self) +{ + return BPy_IDGroup_IterItems_CreatePyObject(self->group, self->reversed); +} + +static Py_ssize_t BPy_Group_View_len(BPy_IDGroup_View *self) +{ + if (self->group == NULL) { + return 0; + } + return self->group->prop->len; +} + +static int BPy_Group_ViewKeys_Contains(BPy_IDGroup_View *self, PyObject *value) +{ + if (self->group == NULL) { + return 0; + } + return BPy_IDGroup_Contains(self->group, value); +} + +static int BPy_Group_ViewValues_Contains(BPy_IDGroup_View *self, PyObject *value) +{ + if (self->group == NULL) { + return 0; + } + /* TODO: implement this without first converting to a list. */ + PyObject *list = PySequence_List((PyObject *)self); + const int result = PySequence_Contains(list, value); + Py_DECREF(list); + return result; +} + +static int BPy_Group_ViewItems_Contains(BPy_IDGroup_View *self, PyObject *value) +{ + if (self->group == NULL) { + return 0; + } + /* TODO: implement this without first converting to a list. */ + PyObject *list = PySequence_List((PyObject *)self); + const int result = PySequence_Contains(list, value); + Py_DECREF(list); + return result; +} + +static PySequenceMethods BPy_IDGroup_ViewKeys_as_sequence = { + (lenfunc)BPy_Group_View_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)BPy_Group_ViewKeys_Contains, /* sq_contains */ +}; + +static PySequenceMethods BPy_IDGroup_ViewValues_as_sequence = { + (lenfunc)BPy_Group_View_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)BPy_Group_ViewValues_Contains, /* sq_contains */ +}; + +static PySequenceMethods BPy_IDGroup_ViewItems_as_sequence = { + (lenfunc)BPy_Group_View_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)BPy_Group_ViewItems_Contains, /* sq_contains */ +}; + +/* Methods. */ + +PyDoc_STRVAR(BPy_IDGroup_View_reversed_doc, + "Return a reverse iterator over the ID Property keys values or items."); + +static PyObject *BPy_IDGroup_View_reversed(BPy_IDGroup_View *self, PyObject *UNUSED(ignored)) +{ + BPy_IDGroup_View *result = IDGroup_View_New_WithType(self->group, Py_TYPE(self)); + result->reversed = !self->reversed; + return (PyObject *)result; +} + +static PyMethodDef BPy_IDGroup_View_methods[] = { + {"__reversed__", + (PyCFunction)(void (*)(void))BPy_IDGroup_View_reversed, + METH_NOARGS, + BPy_IDGroup_View_reversed_doc}, + {NULL, NULL}, +}; + +PyTypeObject BPy_IDGroup_ViewKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)}; +PyTypeObject BPy_IDGroup_ViewValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)}; +PyTypeObject BPy_IDGroup_ViewItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)}; + +/* ID Property Group View. */ +static void IDGroup_View_init_type(void) +{ + PyTypeObject *k_ty = &BPy_IDGroup_ViewKeys_Type; + PyTypeObject *v_ty = &BPy_IDGroup_ViewValues_Type; + PyTypeObject *i_ty = &BPy_IDGroup_ViewItems_Type; + + /* Unique members. */ + k_ty->tp_name = "IDPropertyGroupViewKeys"; + v_ty->tp_name = "IDPropertyGroupViewValues"; + i_ty->tp_name = "IDPropertyGroupViewItems"; + + k_ty->tp_iter = (getiterfunc)BPy_Group_ViewKeys_iter; + v_ty->tp_iter = (getiterfunc)BPy_Group_ViewValues_iter; + i_ty->tp_iter = (getiterfunc)BPy_Group_ViewItems_iter; + + k_ty->tp_as_sequence = &BPy_IDGroup_ViewKeys_as_sequence; + v_ty->tp_as_sequence = &BPy_IDGroup_ViewValues_as_sequence; + i_ty->tp_as_sequence = &BPy_IDGroup_ViewItems_as_sequence; + + /* Shared members. */ +#define SHARED_MEMBER_SET(member, value) \ + { \ + k_ty->member = v_ty->member = i_ty->member = value; \ + } \ + ((void)0) + + SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_View)); + SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_View_dealloc); + SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_View_repr); + SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC); + SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_View_traverse); + SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_View_clear); + SHARED_MEMBER_SET(tp_methods, BPy_IDGroup_View_methods); + +#undef SHARED_MEMBER_SET +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name ID-Property Group Methods * \{ */ @@ -923,22 +1293,6 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args) return pyform; } -PyDoc_STRVAR( - BPy_IDGroup_iter_items_doc, - ".. method:: iteritems()\n" - "\n" - " Iterate through the items in the dict; behaves like dictionary method iteritems.\n"); -static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self) -{ - BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type); - iter->group = self; - Py_INCREF(self); - iter->mode = IDPROP_ITER_ITEMS; - iter->cur = self->prop->data.group.first; - PyObject_GC_Track(iter); - return (PyObject *)iter; -} - /* utility function */ static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func) { @@ -1023,13 +1377,37 @@ PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop) return seq; } +PyObject *BPy_Wrap_GetKeys_View_WithID(ID *id, IDProperty *prop) +{ + PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL; + PyObject *ret = BPy_IDGroup_ViewKeys_CreatePyObject((BPy_IDProperty *)self); + Py_XDECREF(self); /* Owned by `ret`. */ + return ret; +} + +PyObject *BPy_Wrap_GetValues_View_WithID(ID *id, IDProperty *prop) +{ + PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL; + PyObject *ret = BPy_IDGroup_ViewValues_CreatePyObject((BPy_IDProperty *)self); + Py_XDECREF(self); /* Owned by `ret`. */ + return ret; +} + +PyObject *BPy_Wrap_GetItems_View_WithID(ID *id, IDProperty *prop) +{ + PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL; + PyObject *ret = BPy_IDGroup_ViewItems_CreatePyObject((BPy_IDProperty *)self); + Py_XDECREF(self); /* Owned by `ret`. */ + return ret; +} + PyDoc_STRVAR(BPy_IDGroup_keys_doc, ".. method:: keys()\n" "\n" " Return the keys associated with this group as a list of strings.\n"); static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self) { - return BPy_Wrap_GetKeys(self->prop); + return BPy_IDGroup_ViewKeys_CreatePyObject(self); } PyDoc_STRVAR(BPy_IDGroup_values_doc, @@ -1038,16 +1416,16 @@ PyDoc_STRVAR(BPy_IDGroup_values_doc, " Return the values associated with this group.\n"); static PyObject *BPy_IDGroup_values(BPy_IDProperty *self) { - return BPy_Wrap_GetValues(self->id, self->prop); + return BPy_IDGroup_ViewValues_CreatePyObject(self); } PyDoc_STRVAR(BPy_IDGroup_items_doc, ".. method:: items()\n" "\n" - " Return the items associated with this group.\n"); + " Iterate through the items in the dict; behaves like dictionary method items.\n"); static PyObject *BPy_IDGroup_items(BPy_IDProperty *self) { - return BPy_Wrap_GetItems(self->id, self->prop); + return BPy_IDGroup_ViewItems_CreatePyObject(self); } static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value) @@ -1148,7 +1526,6 @@ static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args) static struct PyMethodDef BPy_IDGroup_methods[] = { {"pop", (PyCFunction)BPy_IDGroup_pop, METH_VARARGS, BPy_IDGroup_pop_doc}, - {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc}, {"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc}, {"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc}, {"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc}, @@ -1678,120 +2055,59 @@ PyTypeObject BPy_IDArray_Type = { /** \} */ /* -------------------------------------------------------------------- */ -/** \name ID-Property Group Iterator Type +/** \name Initialize Types * \{ */ -static PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self) +void IDProp_Init_Types(void) { - return PyUnicode_FromFormat("(ID Property Group Iter \"%s\")", self->group->prop->name); -} + IDGroup_Iter_init_type(); + IDGroup_View_init_type(); -static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self) -{ - PyObject_GC_UnTrack(self); - Py_CLEAR(self->group); - PyObject_GC_Del(self); + PyType_Ready(&BPy_IDGroup_Type); + PyType_Ready(&BPy_IDArray_Type); + + PyType_Ready(&BPy_IDGroup_IterKeys_Type); + PyType_Ready(&BPy_IDGroup_IterValues_Type); + PyType_Ready(&BPy_IDGroup_IterItems_Type); + + PyType_Ready(&BPy_IDGroup_ViewKeys_Type); + PyType_Ready(&BPy_IDGroup_ViewValues_Type); + PyType_Ready(&BPy_IDGroup_ViewItems_Type); } -static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg) +/** + * \note `group` may be NULL, unlike most other uses of this argument. + * This is supported so RNA keys/values/items methods returns an iterator with the expected type: + * - Without having ID-properties. + * - Without supporting #BPy_IDProperty.prop being NULL, which would incur many more checks. + * Python's own dictionary-views also works this way too. + */ +static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type) { - Py_VISIT(self->group); - return 0; + BLI_assert(group ? group->prop->type == IDP_GROUP : true); + BPy_IDGroup_View *iter = PyObject_GC_New(BPy_IDGroup_View, type); + iter->reversed = false; + iter->group = group; + if (group != NULL) { + Py_INCREF(group); + PyObject_GC_Track(iter); + } + return iter; } -static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self) +static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group) { - Py_CLEAR(self->group); - return 0; + return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewKeys_Type); } -static PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self) +static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group) { - - if (self->cur) { - PyObject *ret; - IDProperty *cur; - - cur = self->cur; - self->cur = self->cur->next; - - if (self->mode == IDPROP_ITER_ITEMS) { - ret = PyTuple_New(2); - PyTuple_SET_ITEMS(ret, - PyUnicode_FromString(cur->name), - BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop)); - return ret; - } - - return PyUnicode_FromString(cur->name); - } - - PyErr_SetNone(PyExc_StopIteration); - return NULL; + return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewValues_Type); } -PyTypeObject BPy_IDGroup_Iter_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - /* For printing, in format "<module>.<name>" */ - "IDPropertyGroupIter", /* char *tp_name; */ - sizeof(BPy_IDGroup_Iter), /* int tp_basicsize; */ - 0, /* tp_itemsize; For allocation */ - - /* Methods to implement standard operations */ - - (destructor)BPy_IDGroup_Iter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ - NULL, /* cmpfunc tp_compare; */ - (reprfunc)IDGroup_Iter_repr, /* reprfunc tp_repr; */ - - /* Method suites for standard classes */ - - NULL, /* PyNumberMethods *tp_as_number; */ - NULL, /* PySequenceMethods *tp_as_sequence; */ - NULL, /* PyMappingMethods *tp_as_mapping; */ - - /* More standard operations (here for binary compatibility) */ - - NULL, /* hashfunc tp_hash; */ - NULL, /* ternaryfunc tp_call; */ - NULL, /* reprfunc tp_str; */ - NULL, /* getattrofunc tp_getattro; */ - NULL, /* setattrofunc tp_setattro; */ - - /* Functions to access object as input/output buffer */ - NULL, /* PyBufferProcs *tp_as_buffer; */ - - /*** Flags to define presence of optional/expanded features ***/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* long tp_flags; */ - - NULL, /* char *tp_doc; Documentation string */ - /*** Assigned meaning in release 2.0 ***/ - /* call function for all accessible objects */ - (traverseproc)BPy_IDGroup_Iter_traverse, /* traverseproc tp_traverse; */ - - /* delete references to contained objects */ - (inquiry)BPy_IDGroup_Iter_clear, /* inquiry tp_clear; */ - - /*** Assigned meaning in release 2.1 ***/ - /*** rich comparisons ***/ - NULL, /* richcmpfunc tp_richcompare; */ - - /*** weak reference enabler ***/ - 0, /* long tp_weaklistoffset; */ - - /*** Added in release 2.2 ***/ - /* Iterators */ - PyObject_SelfIter, /* getiterfunc tp_iter; */ - (iternextfunc)BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */ -}; - -void IDProp_Init_Types(void) +static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group) { - PyType_Ready(&BPy_IDGroup_Type); - PyType_Ready(&BPy_IDGroup_Iter_Type); - PyType_Ready(&BPy_IDArray_Type); + return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewItems_Type); } /** \} */ @@ -1822,7 +2138,15 @@ static PyObject *BPyInit_idprop_types(void) /* bmesh_py_types.c */ PyModule_AddType(submodule, &BPy_IDGroup_Type); - PyModule_AddType(submodule, &BPy_IDGroup_Iter_Type); + + PyModule_AddType(submodule, &BPy_IDGroup_ViewKeys_Type); + PyModule_AddType(submodule, &BPy_IDGroup_ViewValues_Type); + PyModule_AddType(submodule, &BPy_IDGroup_ViewItems_Type); + + PyModule_AddType(submodule, &BPy_IDGroup_IterKeys_Type); + PyModule_AddType(submodule, &BPy_IDGroup_IterValues_Type); + PyModule_AddType(submodule, &BPy_IDGroup_IterItems_Type); + PyModule_AddType(submodule, &BPy_IDArray_Type); return submodule; diff --git a/source/blender/python/generic/idprop_py_api.h b/source/blender/python/generic/idprop_py_api.h index 4cccea3a936..1e8e26a3b6d 100644 --- a/source/blender/python/generic/idprop_py_api.h +++ b/source/blender/python/generic/idprop_py_api.h @@ -25,16 +25,35 @@ struct ID; struct IDProperty; extern PyTypeObject BPy_IDArray_Type; -extern PyTypeObject BPy_IDGroup_Iter_Type; extern PyTypeObject BPy_IDGroup_Type; +extern PyTypeObject BPy_IDGroup_ViewKeys_Type; +extern PyTypeObject BPy_IDGroup_ViewValues_Type; +extern PyTypeObject BPy_IDGroup_ViewItems_Type; + +extern PyTypeObject BPy_IDGroup_IterKeys_Type; +extern PyTypeObject BPy_IDGroup_IterValues_Type; +extern PyTypeObject BPy_IDGroup_IterItems_Type; + #define BPy_IDArray_Check(v) (PyObject_TypeCheck(v, &BPy_IDArray_Type)) #define BPy_IDArray_CheckExact(v) (Py_TYPE(v) == &BPy_IDArray_Type) -#define BPy_IDGroup_Iter_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Iter_Type)) -#define BPy_IDGroup_Iter_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Iter_Type) #define BPy_IDGroup_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Type)) #define BPy_IDGroup_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Type) +#define BPy_IDGroup_ViewKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewKeys_Type)) +#define BPy_IDGroup_ViewKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewKeys_Type) +#define BPy_IDGroup_ViewValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewValues_Type)) +#define BPy_IDGroup_ViewValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewValues_Type) +#define BPy_IDGroup_ViewItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewItems_Type)) +#define BPy_IDGroup_ViewItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewItems_Type) + +#define BPy_IDGroup_IterKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterKeys_Type)) +#define BPy_IDGroup_IterKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterKeys_Type) +#define BPy_IDGroup_IterValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterValues_Type)) +#define BPy_IDGroup_IterValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterValues_Type) +#define BPy_IDGroup_IterItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterItems_Type)) +#define BPy_IDGroup_IterItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterItems_Type) + typedef struct BPy_IDProperty { PyObject_VAR_HEAD struct ID *id; /* can be NULL */ @@ -52,12 +71,28 @@ typedef struct BPy_IDGroup_Iter { PyObject_VAR_HEAD BPy_IDProperty *group; struct IDProperty *cur; - int mode; + /** Use for detecting manipulation during iteration (which is not allowed). */ + int len_init; + /** Iterate in the reverse direction. */ + bool reversed; } BPy_IDGroup_Iter; +/** Use to implement `IDPropertyGroup.keys/values/items` */ +typedef struct BPy_IDGroup_View { + PyObject_VAR_HEAD + /** This will be NULL when accessing keys on data that has no ID properties. */ + BPy_IDProperty *group; + bool reversed; +} BPy_IDGroup_View; + PyObject *BPy_Wrap_GetKeys(struct IDProperty *prop); PyObject *BPy_Wrap_GetValues(struct ID *id, struct IDProperty *prop); PyObject *BPy_Wrap_GetItems(struct ID *id, struct IDProperty *prop); + +PyObject *BPy_Wrap_GetKeys_View_WithID(struct ID *id, struct IDProperty *prop); +PyObject *BPy_Wrap_GetValues_View_WithID(struct ID *id, struct IDProperty *prop); +PyObject *BPy_Wrap_GetItems_View_WithID(struct ID *id, struct IDProperty *prop); + int BPy_Wrap_SetMapItem(struct IDProperty *prop, PyObject *key, PyObject *val); PyObject *BPy_IDGroup_MapDataToPy(struct IDProperty *prop); @@ -67,6 +102,3 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *key, struct IDProperty *grou void IDProp_Init_Types(void); PyObject *BPyInit_idprop(void); - -#define IDPROP_ITER_KEYS 0 -#define IDPROP_ITER_ITEMS 1 diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt index fe5c559fcc0..1424b35a004 100644 --- a/source/blender/python/gpu/CMakeLists.txt +++ b/source/blender/python/gpu/CMakeLists.txt @@ -37,10 +37,12 @@ set(SRC gpu_py_api.c gpu_py_batch.c gpu_py_buffer.c + gpu_py_capabilities.c gpu_py_element.c gpu_py_framebuffer.c gpu_py_matrix.c gpu_py_offscreen.c + gpu_py_platform.c gpu_py_select.c gpu_py_shader.c gpu_py_state.c @@ -54,10 +56,12 @@ set(SRC gpu_py_api.h gpu_py_batch.h gpu_py_buffer.h + gpu_py_capabilities.h gpu_py_element.h gpu_py_framebuffer.h gpu_py_matrix.h gpu_py_offscreen.h + gpu_py_platform.h gpu_py_select.h gpu_py_shader.h gpu_py_state.h diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c index 0bc18e73d0c..5119b3612f8 100644 --- a/source/blender/python/gpu/gpu_py_api.c +++ b/source/blender/python/gpu/gpu_py_api.c @@ -30,7 +30,9 @@ #include "../generic/python_utildefines.h" +#include "gpu_py_capabilities.h" #include "gpu_py_matrix.h" +#include "gpu_py_platform.h" #include "gpu_py_select.h" #include "gpu_py_state.h" #include "gpu_py_types.h" @@ -61,9 +63,15 @@ PyObject *BPyInit_gpu(void) PyModule_AddObject(mod, "types", (submodule = bpygpu_types_init())); PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule); + PyModule_AddObject(mod, "capabilities", (submodule = bpygpu_capabilities_init())); + PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule); + PyModule_AddObject(mod, "matrix", (submodule = bpygpu_matrix_init())); PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule); + PyModule_AddObject(mod, "platform", (submodule = bpygpu_platform_init())); + PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule); + PyModule_AddObject(mod, "select", (submodule = bpygpu_select_init())); PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule); diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index d0965e83e33..e36e3b42617 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -37,17 +37,90 @@ #include "gpu_py_buffer.h" -// #define PYGPU_BUFFER_PROTOCOL +//#define PYGPU_BUFFER_PROTOCOL +#define MAX_DIMENSIONS 64 /* -------------------------------------------------------------------- */ /** \name Utility Functions * \{ */ -static bool pygpu_buffer_dimensions_compare(int ndim, - const Py_ssize_t *shape_a, - const Py_ssize_t *shape_b) +static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len) +{ + Py_ssize_t tot = shape[0]; + for (int i = 1; i < shape_len; i++) { + tot *= shape[i]; + } + + return tot; +} + +static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a, + const Py_ssize_t shape_a_len, + const Py_ssize_t *shape_b, + const Py_ssize_t shape_b_len) +{ + if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) != + pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len)) { + PyErr_Format(PyExc_BufferError, "array size does not match"); + return false; + } + + return true; +} + +static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj, + Py_ssize_t r_shape[MAX_DIMENSIONS], + Py_ssize_t *r_shape_len) { - return (bool)memcmp(shape_a, shape_b, ndim * sizeof(Py_ssize_t)); + Py_ssize_t shape_len = 0; + if (PyLong_Check(shape_obj)) { + shape_len = 1; + if (((r_shape[0] = PyLong_AsLong(shape_obj)) < 1)) { + PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1"); + return false; + } + } + else if (PySequence_Check(shape_obj)) { + shape_len = PySequence_Size(shape_obj); + if (shape_len > MAX_DIMENSIONS) { + PyErr_SetString(PyExc_AttributeError, + "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS)); + return false; + } + if (shape_len < 1) { + PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension"); + return false; + } + + for (int i = 0; i < shape_len; i++) { + PyObject *ob = PySequence_GetItem(shape_obj, i); + if (!PyLong_Check(ob)) { + PyErr_Format(PyExc_TypeError, + "invalid dimension %i, expected an int, not a %.200s", + i, + Py_TYPE(ob)->tp_name); + Py_DECREF(ob); + return false; + } + + r_shape[i] = PyLong_AsLong(ob); + Py_DECREF(ob); + + if (r_shape[i] < 1) { + PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1"); + return false; + } + } + } + else { + PyErr_Format(PyExc_TypeError, + "invalid second argument argument expected a sequence " + "or an int, not a %.200s", + Py_TYPE(shape_obj)->tp_name); + } + + *r_shape_len = shape_len; + return true; } static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format) @@ -174,7 +247,7 @@ static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self) return list; } -static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg)) +static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void *UNUSED(arg)) { PyObject *list = PyList_New(self->shape_len); int i; @@ -186,6 +259,30 @@ static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg)) return list; } +static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void *UNUSED(type)) +{ + Py_ssize_t shape[MAX_DIMENSIONS]; + Py_ssize_t shape_len = 0; + + if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) { + return -1; + } + + if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) { + return -1; + } + + size_t size = shape_len * sizeof(*self->shape); + if (shape_len != self->shape_len) { + MEM_freeN(self->shape); + self->shape = MEM_mallocN(size, __func__); + } + + self->shape_len = shape_len; + memcpy(self->shape, shape, size); + return 0; +} + static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg) { Py_VISIT(self->parent); @@ -280,14 +377,13 @@ static int pygpu_buffer_ass_slice(BPyGPUBuffer *self, return err; } -#define MAX_DIMENSIONS 64 static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { PyObject *length_ob, *init = NULL; BPyGPUBuffer *buffer = NULL; Py_ssize_t shape[MAX_DIMENSIONS]; - Py_ssize_t i, shape_len = 0; + Py_ssize_t shape_len = 0; if (kwds && PyDict_Size(kwds)) { PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args"); @@ -300,49 +396,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args return NULL; } - if (PyLong_Check(length_ob)) { - shape_len = 1; - if (((shape[0] = PyLong_AsLong(length_ob)) < 1)) { - PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1"); - return NULL; - } - } - else if (PySequence_Check(length_ob)) { - shape_len = PySequence_Size(length_ob); - if (shape_len > MAX_DIMENSIONS) { - PyErr_SetString(PyExc_AttributeError, - "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS)); - return NULL; - } - if (shape_len < 1) { - PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension"); - return NULL; - } - - for (i = 0; i < shape_len; i++) { - PyObject *ob = PySequence_GetItem(length_ob, i); - if (!PyLong_Check(ob)) { - PyErr_Format(PyExc_TypeError, - "invalid dimension %i, expected an int, not a %.200s", - i, - Py_TYPE(ob)->tp_name); - Py_DECREF(ob); - return NULL; - } - shape[i] = PyLong_AsLong(ob); - Py_DECREF(ob); - - if (shape[i] < 1) { - PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1"); - return NULL; - } - } - } - else { - PyErr_Format(PyExc_TypeError, - "invalid second argument argument expected a sequence " - "or an int, not a %.200s", - Py_TYPE(length_ob)->tp_name); + if (!pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len)) { return NULL; } @@ -354,11 +408,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args return NULL; } - if (shape_len != pybuffer.ndim || - !pygpu_buffer_dimensions_compare(shape_len, shape, pybuffer.shape)) { - PyErr_Format(PyExc_TypeError, "array size does not match"); - } - else { + if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) { buffer = pygpu_buffer_make_from_data( init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf); } @@ -518,7 +568,11 @@ static PyMethodDef pygpu_buffer__tp_methods[] = { }; static PyGetSetDef pygpu_buffer_getseters[] = { - {"dimensions", (getter)pygpu_buffer_dimensions, NULL, NULL, NULL}, + {"dimensions", + (getter)pygpu_buffer_dimensions_get, + (setter)pygpu_buffer_dimensions_set, + NULL, + NULL}, {NULL, NULL, NULL, NULL, NULL}, }; @@ -625,13 +679,7 @@ static size_t pygpu_buffer_calc_size(const int format, const int shape_len, const Py_ssize_t *shape) { - size_t r_size = GPU_texture_dataformat_size(format); - - for (int i = 0; i < shape_len; i++) { - r_size *= shape[i]; - } - - return r_size; + return pygpu_buffer_dimensions_tot_elem(shape, shape_len) * GPU_texture_dataformat_size(format); } size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer) diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c new file mode 100644 index 00000000000..cedce485253 --- /dev/null +++ b/source/blender/python/gpu/gpu_py_capabilities.c @@ -0,0 +1,148 @@ +/* + * 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. + */ + +/** \file + * \ingroup bpygpu + * + * - Use ``bpygpu_`` for local API. + * - Use ``BPyGPU`` for public API. + */ + +#include <Python.h> + +#include "BLI_utildefines.h" + +#include "GPU_capabilities.h" + +#include "gpu_py_capabilities.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name Functions + * \{ */ + +static PyObject *pygpu_max_texture_size_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_texture_size()); +} + +static PyObject *pygpu_max_texture_layers_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_texture_layers()); +} + +static PyObject *pygpu_max_textures_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_textures()); +} + +static PyObject *pygpu_max_textures_vert_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_textures_vert()); +} + +static PyObject *pygpu_max_textures_geom_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_textures_geom()); +} + +static PyObject *pygpu_max_textures_frag_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_textures_frag()); +} + +static PyObject *pygpu_max_uniforms_vert_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_uniforms_vert()); +} + +static PyObject *pygpu_max_uniforms_frag_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_uniforms_frag()); +} + +static PyObject *pygpu_max_batch_indices_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_batch_indices()); +} + +static PyObject *pygpu_max_batch_vertices_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_batch_vertices()); +} + +static PyObject *pygpu_max_vertex_attribs_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_vertex_attribs()); +} + +static PyObject *pygpu_max_varying_floats_get(PyObject *UNUSED(self)) +{ + return PyLong_FromLong(GPU_max_varying_floats()); +} + +static PyObject *pygpu_extensions_get(PyObject *UNUSED(self)) +{ + int extensions_len = GPU_extensions_len(); + PyObject *ret = PyTuple_New(extensions_len); + PyObject **ob_items = ((PyTupleObject *)ret)->ob_item; + for (int i = 0; i < extensions_len; i++) { + ob_items[i] = PyUnicode_FromString(GPU_extension_get(i)); + } + + return ret; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Module + * \{ */ + +static struct PyMethodDef pygpu_capabilities__tp_methods[] = { + {"max_texture_size_get", (PyCFunction)pygpu_max_texture_size_get, METH_NOARGS, NULL}, + {"max_texture_layers_get", (PyCFunction)pygpu_max_texture_layers_get, METH_NOARGS, NULL}, + {"max_textures_get", (PyCFunction)pygpu_max_textures_get, METH_NOARGS, NULL}, + {"max_textures_vert_get", (PyCFunction)pygpu_max_textures_vert_get, METH_NOARGS, NULL}, + {"max_textures_geom_get", (PyCFunction)pygpu_max_textures_geom_get, METH_NOARGS, NULL}, + {"max_textures_frag_get", (PyCFunction)pygpu_max_textures_frag_get, METH_NOARGS, NULL}, + {"max_uniforms_vert_get", (PyCFunction)pygpu_max_uniforms_vert_get, METH_NOARGS, NULL}, + {"max_uniforms_frag_get", (PyCFunction)pygpu_max_uniforms_frag_get, METH_NOARGS, NULL}, + {"max_batch_indices_get", (PyCFunction)pygpu_max_batch_indices_get, METH_NOARGS, NULL}, + {"max_batch_vertices_get", (PyCFunction)pygpu_max_batch_vertices_get, METH_NOARGS, NULL}, + {"max_vertex_attribs_get", (PyCFunction)pygpu_max_vertex_attribs_get, METH_NOARGS, NULL}, + {"max_varying_floats_get", (PyCFunction)pygpu_max_varying_floats_get, METH_NOARGS, NULL}, + {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL}, +}; + +PyDoc_STRVAR(pygpu_capabilities__tp_doc, "This module provides access to the GPU capabilities."); +static PyModuleDef pygpu_capabilities_module_def = { + PyModuleDef_HEAD_INIT, + .m_name = "gpu.capabilities", + .m_doc = pygpu_capabilities__tp_doc, + .m_methods = pygpu_capabilities__tp_methods, +}; + +PyObject *bpygpu_capabilities_init(void) +{ + PyObject *submodule; + + submodule = PyModule_Create(&pygpu_capabilities_module_def); + + return submodule; +} + +/** \} */ diff --git a/source/blender/python/gpu/gpu_py_capabilities.h b/source/blender/python/gpu/gpu_py_capabilities.h new file mode 100644 index 00000000000..ac138dda0c9 --- /dev/null +++ b/source/blender/python/gpu/gpu_py_capabilities.h @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** \file + * \ingroup bpygpu + */ + +#pragma once + +PyObject *bpygpu_capabilities_init(void); diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c index bc393aaafa4..0efc0713538 100644 --- a/source/blender/python/gpu/gpu_py_framebuffer.c +++ b/source/blender/python/gpu/gpu_py_framebuffer.c @@ -37,6 +37,8 @@ #include "gpu_py.h" #include "gpu_py_texture.h" +#include "gpu_py.h" +#include "gpu_py_buffer.h" #include "gpu_py_framebuffer.h" /* own include */ /* -------------------------------------------------------------------- */ @@ -46,13 +48,7 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb) { if (UNLIKELY(bpygpu_fb->fb == NULL)) { - PyErr_SetString(PyExc_ReferenceError, -#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD - "GPU framebuffer was freed, no further access is valid" -#else - "GPU framebuffer: internal error" -#endif - ); + PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid"); return -1; } return 0; @@ -68,10 +64,6 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb) static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb) { - if (!fb) { - return; - } - if (GPU_is_init()) { GPU_framebuffer_free(fb); } @@ -80,6 +72,21 @@ static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb) } } +static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self) +{ + if (self->fb) { +#ifndef GPU_NO_USE_PY_REFERENCES + GPU_framebuffer_py_reference_set(self->fb, NULL); + if (!self->shared_reference) +#endif + { + pygpu_framebuffer_free_if_possible(self->fb); + } + + self->fb = NULL; + } +} + /* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */ #define GPU_PY_FRAMEBUFFER_STACK_LEN 16 @@ -316,9 +323,9 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self), return NULL; } - for (int i = 1; i <= color_attachements_len; i++) { + for (int i = 0; i < color_attachements_len; i++) { PyObject *o = PySequence_GetItem(color_attachements, i); - bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i]); + bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i + 1]); Py_DECREF(o); if (!ok) { return NULL; @@ -336,7 +343,7 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self), GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python"); GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1); - return BPyGPUFrameBuffer_CreatePyObject(fb_python); + return BPyGPUFrameBuffer_CreatePyObject(fb_python, false); } PyDoc_STRVAR(pygpu_framebuffer_is_bound_doc, @@ -450,6 +457,100 @@ static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *U return ret; } +PyDoc_STRVAR( + pygpu_framebuffer_read_color_doc, + ".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n" + "\n" + " Read a block of pixels from the frame buffer.\n" + "\n" + " :param x, y: Lower left corner of a rectangular block of pixels.\n" + " :param xsize, ysize: Dimensions of the pixel rectangle.\n" + " :type x, y, xsize, ysize: int\n" + " :param channels: Number of components to read.\n" + " :type channels: int\n" + " :param slot: The framebuffer slot to read data from.\n" + " :type slot: int\n" + " :param format: The format that describes the content of a single channel.\n" + " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n" + " :type type: str\n" + " :arg data: Optional Buffer object to fill with the pixels values.\n" + " :type data: :class:`gpu.types.Buffer`\n" + " :return: The Buffer with the read pixels.\n" + " :rtype: :class:`gpu.types.Buffer`\n"); +static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self, + PyObject *args, + PyObject *kwds) +{ + PYGPU_FRAMEBUFFER_CHECK_OBJ(self); + int x, y, w, h, channels; + uint slot; + struct PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_RGBA8}; + BPyGPUBuffer *py_buffer = NULL; + + static const char *_keywords[] = { + "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL}; + static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kwds, + &_parser, + &x, + &y, + &w, + &h, + &channels, + &slot, + PyC_ParseStringEnum, + &pygpu_dataformat, + &BPyGPU_BufferType, + &py_buffer)) { + return NULL; + } + + if (!IN_RANGE_INCL(channels, 1, 4)) { + PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4"); + return NULL; + } + + if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) { + PyErr_SetString(PyExc_ValueError, "slot overflow"); + return NULL; + } + + if (py_buffer) { + if (pygpu_dataformat.value_found != py_buffer->format) { + PyErr_SetString(PyExc_AttributeError, + "the format of the buffer is different from that specified"); + return NULL; + } + + size_t size_curr = bpygpu_Buffer_size(py_buffer); + size_t size_expected = w * h * channels * + GPU_texture_dataformat_size(pygpu_dataformat.value_found); + if (size_curr < size_expected) { + PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected"); + return NULL; + } + } + else { + py_buffer = BPyGPU_Buffer_CreatePyObject( + pygpu_dataformat.value_found, (Py_ssize_t[3]){h, w, channels}, 3, NULL); + BLI_assert(bpygpu_Buffer_size(py_buffer) == + w * h * channels * GPU_texture_dataformat_size(pygpu_dataformat.value_found)); + } + + GPU_framebuffer_read_color(self->fb, + x, + y, + w, + h, + channels, + (int)slot, + pygpu_dataformat.value_found, + py_buffer->buf.as_void); + + return (PyObject *)py_buffer; +} + #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD PyDoc_STRVAR(pygpu_framebuffer_free_doc, ".. method:: free()\n" @@ -459,15 +560,14 @@ PyDoc_STRVAR(pygpu_framebuffer_free_doc, static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self) { PYGPU_FRAMEBUFFER_CHECK_OBJ(self); - pygpu_framebuffer_free_if_possible(self->fb); - self->fb = NULL; + pygpu_framebuffer_free_safe(self); Py_RETURN_NONE; } #endif static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self) { - pygpu_framebuffer_free_if_possible(self->fb); + pygpu_framebuffer_free_safe(self); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -494,6 +594,10 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = { (PyCFunction)pygpu_framebuffer_viewport_get, METH_NOARGS, pygpu_framebuffer_viewport_get_doc}, + {"read_color", + (PyCFunction)pygpu_framebuffer_read_color, + METH_VARARGS | METH_KEYWORDS, + pygpu_framebuffer_read_color_doc}, #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD {"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc}, #endif @@ -531,13 +635,35 @@ PyTypeObject BPyGPUFrameBuffer_Type = { /** \name Public API * \{ */ -PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb) +PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference) { BPyGPUFrameBuffer *self; +#ifndef GPU_NO_USE_PY_REFERENCES + if (shared_reference) { + void **ref = GPU_framebuffer_py_reference_get(fb); + if (ref) { + /* Retrieve BPyGPUFrameBuffer reference. */ + self = (BPyGPUFrameBuffer *)POINTER_OFFSET(ref, -offsetof(BPyGPUFrameBuffer, fb)); + BLI_assert(self->fb == fb); + Py_INCREF(self); + return (PyObject *)self; + } + } +#else + UNUSED_VARS(shared_reference); +#endif + self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type); self->fb = fb; +#ifndef GPU_NO_USE_PY_REFERENCES + self->shared_reference = shared_reference; + + BLI_assert(GPU_framebuffer_py_reference_get(fb) == NULL); + GPU_framebuffer_py_reference_set(fb, (void **)&self->fb); +#endif + return (PyObject *)self; } diff --git a/source/blender/python/gpu/gpu_py_framebuffer.h b/source/blender/python/gpu/gpu_py_framebuffer.h index 7113e7c35aa..6727328518c 100644 --- a/source/blender/python/gpu/gpu_py_framebuffer.h +++ b/source/blender/python/gpu/gpu_py_framebuffer.h @@ -28,6 +28,11 @@ extern PyTypeObject BPyGPUFrameBuffer_Type; typedef struct BPyGPUFrameBuffer { PyObject_HEAD struct GPUFrameBuffer *fb; + +#ifndef GPU_NO_USE_PY_REFERENCES + bool shared_reference; +#endif } BPyGPUFrameBuffer; -PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb) ATTR_NONNULL(1); +PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb, bool shared_reference) + ATTR_NONNULL(1); diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 9e9a0b9066a..28bd24a6877 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -53,6 +53,8 @@ #include "../generic/py_capi_utils.h" #include "gpu_py.h" +#include "gpu_py_texture.h" + #include "gpu_py_offscreen.h" /* own include */ /* Define the free method to avoid breakage. */ @@ -264,6 +266,17 @@ static PyObject *pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void * return PyLong_FromLong(GPU_texture_opengl_bindcode(texture)); } +PyDoc_STRVAR(pygpu_offscreen_texture_color_doc, + "The color texture attached.\n" + "\n" + ":type: :class:`gpu.types.GPUTexture`"); +static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void *UNUSED(type)) +{ + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); + GPUTexture *texture = GPU_offscreen_color_texture(self->ofs); + return BPyGPUTexture_CreatePyObject(texture, true); +} + PyDoc_STRVAR( pygpu_offscreen_draw_view3d_doc, ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n" @@ -385,6 +398,11 @@ static PyGetSetDef pygpu_offscreen__tp_getseters[] = { (setter)NULL, pygpu_offscreen_color_texture_doc, NULL}, + {"texture_color", + (getter)pygpu_offscreen_texture_color_get, + (setter)NULL, + pygpu_offscreen_texture_color_doc, + NULL}, {"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL}, {"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c new file mode 100644 index 00000000000..e49ad18dfd8 --- /dev/null +++ b/source/blender/python/gpu/gpu_py_platform.c @@ -0,0 +1,81 @@ +/* + * 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. + */ + +/** \file + * \ingroup bpygpu + * + * - Use ``bpygpu_`` for local API. + * - Use ``BPyGPU`` for public API. + */ + +#include <Python.h> + +#include "BLI_utildefines.h" + +#include "GPU_platform.h" + +#include "gpu_py_platform.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name Functions + * \{ */ + +static PyObject *pygpu_platform_vendor_get(PyObject *UNUSED(self)) +{ + return PyUnicode_FromString(GPU_platform_vendor()); +} + +static PyObject *pygpu_platform_renderer_get(PyObject *UNUSED(self)) +{ + return PyUnicode_FromString(GPU_platform_renderer()); +} + +static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self)) +{ + return PyUnicode_FromString(GPU_platform_version()); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Module + * \{ */ + +static struct PyMethodDef pygpu_platform__tp_methods[] = { + {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, NULL}, + {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, NULL}, + {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL}, +}; + +PyDoc_STRVAR(pygpu_platform__tp_doc, "This module provides access to GPU Platform definitions."); +static PyModuleDef pygpu_platform_module_def = { + PyModuleDef_HEAD_INIT, + .m_name = "gpu.platform", + .m_doc = pygpu_platform__tp_doc, + .m_methods = pygpu_platform__tp_methods, +}; + +PyObject *bpygpu_platform_init(void) +{ + PyObject *submodule; + + submodule = PyModule_Create(&pygpu_platform_module_def); + + return submodule; +} + +/** \} */ diff --git a/source/blender/python/gpu/gpu_py_platform.h b/source/blender/python/gpu/gpu_py_platform.h new file mode 100644 index 00000000000..19e3e41fb49 --- /dev/null +++ b/source/blender/python/gpu/gpu_py_platform.h @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** \file + * \ingroup bpygpu + */ + +#pragma once + +PyObject *bpygpu_platform_init(void); diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c index 6b0fade8d1c..173c5afba56 100644 --- a/source/blender/python/gpu/gpu_py_state.c +++ b/source/blender/python/gpu/gpu_py_state.c @@ -25,11 +25,13 @@ #include <Python.h> +#include "GPU_framebuffer.h" #include "GPU_state.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" +#include "gpu_py_framebuffer.h" #include "gpu_py_state.h" /* own include */ /* -------------------------------------------------------------------- */ @@ -334,6 +336,16 @@ static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyOb Py_RETURN_NONE; } +PyDoc_STRVAR(pygpu_state_framebuffer_active_get_doc, + ".. function:: framebuffer_active_get(enable)\n" + "\n" + " Return the active framefuffer in context.\n"); +static PyObject *pygpu_state_framebuffer_active_get(PyObject *UNUSED(self)) +{ + GPUFrameBuffer *fb = GPU_framebuffer_active_get(); + return BPyGPUFrameBuffer_CreatePyObject(fb, true); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -396,6 +408,10 @@ static struct PyMethodDef pygpu_state__tp_methods[] = { (PyCFunction)pygpu_state_program_point_size_set, METH_O, pygpu_state_program_point_size_set_doc}, + {"active_framebuffer_get", + (PyCFunction)pygpu_state_framebuffer_active_get, + METH_NOARGS, + pygpu_state_framebuffer_active_get_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c index 1ae65c1dd11..2181c09b537 100644 --- a/source/blender/python/gpu/gpu_py_texture.c +++ b/source/blender/python/gpu/gpu_py_texture.c @@ -247,7 +247,7 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg return NULL; } - return BPyGPUTexture_CreatePyObject(tex); + return BPyGPUTexture_CreatePyObject(tex, false); } PyDoc_STRVAR(pygpu_texture_width_doc, "Width of the texture.\n\n:type: `int`"); @@ -349,11 +349,12 @@ PyDoc_STRVAR(pygpu_texture_read_doc, static PyObject *pygpu_texture_read(BPyGPUTexture *self) { BPYGPU_TEXTURE_CHECK_OBJ(self); + eGPUTextureFormat tex_format = GPU_texture_format(self->tex); /* #GPU_texture_read is restricted in combining 'data_format' with 'tex_format'. * So choose data_format here. */ eGPUDataFormat best_data_format; - switch (GPU_texture_format(self->tex)) { + switch (tex_format) { case GPU_DEPTH_COMPONENT24: case GPU_DEPTH_COMPONENT16: case GPU_DEPTH_COMPONENT32F: @@ -389,8 +390,12 @@ static PyObject *pygpu_texture_read(BPyGPUTexture *self) } void *buf = GPU_texture_read(self->tex, best_data_format, 0); - const Py_ssize_t shape[2] = {GPU_texture_height(self->tex), GPU_texture_width(self->tex)}; - return (PyObject *)BPyGPU_Buffer_CreatePyObject(best_data_format, shape, ARRAY_SIZE(shape), buf); + const Py_ssize_t shape[3] = {GPU_texture_height(self->tex), + GPU_texture_width(self->tex), + GPU_texture_component_len(tex_format)}; + + int shape_len = (shape[2] == 1) ? 2 : 3; + return (PyObject *)BPyGPU_Buffer_CreatePyObject(best_data_format, shape, shape_len, buf); } #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD @@ -412,6 +417,9 @@ static PyObject *pygpu_texture_free(BPyGPUTexture *self) static void BPyGPUTexture__tp_dealloc(BPyGPUTexture *self) { if (self->tex) { +#ifndef GPU_NO_USE_PY_REFERENCES + GPU_texture_py_reference_set(self->tex, NULL); +#endif GPU_texture_free(self->tex); } Py_TYPE(self)->tp_free((PyObject *)self); @@ -535,10 +543,7 @@ static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg) BKE_imageuser_default(&iuser); GPUTexture *tex = BKE_image_get_gpu_texture(ima, &iuser, NULL); - /* Increase the texture reference count. */ - GPU_texture_ref(tex); - - return BPyGPUTexture_CreatePyObject(tex); + return BPyGPUTexture_CreatePyObject(tex, true); } static struct PyMethodDef pygpu_texture__m_methods[] = { @@ -595,13 +600,33 @@ PyObject *bpygpu_texture_init(void) /** \name Public API * \{ */ -PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex) +PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex, bool shared_reference) { BPyGPUTexture *self; + if (shared_reference) { +#ifndef GPU_NO_USE_PY_REFERENCES + void **ref = GPU_texture_py_reference_get(tex); + if (ref) { + /* Retrieve BPyGPUTexture reference. */ + self = (BPyGPUTexture *)POINTER_OFFSET(ref, -offsetof(BPyGPUTexture, tex)); + BLI_assert(self->tex == tex); + Py_INCREF(self); + return (PyObject *)self; + } +#endif + + GPU_texture_ref(tex); + } + self = PyObject_New(BPyGPUTexture, &BPyGPUTexture_Type); self->tex = tex; +#ifndef GPU_NO_USE_PY_REFERENCES + BLI_assert(GPU_texture_py_reference_get(tex) == NULL); + GPU_texture_py_reference_set(tex, (void **)&self->tex); +#endif + return (PyObject *)self; } diff --git a/source/blender/python/gpu/gpu_py_texture.h b/source/blender/python/gpu/gpu_py_texture.h index 5130273f971..3eaaa3411bd 100644 --- a/source/blender/python/gpu/gpu_py_texture.h +++ b/source/blender/python/gpu/gpu_py_texture.h @@ -33,4 +33,5 @@ typedef struct BPyGPUTexture { int bpygpu_ParseTexture(PyObject *o, void *p); PyObject *bpygpu_texture_init(void); -PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex) ATTR_NONNULL(1); +PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex, bool shared_reference) + ATTR_NONNULL(1); diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 9ac8d4d9f47..2be2105d327 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -79,6 +79,7 @@ set(SRC bpy_rna_driver.c bpy_rna_gizmo.c bpy_rna_id_collection.c + bpy_rna_operator.c bpy_rna_types_capi.c bpy_rna_ui.c bpy_traceback.c @@ -118,6 +119,7 @@ set(SRC bpy_rna_driver.h bpy_rna_gizmo.h bpy_rna_id_collection.h + bpy_rna_operator.h bpy_rna_types_capi.h bpy_rna_ui.h bpy_traceback.h diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index 8ecee9b3f2e..a0b543097e6 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -74,6 +74,7 @@ static PyStructSequence_Field app_cb_info_fields[] = { {"version_update", "on ending the versioning code"}, {"load_factory_preferences_post", "on loading factory preferences (after)"}, {"load_factory_startup_post", "on loading factory startup (after)"}, + {"xr_session_start_pre", "on starting an xr session (before)"}, /* sets the permanent tag */ #define APP_CB_OTHER_FIELDS 1 diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 5f31e0bb74d..4144063cf5c 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -167,6 +167,14 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate) } } +static void bpy_context_end(bContext *C) +{ + if (UNLIKELY(C == NULL)) { + return; + } + CTX_wm_operator_poll_msg_clear(C); +} + /** * Use for `CTX_*_set(..)` functions need to set values which are later read back as expected. * In this case we don't want the Python context to override the values as it causes problems @@ -524,6 +532,9 @@ void BPY_python_end(void) /* finalizing, no need to grab the state, except when we are a module */ gilstate = PyGILState_Ensure(); + /* Clear Python values in the context so freeing the context after Python exits doesn't crash. */ + bpy_context_end(BPY_context_get()); + /* Decrement user counts of all callback functions. */ BPY_rna_props_clear_all(); diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 94ad6a8ef78..4a5e2552598 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -244,12 +244,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) } if (WM_operator_poll_context((bContext *)C, ot, context) == false) { - const char *msg = CTX_wm_operator_poll_msg_get(C); + bool msg_free = false; + const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free); PyErr_Format(PyExc_RuntimeError, "Operator bpy.ops.%.200s.poll() %.200s", opname, msg ? msg : "failed, context is incorrect"); - CTX_wm_operator_poll_msg_set(C, NULL); /* better set to NULL else it could be used again */ + CTX_wm_operator_poll_msg_clear(C); + if (msg_free) { + MEM_freeN((void *)msg); + } error_val = -1; } else { diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 1711637458a..fb1cb823964 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -3562,7 +3562,7 @@ PyDoc_STRVAR(pyrna_struct_keys_doc, " dictionary function of the same name).\n" "\n" " :return: custom property keys.\n" - " :rtype: list of strings\n" + " :rtype: :class:`idprop.type.IDPropertyGroupViewKeys`\n" "\n" BPY_DOC_ID_PROP_TYPE_NOTE); static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self) { @@ -3573,13 +3573,9 @@ static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self) return NULL; } + /* `group` may be NULL. */ group = RNA_struct_idprops(&self->ptr, 0); - - if (group == NULL) { - return PyList_New(0); - } - - return BPy_Wrap_GetKeys(group); + return BPy_Wrap_GetKeys_View_WithID(self->ptr.owner_id, group); } PyDoc_STRVAR(pyrna_struct_items_doc, @@ -3589,7 +3585,7 @@ PyDoc_STRVAR(pyrna_struct_items_doc, " dictionary function of the same name).\n" "\n" " :return: custom property key, value pairs.\n" - " :rtype: list of key, value tuples\n" + " :rtype: :class:`idprop.type.IDPropertyGroupViewItems`\n" "\n" BPY_DOC_ID_PROP_TYPE_NOTE); static PyObject *pyrna_struct_items(BPy_PropertyRNA *self) { @@ -3600,13 +3596,9 @@ static PyObject *pyrna_struct_items(BPy_PropertyRNA *self) return NULL; } + /* `group` may be NULL. */ group = RNA_struct_idprops(&self->ptr, 0); - - if (group == NULL) { - return PyList_New(0); - } - - return BPy_Wrap_GetItems(self->ptr.owner_id, group); + return BPy_Wrap_GetItems_View_WithID(self->ptr.owner_id, group); } PyDoc_STRVAR(pyrna_struct_values_doc, @@ -3616,7 +3608,7 @@ PyDoc_STRVAR(pyrna_struct_values_doc, " dictionary function of the same name).\n" "\n" " :return: custom property values.\n" - " :rtype: list\n" + " :rtype: :class:`idprop.type.IDPropertyGroupViewValues`\n" "\n" BPY_DOC_ID_PROP_TYPE_NOTE); static PyObject *pyrna_struct_values(BPy_PropertyRNA *self) { @@ -3628,13 +3620,9 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self) return NULL; } + /* `group` may be NULL. */ group = RNA_struct_idprops(&self->ptr, 0); - - if (group == NULL) { - return PyList_New(0); - } - - return BPy_Wrap_GetValues(self->ptr.owner_id, group); + return BPy_Wrap_GetValues_View_WithID(self->ptr.owner_id, group); } PyDoc_STRVAR(pyrna_struct_is_property_set_doc, diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c new file mode 100644 index 00000000000..6e0db3eca49 --- /dev/null +++ b/source/blender/python/intern/bpy_rna_operator.c @@ -0,0 +1,150 @@ +/* + * 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. + */ + +/** \file + * \ingroup pythonintern + * + * This file extends `bpy.types.Operator` with C/Python API methods and attributes. + */ + +#include <Python.h> + +#include "BLI_string.h" + +#include "BKE_context.h" + +#include "../generic/python_utildefines.h" + +#include "BPY_extern.h" +#include "bpy_capi_utils.h" + +/* -------------------------------------------------------------------- */ +/** \name Operator `poll_message_set` Method + * \{ */ + +static char *pyop_poll_message_get_fn(bContext *UNUSED(C), void *user_data) +{ + PyGILState_STATE gilstate = PyGILState_Ensure(); + + PyObject *py_args = user_data; + PyObject *py_func_or_msg = PyTuple_GET_ITEM(py_args, 0); + + if (PyUnicode_Check(py_func_or_msg)) { + return BLI_strdup(PyUnicode_AsUTF8(py_func_or_msg)); + } + + PyObject *py_args_after_first = PyTuple_GetSlice(py_args, 1, PY_SSIZE_T_MAX); + PyObject *py_msg = PyObject_CallObject(py_func_or_msg, py_args_after_first); + Py_DECREF(py_args_after_first); + + char *msg = NULL; + bool error = false; + + /* NULL for no string. */ + if (py_msg == NULL) { + error = true; + } + else { + if (py_msg == Py_None) { + /* pass */ + } + else if (PyUnicode_Check(py_msg)) { + msg = BLI_strdup(PyUnicode_AsUTF8(py_msg)); + } + else { + PyErr_Format(PyExc_TypeError, + "poll_message_set(function, ...): expected string or None, got %.200s", + Py_TYPE(py_msg)->tp_name); + error = true; + } + Py_DECREF(py_msg); + } + + if (error) { + PyErr_Print(); + PyErr_Clear(); + } + + PyGILState_Release(gilstate); + return msg; +} + +static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data) +{ + /* Handles the GIL. */ + BPY_DECREF(user_data); +} + +PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc, + ".. method:: poll_message_set(message, ...)\n" + "\n" + " Set the message to show in the tool-tip when poll fails.\n" + "\n" + " When message is callable, " + "additional user defined positional arguments are passed to the message function.\n" + "\n" + " :param message: The message or a function that returns the message.\n" + " :type message: string or a callable that returns a string or None.\n"); + +static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args) +{ + const ssize_t args_len = PyTuple_GET_SIZE(args); + if (args_len == 0) { + PyErr_SetString(PyExc_ValueError, + "poll_message_set(message, ...): requires a message argument"); + return NULL; + } + + PyObject *py_func_or_msg = PyTuple_GET_ITEM(args, 0); + + if (PyUnicode_Check(py_func_or_msg)) { + if (args_len > 1) { + PyErr_SetString(PyExc_ValueError, + "poll_message_set(message): does not support additional arguments"); + return NULL; + } + } + else if (PyCallable_Check(py_func_or_msg)) { + /* pass */ + } + else { + PyErr_Format(PyExc_TypeError, + "poll_message_set(message, ...): " + "expected at least 1 string or callable argument, got %.200s", + Py_TYPE(py_func_or_msg)->tp_name); + return NULL; + } + + bContext *C = BPY_context_get(); + struct bContextPollMsgDyn_Params params = { + .get_fn = pyop_poll_message_get_fn, + .free_fn = pyop_poll_message_free_fn, + .user_data = Py_INCREF_RET(args), + }; + + CTX_wm_operator_poll_msg_set_dynamic(C, ¶ms); + + Py_RETURN_NONE; +} + +PyMethodDef BPY_rna_operator_poll_message_set_method_def = { + "poll_message_set", + (PyCFunction)BPY_rna_operator_poll_message_set, + METH_VARARGS | METH_STATIC, + BPY_rna_operator_poll_message_set_doc, +}; + +/** \} */ diff --git a/source/blender/python/intern/bpy_rna_operator.h b/source/blender/python/intern/bpy_rna_operator.h new file mode 100644 index 00000000000..8040d8a492a --- /dev/null +++ b/source/blender/python/intern/bpy_rna_operator.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +/** \file + * \ingroup pythonintern + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +extern PyMethodDef BPY_rna_operator_poll_message_set_method_def; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c index 9b15e84663d..2f6e197d1e2 100644 --- a/source/blender/python/intern/bpy_rna_types_capi.c +++ b/source/blender/python/intern/bpy_rna_types_capi.c @@ -41,6 +41,8 @@ #include "bpy_rna_types_capi.h" #include "bpy_rna_ui.h" +#include "bpy_rna_operator.h" + #include "../generic/py_capi_utils.h" #include "RNA_access.h" @@ -87,6 +89,17 @@ static struct PyMethodDef pyrna_uilayout_methods[] = { /** \} */ /* -------------------------------------------------------------------- */ +/** \name Operator + * \{ */ + +static struct PyMethodDef pyrna_operator_methods[] = { + {NULL, NULL, 0, NULL}, /* #BPY_rna_operator_poll_message_set */ + {NULL, NULL, 0, NULL}, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Window Manager Clipboard Property * * Avoid using the RNA API because this value may change between checking its length @@ -228,6 +241,11 @@ void BPY_rna_types_extend_capi(void) /* Space */ pyrna_struct_type_extend_capi(&RNA_Space, pyrna_space_methods, NULL); + /* wmOperator */ + ARRAY_SET_ITEMS(pyrna_operator_methods, BPY_rna_operator_poll_message_set_method_def); + BLI_assert(ARRAY_SIZE(pyrna_operator_methods) == 2); + pyrna_struct_type_extend_capi(&RNA_Operator, pyrna_operator_methods, NULL); + /* WindowManager */ pyrna_struct_type_extend_capi( &RNA_WindowManager, pyrna_windowmanager_methods, pyrna_windowmanager_getset); diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 3791a6c2d29..16bf7120606 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -152,7 +152,7 @@ int mathutils_array_parse( return -1; } - memcpy(array, ((BaseMathObject *)value)->data, size * sizeof(float)); + memcpy(array, ((const BaseMathObject *)value)->data, size * sizeof(float)); } else #endif @@ -235,7 +235,7 @@ int mathutils_array_parse_alloc(float **array, } *array = PyMem_Malloc(size * sizeof(float)); - memcpy(*array, ((BaseMathObject *)value)->data, size * sizeof(float)); + memcpy(*array, ((const BaseMathObject *)value)->data, size * sizeof(float)); return size; } @@ -471,7 +471,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error return -1; } - eulO_to_mat3(rmat, ((EulerObject *)value)->eul, ((EulerObject *)value)->order); + eulO_to_mat3(rmat, ((const EulerObject *)value)->eul, ((const EulerObject *)value)->order); return 0; } if (QuaternionObject_Check(value)) { @@ -480,7 +480,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error } float tquat[4]; - normalize_qt_qt(tquat, ((QuaternionObject *)value)->quat); + normalize_qt_qt(tquat, ((const QuaternionObject *)value)->quat); quat_to_mat3(rmat, tquat); return 0; } @@ -530,8 +530,8 @@ int EXPP_FloatsAreEqual(float af, float bf, int maxDiff) { /* solid, fast routine across all platforms * with constant time behavior */ - const int ai = *(int *)(&af); - const int bi = *(int *)(&bf); + const int ai = *(const int *)(&af); + const int bi = *(const int *)(&bf); const int test = SIGNMASK(ai ^ bi); int diff, v1, v2; diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c index f2a8af18073..b6a0183d04e 100644 --- a/source/blender/python/mathutils/mathutils_Euler.c +++ b/source/blender/python/mathutils/mathutils_Euler.c @@ -88,7 +88,7 @@ short euler_order_from_string(const char *str, const char *error_prefix) # define MAKE_ID3(a, b, c) (((a) << 24) | ((b) << 16) | ((c) << 8)) #endif - switch (*((PY_INT32_T *)str)) { + switch (*((const PY_INT32_T *)str)) { case MAKE_ID3('X', 'Y', 'Z'): return EULER_ORDER_XYZ; case MAKE_ID3('X', 'Z', 'Y'): diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 161d2f41592..c312012b24c 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -187,7 +187,7 @@ static int mathutils_matrix_col_get(BaseMathObject *bmo, int col) } /* for 'translation' size will always be '3' even on 4x4 vec */ - num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size); + num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size); for (row = 0; row < num_row; row++) { bmo->data[row] = MATRIX_ITEM(self, row, col); @@ -210,7 +210,7 @@ static int mathutils_matrix_col_set(BaseMathObject *bmo, int col) } /* for 'translation' size will always be '3' even on 4x4 vec */ - num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size); + num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size); for (row = 0; row < num_row; row++) { MATRIX_ITEM(self, row, col) = bmo->data[row]; @@ -969,6 +969,104 @@ static PyObject *C_Matrix_Shear(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } +PyDoc_STRVAR( + C_Matrix_LocRotScale_doc, + ".. classmethod:: LocRotScale(location, rotation, scale)\n" + "\n" + " Create a matrix combining translation, rotation and scale,\n" + " acting as the inverse of the decompose() method.\n" + "\n" + " Any of the inputs may be replaced with None if not needed.\n" + "\n" + " :arg location: The translation component.\n" + " :type location: :class:`Vector` or None\n" + " :arg rotation: The rotation component.\n" + " :type rotation: 3x3 :class:`Matrix`, :class:`Quaternion`, :class:`Euler` or None\n" + " :arg scale: The scale component.\n" + " :type scale: :class:`Vector` or None\n" + " :return: Combined transformation matrix. \n" + " :rtype: 4x4 :class:`Matrix`\n"); +static PyObject *C_Matrix_LocRotScale(PyObject *cls, PyObject *args) +{ + PyObject *loc_obj, *rot_obj, *scale_obj; + float mat[4][4], loc[3]; + + if (!PyArg_ParseTuple(args, "OOO:Matrix.LocRotScale", &loc_obj, &rot_obj, &scale_obj)) { + return NULL; + } + + /* Decode location. */ + if (loc_obj == Py_None) { + zero_v3(loc); + } + else if (mathutils_array_parse( + loc, 3, 3, loc_obj, "Matrix.LocRotScale(), invalid location argument") == -1) { + return NULL; + } + + /* Decode rotation. */ + if (rot_obj == Py_None) { + unit_m4(mat); + } + else if (QuaternionObject_Check(rot_obj)) { + QuaternionObject *quat_obj = (QuaternionObject *)rot_obj; + + if (BaseMath_ReadCallback(quat_obj) == -1) { + return NULL; + } + + quat_to_mat4(mat, quat_obj->quat); + } + else if (EulerObject_Check(rot_obj)) { + EulerObject *eul_obj = (EulerObject *)rot_obj; + + if (BaseMath_ReadCallback(eul_obj) == -1) { + return NULL; + } + + eulO_to_mat4(mat, eul_obj->eul, eul_obj->order); + } + else if (MatrixObject_Check(rot_obj)) { + MatrixObject *mat_obj = (MatrixObject *)rot_obj; + + if (BaseMath_ReadCallback(mat_obj) == -1) { + return NULL; + } + + if (mat_obj->num_col == 3 && mat_obj->num_row == 3) { + copy_m4_m3(mat, (const float(*)[3])mat_obj->matrix); + } + else { + PyErr_SetString(PyExc_ValueError, + "Matrix.LocRotScale(): " + "inappropriate rotation matrix size - expects 3x3 matrix"); + return NULL; + } + } + else { + PyErr_SetString(PyExc_ValueError, + "Matrix.LocRotScale(): " + "rotation argument must be Matrix, Quaternion, Euler or None"); + return NULL; + } + + /* Decode scale. */ + if (scale_obj != Py_None) { + float scale[3]; + + if (mathutils_array_parse( + scale, 3, 3, scale_obj, "Matrix.LocRotScale(), invalid scale argument") == -1) { + return NULL; + } + + rescale_m4(mat, scale); + } + + copy_v3_v3(mat[3], loc); + + return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls); +} + void matrix_as_3x3(float mat[3][3], MatrixObject *self) { copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0)); @@ -1029,7 +1127,7 @@ static float matrix_determinant_internal(const MatrixObject *self) MATRIX_ITEM(self, 2, 2)); } - return determinant_m4((float(*)[4])self->matrix); + return determinant_m4((const float(*)[4])self->matrix); } static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim) @@ -1037,15 +1135,15 @@ static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort /* calculate the classical adjoint */ switch (dim) { case 2: { - adjoint_m2_m2((float(*)[2])mat_dst, (float(*)[2])mat_src); + adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src); break; } case 3: { - adjoint_m3_m3((float(*)[3])mat_dst, (float(*)[3])mat_src); + adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src); break; } case 4: { - adjoint_m4_m4((float(*)[4])mat_dst, (float(*)[4])mat_src); + adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src); break; } default: @@ -1115,7 +1213,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat) float(*mat)[2] = (float(*)[2])in_mat; if (in_mat != self->matrix) { - copy_m2_m2(mat, (float(*)[2])self->matrix); + copy_m2_m2(mat, (const float(*)[2])self->matrix); } mat[0][0] += eps; mat[1][1] += eps; @@ -1130,7 +1228,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat) float(*mat)[3] = (float(*)[3])in_mat; if (in_mat != self->matrix) { - copy_m3_m3(mat, (float(*)[3])self->matrix); + copy_m3_m3(mat, (const float(*)[3])self->matrix); } mat[0][0] += eps; mat[1][1] += eps; @@ -1146,7 +1244,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat) float(*mat)[4] = (float(*)[4])in_mat; if (in_mat != self->matrix) { - copy_m4_m4(mat, (float(*)[4])self->matrix); + copy_m4_m4(mat, (const float(*)[4])self->matrix); } mat[0][0] += eps; mat[1][1] += eps; @@ -1194,7 +1292,7 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self) mat3_to_quat(quat, (float(*)[3])self->matrix); } else { - mat4_to_quat(quat, (float(*)[4])self->matrix); + mat4_to_quat(quat, (const float(*)[4])self->matrix); } return Quaternion_CreatePyObject(quat, NULL); @@ -1243,10 +1341,10 @@ static PyObject *Matrix_to_euler(MatrixObject *self, PyObject *args) /*must be 3-4 cols, 3-4 rows, square matrix */ if (self->num_row == 3 && self->num_col == 3) { - copy_m3_m3(mat, (float(*)[3])self->matrix); + copy_m3_m3(mat, (const float(*)[3])self->matrix); } else if (self->num_row == 4 && self->num_col == 4) { - copy_m3_m4(mat, (float(*)[4])self->matrix); + copy_m3_m4(mat, (const float(*)[4])self->matrix); } else { PyErr_SetString(PyExc_ValueError, @@ -1321,7 +1419,7 @@ static PyObject *Matrix_resize_4x4(MatrixObject *self) memcpy(mat[col], MATRIX_COL_PTR(self, col), self->num_row * sizeof(float)); } - copy_m4_m4((float(*)[4])self->matrix, (float(*)[4])mat); + copy_m4_m4((float(*)[4])self->matrix, (const float(*)[4])mat); self->num_col = 4; self->num_row = 4; @@ -1479,7 +1577,7 @@ static bool matrix_invert_args_check(const MatrixObject *self, PyObject *args, b return true; case 1: if (check_type) { - const MatrixObject *fallback = (MatrixObject *)PyTuple_GET_ITEM(args, 0); + const MatrixObject *fallback = (const MatrixObject *)PyTuple_GET_ITEM(args, 0); if (!MatrixObject_Check(fallback)) { PyErr_SetString(PyExc_TypeError, "Matrix.invert: " @@ -1797,7 +1895,7 @@ static PyObject *Matrix_decompose(MatrixObject *self) return NULL; } - mat4_to_loc_rot_size(loc, rot, size, (float(*)[4])self->matrix); + mat4_to_loc_rot_size(loc, rot, size, (const float(*)[4])self->matrix); mat3_to_quat(quat, rot); ret = PyTuple_New(3); @@ -2059,7 +2157,7 @@ static PyObject *Matrix_identity(MatrixObject *self) static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix) { - return Matrix_CreatePyObject((float *)matrix, self->num_col, self->num_row, Py_TYPE(self)); + return Matrix_CreatePyObject((const float *)matrix, self->num_col, self->num_row, Py_TYPE(self)); } PyDoc_STRVAR(Matrix_copy_doc, @@ -2960,10 +3058,10 @@ static PyObject *Matrix_is_negative_get(MatrixObject *self, void *UNUSED(closure /*must be 3-4 cols, 3-4 rows, square matrix*/ if (self->num_row == 4 && self->num_col == 4) { - return PyBool_FromLong(is_negative_m4((float(*)[4])self->matrix)); + return PyBool_FromLong(is_negative_m4((const float(*)[4])self->matrix)); } if (self->num_row == 3 && self->num_col == 3) { - return PyBool_FromLong(is_negative_m3((float(*)[3])self->matrix)); + return PyBool_FromLong(is_negative_m3((const float(*)[3])self->matrix)); } PyErr_SetString(PyExc_AttributeError, @@ -2982,10 +3080,10 @@ static PyObject *Matrix_is_orthogonal_get(MatrixObject *self, void *UNUSED(closu /*must be 3-4 cols, 3-4 rows, square matrix*/ if (self->num_row == 4 && self->num_col == 4) { - return PyBool_FromLong(is_orthonormal_m4((float(*)[4])self->matrix)); + return PyBool_FromLong(is_orthonormal_m4((const float(*)[4])self->matrix)); } if (self->num_row == 3 && self->num_col == 3) { - return PyBool_FromLong(is_orthonormal_m3((float(*)[3])self->matrix)); + return PyBool_FromLong(is_orthonormal_m3((const float(*)[3])self->matrix)); } PyErr_SetString(PyExc_AttributeError, @@ -3005,10 +3103,10 @@ static PyObject *Matrix_is_orthogonal_axis_vectors_get(MatrixObject *self, void /*must be 3-4 cols, 3-4 rows, square matrix*/ if (self->num_row == 4 && self->num_col == 4) { - return PyBool_FromLong(is_orthogonal_m4((float(*)[4])self->matrix)); + return PyBool_FromLong(is_orthogonal_m4((const float(*)[4])self->matrix)); } if (self->num_row == 3 && self->num_col == 3) { - return PyBool_FromLong(is_orthogonal_m3((float(*)[3])self->matrix)); + return PyBool_FromLong(is_orthogonal_m3((const float(*)[3])self->matrix)); } PyErr_SetString(PyExc_AttributeError, @@ -3111,6 +3209,10 @@ static struct PyMethodDef Matrix_methods[] = { (PyCFunction)C_Matrix_OrthoProjection, METH_VARARGS | METH_CLASS, C_Matrix_OrthoProjection_doc}, + {"LocRotScale", + (PyCFunction)C_Matrix_LocRotScale, + METH_VARARGS | METH_CLASS, + C_Matrix_LocRotScale_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c index 6abb66899d5..bb6a7720c44 100644 --- a/source/blender/python/mathutils/mathutils_interpolate.c +++ b/source/blender/python/mathutils/mathutils_interpolate.c @@ -54,24 +54,15 @@ static PyObject *M_Interpolate_poly_3d_calc(PyObject *UNUSED(self), PyObject *ar PyObject *point, *veclist, *ret; int i; - if (!PyArg_ParseTuple(args, "OO!:poly_3d_calc", &veclist, &vector_Type, &point)) { + if (!PyArg_ParseTuple(args, "OO:poly_3d_calc", &veclist, &point)) { return NULL; } - if (BaseMath_ReadCallback((VectorObject *)point) == -1) { + if (mathutils_array_parse( + fp, 2, 3 | MU_ARRAY_ZERO, point, "pt must be a 2-3 dimensional vector") == -1) { return NULL; } - fp[0] = ((VectorObject *)point)->vec[0]; - fp[1] = ((VectorObject *)point)->vec[1]; - if (((VectorObject *)point)->size > 2) { - fp[2] = ((VectorObject *)point)->vec[2]; - } - else { - /* if its a 2d vector then set the z to be zero */ - fp[2] = 0.0f; - } - len = mathutils_array_parse_alloc_v(((float **)&vecs), 3, veclist, __func__); if (len == -1) { return NULL; diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c index fe8f9ec0334..d54dbc9ab05 100644 --- a/source/blender/python/mathutils/mathutils_kdtree.c +++ b/source/blender/python/mathutils/mathutils_kdtree.c @@ -52,7 +52,7 @@ static void kdtree_nearest_to_py_tuple(const KDTreeNearest_3d *nearest, PyObject BLI_assert(PyTuple_GET_SIZE(py_retval) == 3); PyTuple_SET_ITEMS(py_retval, - Vector_CreatePyObject((float *)nearest->co, 3, NULL), + Vector_CreatePyObject(nearest->co, 3, NULL), PyLong_FromLong(nearest->index), PyFloat_FromDouble(nearest->dist)); } diff --git a/source/blender/render/intern/zbuf.c b/source/blender/render/intern/zbuf.c index 33af3bbaf29..242c8a199fb 100644 --- a/source/blender/render/intern/zbuf.c +++ b/source/blender/render/intern/zbuf.c @@ -175,7 +175,7 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2]) /* Functions */ /*-----------------------------------------------------------*/ -/* Scanconvert for strand triangles, calls func for each x, y coordinate +/* Scan-convert for strand triangles, calls function for each x, y coordinate * and gives UV barycentrics and z. */ void zspan_scanconvert(ZSpan *zspan, diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index 669c55e1f4c..b4068bac1d0 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -27,39 +27,65 @@ extern "C" { #endif +#include "BLI_ghash.h" + struct Editing; struct Sequence; +struct GSet; +struct GSetIterator; -typedef struct SeqIterator { - struct Sequence **array; - int tot, cur; +#define SEQ_ITERATOR_FOREACH(var, collection) \ + for (SeqIterator iter = {{{NULL}}}; \ + SEQ_iterator_ensure(collection, &iter, &var) && var != NULL; \ + var = SEQ_iterator_yield(&iter)) - struct Sequence *seq; - int valid; -} SeqIterator; - -#define SEQ_ALL_BEGIN(ed, _seq) \ +#define SEQ_ALL_BEGIN(ed, var) \ { \ - SeqIterator iter_macro; \ - for (SEQ_iterator_begin(ed, &iter_macro, false); iter_macro.valid; \ - SEQ_iterator_next(&iter_macro)) { \ - _seq = iter_macro.seq; + if (ed != NULL) { \ + SeqCollection *all_strips = SEQ_query_all_strips_recursive(&ed->seqbase); \ + GSetIterator gsi; \ + GSET_ITER (gsi, all_strips->set) { \ + var = (Sequence *)(BLI_gsetIterator_getKey(&gsi)); #define SEQ_ALL_END \ } \ - SEQ_iterator_end(&iter_macro); \ + SEQ_collection_free(all_strips); \ + } \ } \ ((void)0) -void SEQ_iterator_begin(struct Editing *ed, SeqIterator *iter, const bool use_current_sequences); -void SEQ_iterator_next(SeqIterator *iter); -void SEQ_iterator_end(SeqIterator *iter); -int SEQ_iterator_seqbase_recursive_apply(struct ListBase *seqbase, - int (*apply_fn)(struct Sequence *seq, void *), - void *arg); -int SEQ_iterator_recursive_apply(struct Sequence *seq, - int (*apply_fn)(struct Sequence *, void *), - void *arg); +typedef struct SeqCollection { + struct SeqCollection *next, *prev; + struct GSet *set; +} SeqCollection; + +typedef struct SeqIterator { + GSetIterator gsi; + SeqCollection *collection; + bool iterator_initialized; +} SeqIterator; + +bool SEQ_iterator_ensure(SeqCollection *collection, + SeqIterator *iterator, + struct Sequence **r_seq); +struct Sequence *SEQ_iterator_yield(SeqIterator *iterator); + +SeqCollection *SEQ_collection_create(void); +bool SEQ_collection_append_strip(struct Sequence *seq, SeqCollection *data); +void SEQ_collection_free(SeqCollection *collection); +void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src); +void SEQ_collection_expand(struct ListBase *seqbase, + SeqCollection *collection, + void query_func(struct Sequence *seq_reference, + struct ListBase *seqbase, + SeqCollection *collection)); +SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference, + struct ListBase *seqbase, + void seq_query_func(struct Sequence *seq_reference, + struct ListBase *seqbase, + SeqCollection *collection)); +SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase); +SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h index 45f53a64688..52fac5d7d0e 100644 --- a/source/blender/sequencer/SEQ_utils.h +++ b/source/blender/sequencer/SEQ_utils.h @@ -54,7 +54,12 @@ void SEQ_set_scale_to_fit(const struct Sequence *seq, const int preview_width, const int preview_height, const eSeqImageFitMethod fit_method); - +int SEQ_seqbase_recursive_apply(struct ListBase *seqbase, + int (*apply_fn)(struct Sequence *seq, void *), + void *arg); +int SEQ_recursive_apply(struct Sequence *seq, + int (*apply_fn)(struct Sequence *, void *), + void *arg); #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 278320d873e..d41a2c19d55 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -3033,10 +3033,9 @@ static ImBuf *do_adjustment_impl(const SeqRenderData *context, Sequence *seq, fl i = seq_render_give_ibuf_seqbase(context, timeline_frame, seq->machine - 1, seqbasep); } - /* found nothing? so let's work the way up the metastrip stack, so + /* Found nothing? so let's work the way up the meta-strip stack, so * that it is possible to group a bunch of adjustment strips into - * a metastrip and have that work on everything below the metastrip - */ + * a meta-strip and have that work on everything below the meta-strip. */ if (!i) { Sequence *meta; diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index f99667dea04..e49594f91f5 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -31,138 +31,202 @@ #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BKE_scene.h" #include "SEQ_iterator.h" -/* ************************* iterator ************************** */ -/* *************** (replaces old WHILE_SEQ) ********************* */ -/* **************** use now SEQ_ALL_BEGIN () SEQ_ALL_END ***************** */ +/* -------------------------------------------------------------------- */ +/** \Iterator API + * \{ */ -/* sequence strip iterator: - * - builds a full array, recursively into meta strips +/** + * Utility function for SEQ_ITERATOR_FOREACH macro. + * Ensure, that iterator is initialized. During initialization return pointer to collection element + * and step gset iterator. When this function is called after iterator has been initialized, it + * will do nothing and return true. + * + * \param collection: collection to iterate + * \param iterator: iterator to be initialized + * \param r_seq: pointer to Sequence pointer + * + * \return false when iterator can not be initialized, true otherwise */ - -static void seq_count(ListBase *seqbase, int *tot) +bool SEQ_iterator_ensure(SeqCollection *collection, SeqIterator *iterator, Sequence **r_seq) { - Sequence *seq; - - for (seq = seqbase->first; seq; seq = seq->next) { - (*tot)++; - - if (seq->seqbase.first) { - seq_count(&seq->seqbase, tot); - } + if (iterator->iterator_initialized) { + return true; } -} -static void seq_build_array(ListBase *seqbase, Sequence ***array, int depth) -{ - Sequence *seq; + if (BLI_gset_len(collection->set) == 0) { + return false; + } - for (seq = seqbase->first; seq; seq = seq->next) { - seq->depth = depth; + iterator->collection = collection; + BLI_gsetIterator_init(&iterator->gsi, iterator->collection->set); + iterator->iterator_initialized = true; - if (seq->seqbase.first) { - seq_build_array(&seq->seqbase, array, depth + 1); - } + *r_seq = BLI_gsetIterator_getKey(&iterator->gsi); + BLI_gsetIterator_step(&iterator->gsi); - **array = seq; - (*array)++; - } + return true; } -static void seq_array(Editing *ed, - const bool use_current_sequences, - Sequence ***r_seqarray, - int *r_seqarray_len) +/** + * Utility function for SEQ_ITERATOR_FOREACH macro. + * Yield collection element + * + * \param iterator: iterator to be initialized + * + * \return collection element or NULL when iteration has ended + */ +Sequence *SEQ_iterator_yield(SeqIterator *iterator) { - Sequence **array; - - *r_seqarray = NULL; - *r_seqarray_len = 0; - - if (ed == NULL) { - return; - } - - if (use_current_sequences) { - seq_count(ed->seqbasep, r_seqarray_len); - } - else { - seq_count(&ed->seqbase, r_seqarray_len); - } - - if (*r_seqarray_len == 0) { - return; - } + Sequence *seq = BLI_gsetIterator_done(&iterator->gsi) ? NULL : + BLI_gsetIterator_getKey(&iterator->gsi); + BLI_gsetIterator_step(&iterator->gsi); + return seq; +} - *r_seqarray = array = MEM_mallocN(sizeof(Sequence *) * (*r_seqarray_len), "SeqArray"); - if (use_current_sequences) { - seq_build_array(ed->seqbasep, &array, 0); - } - else { - seq_build_array(&ed->seqbase, &array, 0); - } +/** + * Free strip collection. + * + * \param collection: collection to be freed + */ +void SEQ_collection_free(SeqCollection *collection) +{ + BLI_gset_free(collection->set, NULL); + MEM_freeN(collection); } -void SEQ_iterator_begin(Editing *ed, SeqIterator *iter, const bool use_current_sequences) +/** + * Create new empty strip collection. + * + * \return empty strip collection. + */ +SeqCollection *SEQ_collection_create(void) { - memset(iter, 0, sizeof(*iter)); - seq_array(ed, use_current_sequences, &iter->array, &iter->tot); + SeqCollection *collection = MEM_callocN(sizeof(SeqCollection), "SeqCollection"); + collection->set = BLI_gset_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "SeqCollection GSet"); + return collection; +} - if (iter->tot) { - iter->cur = 0; - iter->seq = iter->array[iter->cur]; - iter->valid = 1; +/** + * Query strips from seqbase. seq_reference is used by query function as filter condition. + * + * \param seq_reference: reference strip for query function + * \param seqbase: ListBase in which strips are queried + * \param seq_query_func: query function callback + * \return strip collection + */ +SeqCollection *SEQ_query_by_reference(Sequence *seq_reference, + ListBase *seqbase, + void seq_query_func(Sequence *seq_reference, + ListBase *seqbase, + SeqCollection *collection)) +{ + SeqCollection *collection = SEQ_collection_create(); + seq_query_func(seq_reference, seqbase, collection); + return collection; +} +/** + * Add strip to collection. + * + * \param seq: strip to be added + * \param collection: collection to which strip will be added + * \return false if strip is already in set, otherwise true + */ +bool SEQ_collection_append_strip(Sequence *seq, SeqCollection *collection) +{ + if (BLI_gset_lookup(collection->set, seq) != NULL) { + return false; } + BLI_gset_insert(collection->set, seq); + return true; } -void SEQ_iterator_next(SeqIterator *iter) +/** + * Move strips from collection_src to collection_dst. Source collection will be freed. + * + * \param collection_dst: destination collection + * \param collection_src: source collection + */ +void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src) { - if (++iter->cur < iter->tot) { - iter->seq = iter->array[iter->cur]; - } - else { - iter->valid = 0; + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection_src) { + SEQ_collection_append_strip(seq, collection_dst); } + SEQ_collection_free(collection_src); } -void SEQ_iterator_end(SeqIterator *iter) +/** + * Expand collection by running SEQ_query() for each strip, which will be used as reference. + * Results of these queries will be merged into provided collection. + * + * \param seqbase: ListBase in which strips are queried + * \param collection: SeqCollection to be expanded + * \param seq_query_func: query function callback + */ +void SEQ_collection_expand(ListBase *seqbase, + SeqCollection *collection, + void seq_query_func(Sequence *seq_reference, + ListBase *seqbase, + SeqCollection *collection)) { - if (iter->array) { - MEM_freeN(iter->array); + /* Collect expanded results for each sequence in provided SeqIteratorCollection. */ + ListBase expand_collections = {0}; + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + SeqCollection *expand_collection = SEQ_query_by_reference(seq, seqbase, seq_query_func); + BLI_addtail(&expand_collections, expand_collection); } - iter->valid = 0; + /* Merge all expanded results in provided SeqIteratorCollection. */ + LISTBASE_FOREACH_MUTABLE (SeqCollection *, expand_collection, &expand_collections) { + BLI_remlink(&expand_collections, expand_collection); + SEQ_collection_merge(collection, expand_collection); + } } -int SEQ_iterator_seqbase_recursive_apply(ListBase *seqbase, - int (*apply_fn)(Sequence *seq, void *), - void *arg) +/** \} */ + +/** + * Query all strips in seqbase and nested meta strips. + * + * \param seqbase: ListBase in which strips are queried + * \return strip collection + */ +SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase) { - Sequence *iseq; - for (iseq = seqbase->first; iseq; iseq = iseq->next) { - if (SEQ_iterator_recursive_apply(iseq, apply_fn, arg) == -1) { - return -1; /* bail out */ + SeqCollection *collection = SEQ_collection_create(); + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (seq->type == SEQ_TYPE_META) { + SEQ_collection_merge(collection, SEQ_query_all_strips_recursive(&seq->seqbase)); } + SEQ_collection_append_strip(seq, collection); } - return 1; + return collection; } -int SEQ_iterator_recursive_apply(Sequence *seq, int (*apply_fn)(Sequence *, void *), void *arg) +/** + * Query all selected strips in seqbase. + * + * \param seqbase: ListBase in which strips are queried + * \return strip collection + */ +SeqCollection *SEQ_query_selected_strips(ListBase *seqbase) { - int ret = apply_fn(seq, arg); - - if (ret == -1) { - return -1; /* bail out */ - } - - if (ret && seq->seqbase.first) { - ret = SEQ_iterator_seqbase_recursive_apply(&seq->seqbase, apply_fn, arg); + SeqCollection *collection = SEQ_collection_create(); + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if ((seq->flag & SELECT) == 0) { + continue; + } + SEQ_collection_append_strip(seq, collection); } - - return ret; + return collection; } diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index cc11796496c..55c14944a23 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -448,8 +448,8 @@ static Sequence *seq_dupli(const Scene *scene_src, seqn->strip->stripdata = NULL; BLI_listbase_clear(&seqn->seqbase); - /* WATCH OUT!!! - This metastrip is not recursively duplicated here - do this after!!! */ - /* - seq_dupli_recursive(&seq->seqbase, &seqn->seqbase);*/ + /* WARNING: This meta-strip is not recursively duplicated here - do this after! */ + // seq_dupli_recursive(&seq->seqbase, &seqn->seqbase); } else if (seq->type == SEQ_TYPE_SCENE) { seqn->strip->stripdata = NULL; diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 4a27fb3a087..f1d4592fdec 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -149,7 +149,7 @@ void SEQ_edit_update_muting(Editing *ed) static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq) { LISTBASE_FOREACH (Sequence *, user_seq, seqbase) { - /* Look in metas for usage of seq. */ + /* Look in meta-strips for usage of seq. */ if (user_seq->type == SEQ_TYPE_META) { sequencer_flag_users_for_removal(scene, &user_seq->seqbase, seq); } diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 4a01b0e1938..a7a6e2595be 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -187,7 +187,7 @@ static void seq_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta) { seq_time_update_meta_strip(scene, seq_meta); - /* Prevent metastrip to move in timeline. */ + /* Prevent meta-strip to move in timeline. */ SEQ_transform_set_left_handle_frame(seq_meta, seq_meta->startdisp); SEQ_transform_set_right_handle_frame(seq_meta, seq_meta->enddisp); } @@ -196,7 +196,7 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) { Sequence *seqm; - /* check all metas recursively */ + /* Check all meta-strips recursively. */ seqm = seq->seqbase.first; while (seqm) { if (seqm->seqbase.first) { diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index a15465eb3c0..6d5332b2b15 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -160,7 +160,7 @@ void SEQ_sequence_base_unique_name_recursive(ListBase *seqbasep, Sequence *seq) while (sui.match) { sui.match = 0; seqbase_unique_name(seqbasep, &sui); - SEQ_iterator_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui); + SEQ_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui); } BLI_strncpy(seq->name + 2, sui.name_dest, sizeof(seq->name) - 2); @@ -584,3 +584,31 @@ void SEQ_set_scale_to_fit(const Sequence *seq, break; } } + +int SEQ_seqbase_recursive_apply(ListBase *seqbase, + int (*apply_fn)(Sequence *seq, void *), + void *arg) +{ + Sequence *iseq; + for (iseq = seqbase->first; iseq; iseq = iseq->next) { + if (SEQ_recursive_apply(iseq, apply_fn, arg) == -1) { + return -1; /* bail out */ + } + } + return 1; +} + +int SEQ_recursive_apply(Sequence *seq, int (*apply_fn)(Sequence *, void *), void *arg) +{ + int ret = apply_fn(seq, arg); + + if (ret == -1) { + return -1; /* bail out */ + } + + if (ret && seq->seqbase.first) { + ret = SEQ_seqbase_recursive_apply(&seq->seqbase, apply_fn, arg); + } + + return ret; +} diff --git a/source/blender/shader_fx/intern/FX_shader_glow.c b/source/blender/shader_fx/intern/FX_shader_glow.c index 30eaa35a049..fc75771cd62 100644 --- a/source/blender/shader_fx/intern/FX_shader_glow.c +++ b/source/blender/shader_fx/intern/FX_shader_glow.c @@ -71,12 +71,11 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); - if (mode == eShaderFxGlowMode_Luminance) { - uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE); - } - else { + uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE); + if (mode == eShaderFxGlowMode_Color) { uiItemR(layout, ptr, "select_color", 0, NULL, ICON_NONE); } + uiItemR(layout, ptr, "glow_color", 0, NULL, ICON_NONE); uiItemS(layout); diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 0f26ec50816..183b22c9791 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -203,6 +203,7 @@ if(WITH_XR_OPENXR) list(APPEND SRC xr/intern/wm_xr.c + xr/intern/wm_xr_actions.c xr/intern/wm_xr_draw.c xr/intern/wm_xr_session.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 280ee75a50f..edd5b555e2f 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -71,6 +71,11 @@ struct wmTabletData; struct wmNDOFMotionData; #endif +#ifdef WITH_XR_OPENXR +struct wmXrActionState; +struct wmXrPose; +#endif + typedef struct wmGizmo wmGizmo; typedef struct wmGizmoMap wmGizmoMap; typedef struct wmGizmoMapType wmGizmoMapType; @@ -929,7 +934,7 @@ void WM_generic_user_data_free(struct wmGenericUserData *wm_userdata); bool WM_region_use_viewport(struct ScrArea *area, struct ARegion *region); #ifdef WITH_XR_OPENXR -/* wm_xr.c */ +/* wm_xr_session.c */ bool WM_xr_session_exists(const wmXrData *xr); bool WM_xr_session_is_ready(const wmXrData *xr); struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr); @@ -939,7 +944,74 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, float r_viewmat[4][4], float *r_focal_len); -#endif +bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_location[3]); +bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_rotation[4]); + +/* wm_xr_actions.c */ +/* XR action functions to be called pre-XR session start. + * Note: The "destroy" functions can also be called post-session start. */ +bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name); +void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name); +bool WM_xr_action_create(wmXrData *xr, + const char *action_set_name, + const char *action_name, + eXrActionType type, + unsigned int count_subaction_paths, + const char **subaction_paths, + const float *float_threshold, + struct wmOperatorType *ot, + struct IDProperty *op_properties, + eXrOpFlag op_flag); +void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name); +bool WM_xr_action_space_create(wmXrData *xr, + const char *action_set_name, + const char *action_name, + unsigned int count_subaction_paths, + const char **subaction_paths, + const struct wmXrPose *poses); +void WM_xr_action_space_destroy(wmXrData *xr, + const char *action_set_name, + const char *action_name, + unsigned int count_subaction_paths, + const char **subaction_paths); +bool WM_xr_action_binding_create(wmXrData *xr, + const char *action_set_name, + const char *profile_path, + const char *action_name, + unsigned int count_interaction_paths, + const char **interaction_paths); +void WM_xr_action_binding_destroy(wmXrData *xr, + const char *action_set_name, + const char *profile_path, + const char *action_name, + unsigned int count_interaction_paths, + const char **interaction_paths); + +bool WM_xr_active_action_set_set( + wmXrData *xr, const char *action_set_name); /* If action_set_name is NULL, then + * all action sets will be treated as active. */ +bool WM_xr_controller_pose_action_set(wmXrData *xr, + const char *action_set_name, + const char *action_name); + +/* XR action functions to be called post-XR session start. */ +bool WM_xr_action_state_get(const wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *subaction_path, + struct wmXrActionState *r_state); +bool WM_xr_haptic_action_apply(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const long long *duration, + const float *frequency, + const float *amplitude); +void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name); +#endif /* WITH_XR_OPENXR */ #ifdef __cplusplus } diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index d54925272de..168b775d16d 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -132,17 +132,21 @@ struct wmWindowManager; extern "C" { #endif +typedef void (*wmGenericUserDataFreeFn)(void *data); + typedef struct wmGenericUserData { void *data; /** When NULL, use #MEM_freeN. */ - void (*free_fn)(void *data); + wmGenericUserDataFreeFn free_fn; bool use_free; } wmGenericUserData; +typedef void (*wmGenericCallbackFn)(struct bContext *C, void *user_data); + typedef struct wmGenericCallback { - void (*exec)(struct bContext *C, void *user_data); + wmGenericCallbackFn exec; void *user_data; - void (*free_user_data)(void *user_data); + wmGenericUserDataFreeFn free_user_data; } wmGenericCallback; /* ************** wmOperatorType ************************ */ @@ -680,6 +684,25 @@ typedef struct wmNDOFMotionData { } wmNDOFMotionData; #endif /* WITH_INPUT_NDOF */ +#ifdef WITH_XR_OPENXR +/* Similar to GHOST_XrPose. */ +typedef struct wmXrPose { + float position[3]; + /* Blender convention (w, x, y, z) */ + float orientation_quat[4]; +} wmXrPose; + +typedef struct wmXrActionState { + union { + bool state_boolean; + float state_float; + float state_vector2f[2]; + wmXrPose state_pose; + }; + int type; /* eXrActionType */ +} wmXrActionState; +#endif + /** Timer flags. */ typedef enum { /** Do not attempt to free customdata pointer even if non-NULL. */ diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index cdb7b591907..11783ae3517 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -264,7 +264,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4]) if (wrap == WM_CURSOR_WRAP_X) { mode_axis = GHOST_kAxisX; } - if (wrap == WM_CURSOR_WRAP_Y) { + else if (wrap == WM_CURSOR_WRAP_Y) { mode_axis = GHOST_kGrabAxisY; } } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 0d1f4cc4830..a6588c40f0f 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1048,7 +1048,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons wmWindowManager *wm = CTX_wm_manager(C); int retval = OPERATOR_CANCELLED; - CTX_wm_operator_poll_msg_set(C, NULL); + CTX_wm_operator_poll_msg_clear(C); if (op == NULL || op->type == NULL) { return retval; @@ -1469,7 +1469,7 @@ static int wm_operator_call_internal(bContext *C, { int retval; - CTX_wm_operator_poll_msg_set(C, NULL); + CTX_wm_operator_poll_msg_clear(C); /* Dummy test. */ if (ot) { @@ -4478,16 +4478,21 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.prevtype = event.type; event.prevval = event.val; - /* Ensure the event state is correct, any deviation from this may cause bugs. */ + /* Ensure the event state is correct, any deviation from this may cause bugs. + * + * NOTE: #EVENT_NONE is set when unknown keys are pressed, + * while not common, avoid a false alarm. */ #ifndef NDEBUG if ((event_state->type || event_state->val) && /* Ignore cleared event state. */ - !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type))) { + !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type) || + (event_state->type == EVENT_NONE))) { CLOG_WARN(WM_LOG_HANDLERS, "Non-keyboard/mouse button found in 'win->eventstate->type = %d'", event_state->type); } if ((event_state->prevtype || event_state->prevval) && /* Ignore cleared event state. */ - !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype))) { + !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype) || + (event_state->type == EVENT_NONE))) { CLOG_WARN(WM_LOG_HANDLERS, "Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'", event_state->prevtype); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index bbcb0669cce..d0ee7075516 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -2157,21 +2157,9 @@ static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data) C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data); } -static void wm_free_operator_properties_callback(void *user_data) -{ - IDProperty *properties = (IDProperty *)user_data; - IDP_FreeProperty(properties); -} - static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - if (U.uiflag & USER_SAVE_PROMPT && - wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) { - wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__); - callback->exec = wm_homefile_read_after_dialog_callback; - callback->user_data = IDP_CopyProperty(op->properties); - callback->free_user_data = wm_free_operator_properties_callback; - wm_close_file_dialog(C, callback); + if (wm_operator_close_file_dialog_if_needed(C, op, wm_homefile_read_after_dialog_callback)) { return OPERATOR_INTERFACE; } return wm_homefile_read_exec(C, op); @@ -2331,13 +2319,7 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op) set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN); } - if (U.uiflag & USER_SAVE_PROMPT && - wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) { - wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__); - callback->exec = wm_open_mainfile_after_dialog_callback; - callback->user_data = IDP_CopyProperty(op->properties); - callback->free_user_data = wm_free_operator_properties_callback; - wm_close_file_dialog(C, callback); + if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) { return OPERATOR_INTERFACE; } return wm_open_mainfile_dispatch(C, op); @@ -2637,12 +2619,25 @@ static int wm_recover_last_session_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -static int wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data) +{ + WM_operator_name_call_with_properties( + C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data); +} + +static int wm_recover_last_session_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) { /* Keep the current setting instead of using the preferences since a file selector * doesn't give us the option to change the setting. */ wm_open_init_use_scripts(op, false); - return WM_operator_confirm(C, op, event); + + if (wm_operator_close_file_dialog_if_needed( + C, op, wm_recover_last_session_after_dialog_callback)) { + return OPERATOR_INTERFACE; + } + return wm_recover_last_session_exec(C, op); } void WM_OT_recover_last_session(wmOperatorType *ot) @@ -3270,7 +3265,10 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0'; if (file_has_been_saved_before) { - WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL); + if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL) & + OPERATOR_CANCELLED) { + execute_callback = false; + } } else { WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL); @@ -3456,4 +3454,31 @@ void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action) } } +static void wm_free_operator_properties_callback(void *user_data) +{ + IDProperty *properties = (IDProperty *)user_data; + IDP_FreeProperty(properties); +} + +/** + * \return True if the dialog was created, the calling operator should return #OPERATOR_INTERFACE + * then. + */ +bool wm_operator_close_file_dialog_if_needed(bContext *C, + wmOperator *op, + wmGenericCallbackFn post_action_fn) +{ + if (U.uiflag & USER_SAVE_PROMPT && + wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) { + wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__); + callback->exec = post_action_fn; + callback->user_data = IDP_CopyProperty(op->properties); + callback->free_user_data = wm_free_operator_properties_callback; + wm_close_file_dialog(C, callback); + return true; + } + + return false; +} + /** \} */ diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index de563cd309d..5300649a0cd 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -97,45 +97,69 @@ static AUD_Device *audio_device = NULL; struct PlayState; static void playanim_window_zoom(struct PlayState *ps, const float zoom_offset); +/** + * The current state of the player. + * + * \warning Don't store results of parsing command-line arguments + * in this struct if they need to persist across playing back different + * files as these will be cleared when playing other files (drag & drop). + */ typedef struct PlayState { - /* window and viewport size */ + /** Window and viewport size. */ int win_x, win_y; - /* current zoom level */ + /** Current zoom level. */ float zoom; - /* playback state */ + /** Playback direction (-1, 1). */ short direction; + /** Set the next frame to implement frame stepping (using shortcuts). */ short next_frame; + /** Playback once then wait. */ bool once; - bool turbo; + /** Play forwards/backwards. */ bool pingpong; + /** Disable frame skipping. */ bool noskip; + /** Display current frame over the window. */ bool indicator; + /** Single-frame stepping has been enabled (frame loading and update pending). */ bool sstep; + /** Playback has stopped the image has been displayed. */ bool wait2; + /** Playback stopped state once stop/start variables have been handled. */ bool stopped; + /** + * When disabled the current animation will exit, + * after this either the application exits or a new animation window is opened. + * + * This is used so drag & drop can load new files which setup a newly created animation window. + */ bool go; - /* waiting for images to load */ + /** True when waiting for images to load. */ bool loading; - /* x/y image flip */ + /** X/Y image flip (set via key bindings). */ bool draw_flip[2]; + /** The number of frames to step each update (default to 1, command line argument). */ int fstep; - /* current picture */ + /** Current frame (picture). */ struct PlayAnimPict *picture; - /* set once at the start */ + /** Image size in pixels, set once at the start. */ int ibufx, ibufy; + /** Mono-space font ID. */ int fontid; - /* restarts player for file drop */ + /** Restarts player for file drop (drag & drop). */ char dropped_file[FILE_MAX]; + /** Force update when scrubbing with the cursor. */ bool need_frame_update; + /** The current frame calculated by scrubbing the mouse cursor. */ int frame_cursor_x; ColorManagedViewSettings view_settings; @@ -144,17 +168,14 @@ typedef struct PlayState { /* for debugging */ #if 0 -void print_ps(PlayState *ps) +static void print_ps(PlayState *ps) { printf("ps:\n"); printf(" direction=%d,\n", (int)ps->direction); - printf(" next=%d,\n", ps->next); printf(" once=%d,\n", ps->once); - printf(" turbo=%d,\n", ps->turbo); printf(" pingpong=%d,\n", ps->pingpong); printf(" noskip=%d,\n", ps->noskip); printf(" sstep=%d,\n", ps->sstep); - printf(" pause=%d,\n", ps->pause); printf(" wait2=%d,\n", ps->wait2); printf(" stopped=%d,\n", ps->stopped); printf(" go=%d,\n\n", ps->go); @@ -869,16 +890,14 @@ static void change_frame(PlayState *ps) static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) { PlayState *ps = (PlayState *)ps_void; - GHOST_TEventType type = GHOST_GetEventType(evt); - int val; + const GHOST_TEventType type = GHOST_GetEventType(evt); + /* Convert ghost event into value keyboard or mouse. */ + const int val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown); // print_ps(ps); playanim_event_qual_update(); - /* convert ghost event into value keyboard or mouse */ - val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown); - /* first check if we're busy loading files */ if (ps->loading) { switch (type) { @@ -902,8 +921,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) return 1; } - if (ps->wait2 && ps->stopped) { - ps->stopped = false; + if (ps->wait2 && ps->stopped == false) { + ps->stopped = true; } if (ps->wait2) { @@ -1379,7 +1398,9 @@ static void playanim_window_zoom(PlayState *ps, const float zoom_offset) GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey); } -/* return path for restart */ +/** + * \return The a path used to restart the animation player or NULL to exit. + */ static char *wm_main_playanim_intern(int argc, const char **argv) { struct ImBuf *ibuf = NULL; @@ -1398,7 +1419,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv) ps.direction = true; ps.next_frame = 1; ps.once = false; - ps.turbo = false; ps.pingpong = false; ps.noskip = false; ps.sstep = false; @@ -1420,6 +1440,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv) IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE)); IMB_colormanagement_init_default_view_settings(&ps.view_settings, &ps.display_settings); + /* Skip the first argument which is assumed to be '-a' (used to launch this player). */ while (argc > 1) { if (argv[1][0] == '-') { switch (argv[1][1]) { @@ -1720,14 +1741,15 @@ static char *wm_main_playanim_intern(int argc, const char **argv) ps.wait2 = ps.sstep; - if (ps.wait2 == false && ps.stopped == false) { - ps.stopped = true; + if (ps.wait2 == false && ps.stopped) { + ps.stopped = false; } pupdate_time(); if (ps.picture && ps.next_frame) { - /* always at least set one step */ + /* Advance to the next frame, always at least set one step. + * Implement frame-skipping when enabled and playback is not fast enough. */ while (ps.picture) { ps.picture = playanim_step(ps.picture, ps.next_frame); @@ -1740,7 +1762,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv) } } - if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) { + if (ps.wait2 || ptottime < swaptime || ps.noskip) { break; } ptottime -= swaptime; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 2e9fd1b1b16..cdd5ea12df8 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -133,7 +133,7 @@ static struct WMInitStruct { * \{ */ static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate); -static int wm_window_timer(const bContext *C); +static bool wm_window_timer(const bContext *C); /* XXX this one should correctly check for apple top header... * done for Cocoa : returns window contents (and not frame) max size*/ @@ -1498,12 +1498,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr * Timer handlers should check for delta to decide if they just update, or follow real time. * Timer handlers can also set duration to match frames passed */ -static int wm_window_timer(const bContext *C) +static bool wm_window_timer(const bContext *C) { Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); double time = PIL_check_seconds_timer(); - int retval = 0; + bool has_event = false; /* Mutable in case the timer gets removed. */ LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) { @@ -1540,31 +1540,34 @@ static int wm_window_timer(const bContext *C) event.customdata = wt; wm_event_add(win, &event); - retval = 1; + has_event = true; } } } - return retval; + return has_event; } void wm_window_process_events(const bContext *C) { BLI_assert(BLI_thread_is_main()); - int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */ + bool has_event = GHOST_ProcessEvents(g_system, false); /* `false` is no wait. */ - if (hasevent) { + if (has_event) { GHOST_DispatchEvents(g_system); } - hasevent |= wm_window_timer(C); + has_event |= wm_window_timer(C); #ifdef WITH_XR_OPENXR /* XR events don't use the regular window queues. So here we don't only trigger * processing/dispatching but also handling. */ - hasevent |= wm_xr_events_handle(CTX_wm_manager(C)); + has_event |= wm_xr_events_handle(CTX_wm_manager(C)); #endif - /* no event, we sleep 5 milliseconds */ - if (hasevent == 0) { + /* When there is no event, sleep 5 milliseconds not to use too much CPU when idle. + * + * Skip sleeping when simulating events so tests don't idle unnecessarily as simulated + * events are typically generated from a timer that runs in the main loop. */ + if ((has_event == false) && !(G.f & G_FLAG_EVENT_SIMULATE)) { PIL_sleep_ms(5); } } diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index d54090a6025..c7fe07cad7f 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -45,6 +45,9 @@ void wm_homefile_read(struct bContext *C, void wm_file_read_report(bContext *C, struct Main *bmain); void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action); +bool wm_operator_close_file_dialog_if_needed(bContext *C, + wmOperator *op, + wmGenericCallbackFn exec_fn); bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm); void WM_OT_save_homefile(struct wmOperatorType *ot); diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 439d611b085..2a67c2bee9f 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -128,6 +128,11 @@ bool wm_xr_events_handle(wmWindowManager *wm) if (wm->xr.runtime && wm->xr.runtime->context) { GHOST_XrEventsHandle(wm->xr.runtime->context); + /* Process OpenXR action events. */ + if (WM_xr_session_is_ready(&wm->xr)) { + wm_xr_session_actions_update(&wm->xr); + } + /* wm_window_process_events() uses the return value to determine if it can put the main thread * to sleep for some milliseconds. We never want that to happen while the VR session runs on * the main thread. So always return true. */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_actions.c new file mode 100644 index 00000000000..51ed3dcfd3c --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_actions.c @@ -0,0 +1,480 @@ +/* + * 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. + */ + +/** \file + * \ingroup wm + * + * \name Window-Manager XR Actions + * + * Uses the Ghost-XR API to manage OpenXR actions. + * All functions are designed to be usable by RNA / the Python API. + */ + +#include "BLI_math.h" + +#include "GHOST_C-api.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name XR-Action API + * + * API functions for managing OpenXR actions. + * + * \{ */ + +static wmXrActionSet *action_set_create(const char *action_set_name) +{ + wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__); + action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name"); + strcpy(action_set->name, action_set_name); + + return action_set; +} + +static void action_set_destroy(void *val) +{ + wmXrActionSet *action_set = val; + + MEM_SAFE_FREE(action_set->name); + + MEM_freeN(action_set); +} + +static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name) +{ + return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name); +} + +static wmXrAction *action_create(const char *action_name, + eXrActionType type, + unsigned int count_subaction_paths, + const char **subaction_paths, + const float *float_threshold, + wmOperatorType *ot, + IDProperty *op_properties, + eXrOpFlag op_flag) +{ + wmXrAction *action = MEM_callocN(sizeof(*action), __func__); + action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name"); + strcpy(action->name, action_name); + action->type = type; + + const unsigned int count = count_subaction_paths; + action->count_subaction_paths = count; + + action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count, + "XrAction_SubactionPaths"); + for (unsigned int i = 0; i < count; ++i) { + action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1, + "XrAction_SubactionPath"); + strcpy(action->subaction_paths[i], subaction_paths[i]); + } + + size_t size; + switch (type) { + case XR_BOOLEAN_INPUT: + size = sizeof(bool); + break; + case XR_FLOAT_INPUT: + size = sizeof(float); + break; + case XR_VECTOR2F_INPUT: + size = sizeof(float) * 2; + break; + case XR_POSE_INPUT: + size = sizeof(GHOST_XrPose); + break; + case XR_VIBRATION_OUTPUT: + return action; + } + action->states = MEM_calloc_arrayN(count, size, "XrAction_States"); + action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev"); + + if (float_threshold) { + BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); + action->float_threshold = *float_threshold; + CLAMP(action->float_threshold, 0.0f, 1.0f); + } + + action->ot = ot; + action->op_properties = op_properties; + action->op_flag = op_flag; + + return action; +} + +static void action_destroy(void *val) +{ + wmXrAction *action = val; + + MEM_SAFE_FREE(action->name); + + const unsigned int count = action->count_subaction_paths; + char **subaction_paths = action->subaction_paths; + if (subaction_paths) { + for (unsigned int i = 0; i < count; ++i) { + MEM_SAFE_FREE(subaction_paths[i]); + } + MEM_freeN(subaction_paths); + } + + MEM_SAFE_FREE(action->states); + MEM_SAFE_FREE(action->states_prev); + + MEM_freeN(action); +} + +static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + return GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name); +} + +bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name) +{ + if (action_set_find(xr, action_set_name)) { + return false; + } + + wmXrActionSet *action_set = action_set_create(action_set_name); + + GHOST_XrActionSetInfo info = { + .name = action_set_name, + .customdata_free_fn = action_set_destroy, + .customdata = action_set, + }; + + if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) { + return false; + } + + return true; +} + +void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return; + } + + wmXrSessionState *session_state = &xr->runtime->session_state; + + if (action_set == session_state->active_action_set) { + if (action_set->controller_pose_action) { + wm_xr_session_controller_data_clear(session_state); + action_set->controller_pose_action = NULL; + } + if (action_set->active_modal_action) { + action_set->active_modal_action = NULL; + } + session_state->active_action_set = NULL; + } + + GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name); +} + +bool WM_xr_action_create(wmXrData *xr, + const char *action_set_name, + const char *action_name, + eXrActionType type, + unsigned int count_subaction_paths, + const char **subaction_paths, + const float *float_threshold, + wmOperatorType *ot, + IDProperty *op_properties, + eXrOpFlag op_flag) +{ + if (action_find(xr, action_set_name, action_name)) { + return false; + } + + wmXrAction *action = action_create(action_name, + type, + count_subaction_paths, + subaction_paths, + float_threshold, + ot, + op_properties, + op_flag); + + GHOST_XrActionInfo info = { + .name = action_name, + .count_subaction_paths = count_subaction_paths, + .subaction_paths = subaction_paths, + .states = action->states, + .customdata_free_fn = action_destroy, + .customdata = action, + }; + + switch (type) { + case XR_BOOLEAN_INPUT: + info.type = GHOST_kXrActionTypeBooleanInput; + break; + case XR_FLOAT_INPUT: + info.type = GHOST_kXrActionTypeFloatInput; + break; + case XR_VECTOR2F_INPUT: + info.type = GHOST_kXrActionTypeVector2fInput; + break; + case XR_POSE_INPUT: + info.type = GHOST_kXrActionTypePoseInput; + break; + case XR_VIBRATION_OUTPUT: + info.type = GHOST_kXrActionTypeVibrationOutput; + break; + } + + if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) { + return false; + } + + return true; +} + +void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return; + } + + if (action_set->controller_pose_action && + STREQ(action_set->controller_pose_action->name, action_name)) { + if (action_set == xr->runtime->session_state.active_action_set) { + wm_xr_session_controller_data_clear(&xr->runtime->session_state); + } + action_set->controller_pose_action = NULL; + } + if (action_set->active_modal_action && + STREQ(action_set->active_modal_action->name, action_name)) { + action_set->active_modal_action = NULL; + } + + wmXrAction *action = action_find(xr, action_set_name, action_name); + if (!action) { + return; + } +} + +bool WM_xr_action_space_create(wmXrData *xr, + const char *action_set_name, + const char *action_name, + unsigned int count_subaction_paths, + const char **subaction_paths, + const wmXrPose *poses) +{ + GHOST_XrActionSpaceInfo info = { + .action_name = action_name, + .count_subaction_paths = count_subaction_paths, + .subaction_paths = subaction_paths, + }; + + GHOST_XrPose *ghost_poses = MEM_malloc_arrayN( + count_subaction_paths, sizeof(*ghost_poses), __func__); + for (unsigned int i = 0; i < count_subaction_paths; ++i) { + const wmXrPose *pose = &poses[i]; + GHOST_XrPose *ghost_pose = &ghost_poses[i]; + copy_v3_v3(ghost_pose->position, pose->position); + copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat); + } + info.poses = ghost_poses; + + bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true : + false; + MEM_freeN(ghost_poses); + return ret; +} + +void WM_xr_action_space_destroy(wmXrData *xr, + const char *action_set_name, + const char *action_name, + unsigned int count_subaction_paths, + const char **subaction_paths) +{ + GHOST_XrActionSpaceInfo info = { + .action_name = action_name, + .count_subaction_paths = count_subaction_paths, + .subaction_paths = subaction_paths, + }; + + GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info); +} + +bool WM_xr_action_binding_create(wmXrData *xr, + const char *action_set_name, + const char *profile_path, + const char *action_name, + unsigned int count_interaction_paths, + const char **interaction_paths) +{ + GHOST_XrActionBindingInfo binding_info = { + .action_name = action_name, + .count_interaction_paths = count_interaction_paths, + .interaction_paths = interaction_paths, + }; + + GHOST_XrActionProfileInfo profile_info = { + .profile_path = profile_path, + .count_bindings = 1, + .bindings = &binding_info, + }; + + return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); +} + +void WM_xr_action_binding_destroy(wmXrData *xr, + const char *action_set_name, + const char *profile_path, + const char *action_name, + unsigned int count_interaction_paths, + const char **interaction_paths) +{ + GHOST_XrActionBindingInfo binding_info = { + .action_name = action_name, + .count_interaction_paths = count_interaction_paths, + .interaction_paths = interaction_paths, + }; + + GHOST_XrActionProfileInfo profile_info = { + .profile_path = profile_path, + .count_bindings = 1, + .bindings = &binding_info, + }; + + GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); +} + +bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return false; + } + + { + /* Unset active modal action (if any). */ + wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set; + if (active_action_set) { + wmXrAction *active_modal_action = active_action_set->active_modal_action; + if (active_modal_action) { + if (active_modal_action->active_modal_path) { + active_modal_action->active_modal_path = NULL; + } + active_action_set->active_modal_action = NULL; + } + } + } + + xr->runtime->session_state.active_action_set = action_set; + + if (action_set->controller_pose_action) { + wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr); + } + + return true; +} + +bool WM_xr_controller_pose_action_set(wmXrData *xr, + const char *action_set_name, + const char *action_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return false; + } + + wmXrAction *action = action_find(xr, action_set_name, action_name); + if (!action) { + return false; + } + + action_set->controller_pose_action = action; + + if (action_set == xr->runtime->session_state.active_action_set) { + wm_xr_session_controller_data_populate(action, xr); + } + + return true; +} + +bool WM_xr_action_state_get(const wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *subaction_path, + wmXrActionState *r_state) +{ + const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name); + if (!action) { + return false; + } + + BLI_assert(action->type == (eXrActionType)r_state->type); + + /* Find the action state corresponding to the subaction path. */ + for (unsigned int i = 0; i < action->count_subaction_paths; ++i) { + if (STREQ(subaction_path, action->subaction_paths[i])) { + switch ((eXrActionType)r_state->type) { + case XR_BOOLEAN_INPUT: + r_state->state_boolean = ((bool *)action->states)[i]; + break; + case XR_FLOAT_INPUT: + r_state->state_float = ((float *)action->states)[i]; + break; + case XR_VECTOR2F_INPUT: + copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]); + break; + case XR_POSE_INPUT: { + const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i]; + copy_v3_v3(r_state->state_pose.position, pose->position); + copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat); + break; + } + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + return true; + } + } + + return false; +} + +bool WM_xr_haptic_action_apply(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const long long *duration, + const float *frequency, + const float *amplitude) +{ + return GHOST_XrApplyHapticAction( + xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ? + true : + false; +} + +void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name); +} + +/** \} */ /* XR-Action API */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index cc4a7e41e82..1f722855696 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -45,6 +45,12 @@ void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]) translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]); } +void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]) +{ + quat_to_mat4(r_mat, pose->orientation_quat); + copy_v3_v3(r_mat[3], pose->position); +} + static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, const XrSessionSettings *session_settings, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 25e3da3ffb4..9bf63be61dd 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -24,6 +24,21 @@ #include "wm_xr.h" +struct wmXrActionSet; + +typedef struct wmXrControllerData { + /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). + This subaction path will later be combined with a component path, and that combined path should + also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = + /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). + */ + char subaction_path[64]; + /** Last known controller pose (in world space) stored for queries. */ + GHOST_XrPose pose; + /** The last known controller matrix, calculated from above's controller pose. */ + float mat[4][4]; +} wmXrControllerData; + typedef struct wmXrSessionState { bool is_started; @@ -39,11 +54,23 @@ typedef struct wmXrSessionState { Object *prev_base_pose_object; /** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */ int prev_settings_flag; + /** Copy of wmXrDrawData.base_pose. */ + GHOST_XrPose prev_base_pose; + /** Copy of GHOST_XrDrawViewInfo.local_pose. */ + GHOST_XrPose prev_local_pose; /** Copy of wmXrDrawData.eye_position_ofs. */ float prev_eye_position_ofs[3]; bool force_reset_to_base_pose; bool is_view_data_set; + + /** Last known controller data. */ + wmXrControllerData controllers[2]; + + /** The currently active action set that will be updated on calls to + * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and + * updated. */ + struct wmXrActionSet *active_action_set; } wmXrSessionState; typedef struct wmXrRuntimeData { @@ -79,6 +106,40 @@ typedef struct wmXrDrawData { float eye_position_ofs[3]; /* Local/view space. */ } wmXrDrawData; +typedef struct wmXrAction { + char *name; + eXrActionType type; + unsigned int count_subaction_paths; + char **subaction_paths; + /** States for each subaction path. */ + void *states; + /** Previous states, stored to determine XR events. */ + void *states_prev; + + /** Input threshold for float/vector2f actions. */ + float float_threshold; + + /** The currently active subaction path (if any) for modal actions. */ + char **active_modal_path; + + /** Operator to be called on XR events. */ + struct wmOperatorType *ot; + IDProperty *op_properties; + eXrOpFlag op_flag; +} wmXrAction; + +typedef struct wmXrActionSet { + char *name; + + /** The XR pose action that determines the controller + * transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose", + * although it could differ depending on the specification and hardware. */ + wmXrAction *controller_pose_action; + + /** The currently active modal action (if any). */ + wmXrAction *active_modal_action; +} wmXrActionSet; + wmXrRuntimeData *wm_xr_runtime_data_create(void); void wm_xr_runtime_data_free(wmXrRuntimeData **runtime); @@ -95,5 +156,12 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, void *wm_xr_session_gpu_binding_context_create(void); void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context); +void wm_xr_session_actions_init(wmXrData *xr); +void wm_xr_session_actions_update(wmXrData *xr); +void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, + wmXrData *xr); +void wm_xr_session_controller_data_clear(wmXrSessionState *state); + void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]); +void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index b9ef40e3398..1ddbe228e05 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -18,7 +18,9 @@ * \ingroup wm */ +#include "BKE_callbacks.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_main.h" #include "BKE_scene.h" @@ -49,11 +51,24 @@ static CLG_LogRef LOG = {"wm.xr"}; /* -------------------------------------------------------------------- */ +static void wm_xr_session_create_cb(void) +{ + Main *bmain = G_MAIN; + wmWindowManager *wm = bmain->wm.first; + wmXrData *xr_data = &wm->xr; + + /* Get action set data from Python. */ + BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE); + + wm_xr_session_actions_init(xr_data); +} + static void wm_xr_session_exit_cb(void *customdata) { wmXrData *xr_data = customdata; xr_data->runtime->session_state.is_started = false; + if (xr_data->runtime->exit_fn) { xr_data->runtime->exit_fn(xr_data); } @@ -65,6 +80,10 @@ static void wm_xr_session_exit_cb(void *customdata) static void wm_xr_session_begin_info_create(wmXrData *xr_data, GHOST_XrSessionBeginInfo *r_begin_info) { + /* Callback for when the session is created. This is needed to create and bind OpenXR actions + * after the session is created but before it is started. */ + r_begin_info->create_fn = wm_xr_session_create_cb; + /* WM-XR exit function, does some own stuff and calls callback passed to wm_xr_session_toggle(), * to allow external code to execute its own session-exit logic. */ r_begin_info->exit_fn = wm_xr_session_exit_cb; @@ -289,6 +308,7 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state, /** * Update information that is only stored for external state queries. E.g. for Python API to * request the current (as in, last known) viewer pose. + * Controller data and action sets will be updated separately via wm_xr_session_actions_update(). */ void wm_xr_session_state_update(const XrSessionSettings *settings, const wmXrDrawData *draw_data, @@ -322,6 +342,8 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, DEFAULT_SENSOR_WIDTH); copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); + memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose)); + memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose)); state->prev_settings_flag = settings->flag; state->prev_base_pose_type = settings->base_pose_type; state->prev_base_pose_object = settings->base_pose_object; @@ -373,6 +395,132 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, return true; } +bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_location[3]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || + subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) { + zero_v3(r_location); + return false; + } + + copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position); + return true; +} + +bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_rotation[4]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || + subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) { + unit_qt(r_rotation); + return false; + } + + copy_v4_v4(r_rotation, + xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat); + return true; +} + +/* -------------------------------------------------------------------- */ +/** \name XR-Session Actions + * + * XR action processing and event dispatching. + * + * \{ */ + +void wm_xr_session_actions_init(wmXrData *xr) +{ + if (!xr->runtime) { + return; + } + + GHOST_XrAttachActionSets(xr->runtime->context); +} + +static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings, + const wmXrAction *controller_pose_action, + wmXrSessionState *state) +{ + const unsigned int count = (unsigned int)min_ii( + (int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers)); + + float view_ofs[3]; + float base_inv[4][4]; + float tmp[4][4]; + + zero_v3(view_ofs); + if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { + add_v3_v3(view_ofs, state->prev_local_pose.position); + } + + wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv); + invert_m4(base_inv); + + for (unsigned int i = 0; i < count; ++i) { + wmXrControllerData *controller = &state->controllers[i]; + + /* Calculate controller matrix in world space. */ + wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp); + + /* Apply eye position and base pose offsets. */ + sub_v3_v3(tmp[3], view_ofs); + mul_m4_m4m4(controller->mat, base_inv, tmp); + + /* Save final pose. */ + mat4_to_loc_quat( + controller->pose.position, controller->pose.orientation_quat, controller->mat); + } +} + +void wm_xr_session_actions_update(wmXrData *xr) +{ + if (!xr->runtime) { + return; + } + + GHOST_XrContextHandle xr_context = xr->runtime->context; + wmXrSessionState *state = &xr->runtime->session_state; + wmXrActionSet *active_action_set = state->active_action_set; + + int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL); + if (!ret) { + return; + } + + /* Only update controller mats for active action set. */ + if (active_action_set) { + if (active_action_set->controller_pose_action) { + wm_xr_session_controller_mats_update( + &xr->session_settings, active_action_set->controller_pose_action, state); + } + } +} + +void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr) +{ + wmXrSessionState *state = &xr->runtime->session_state; + + const unsigned int count = (unsigned int)min_ii( + (int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths); + + for (unsigned int i = 0; i < count; ++i) { + wmXrControllerData *c = &state->controllers[i]; + strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]); + memset(&c->pose, 0, sizeof(c->pose)); + zero_m4(c->mat); + } +} + +void wm_xr_session_controller_data_clear(wmXrSessionState *state) +{ + memset(state->controllers, 0, sizeof(state->controllers)); +} + +/** \} */ /* XR-Session Actions */ + /* -------------------------------------------------------------------- */ /** \name XR-Session Surface * diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index cb5fc538e69..f1741c3b495 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -130,14 +130,11 @@ set(SRC # MSVC 2010 gives linking errors with the manifest if(WIN32 AND NOT UNIX) - string(SUBSTRING ${BLENDER_VERSION} 0 1 bver1) - string(SUBSTRING ${BLENDER_VERSION} 2 1 bver2) - string(SUBSTRING ${BLENDER_VERSION} 3 1 bver3) add_definitions( -DBLEN_VER_RC_STR="${BLENDER_VERSION}" - -DBLEN_VER_RC_1=${bver1} - -DBLEN_VER_RC_2=${bver2} - -DBLEN_VER_RC_3=${bver3} + -DBLEN_VER_RC_1=${BLENDER_VERSION_MAJOR} + -DBLEN_VER_RC_2=${BLENDER_VERSION_MINOR} + -DBLEN_VER_RC_3=${BLENDER_VERSION_PATCH} -DBLEN_VER_RC_4=0 ) diff --git a/source/creator/creator.c b/source/creator/creator.c index b40718d1f7c..51efadf5e56 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -132,7 +132,6 @@ struct ApplicationState app_state = { /** \name Application Level Callbacks * * Initialize callbacks for the modules that need them. - * * \{ */ static void callback_mem_error(const char *errorStr) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 9c7b7dc3f34..36fdaef507b 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -1195,15 +1195,17 @@ static const char arg_handle_playback_mode_doc[] = "\t\tZero disables (clamping to a fixed number of frames instead)."; static int arg_handle_playback_mode(int argc, const char **argv, void *UNUSED(data)) { - /* not if -b was given first */ + /* Ignore the animation player if `-b` was given first. */ if (G.background == 0) { # ifdef WITH_FFMPEG /* Setup FFmpeg with current debug flags. */ IMB_ffmpeg_init(); # endif - WM_main_playanim(argc, argv); /* not the same argc and argv as before */ - exit(0); /* 2.4x didn't do this */ + /* This function knows to skip this argument ('-a'). */ + WM_main_playanim(argc, argv); + + exit(0); } return -2; diff --git a/source/creator/creator_intern.h b/source/creator/creator_intern.h index bcc8a15355a..2260da8db11 100644 --- a/source/creator/creator_intern.h +++ b/source/creator/creator_intern.h @@ -72,7 +72,7 @@ enum { /* for the callbacks: */ #ifndef WITH_PYTHON_MODULE -# define BLEND_VERSION_FMT "Blender %d.%02d.%d" +# define BLEND_VERSION_FMT "Blender %d.%d.%d" # define BLEND_VERSION_ARG (BLENDER_VERSION / 100), (BLENDER_VERSION % 100), BLENDER_VERSION_PATCH #endif |