diff options
Diffstat (limited to 'source')
657 files changed, 23278 insertions, 7000 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 8d18cf0ae9a..fbc0ec440cf 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -92,6 +92,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_texture_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_tracking_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_userdef_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_uuid_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vec_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vfont_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view2d_types.h diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.h b/source/blender/blenkernel/BKE_anonymous_attribute.h new file mode 100644 index 00000000000..ebdb0b05160 --- /dev/null +++ b/source/blender/blenkernel/BKE_anonymous_attribute.h @@ -0,0 +1,43 @@ +/* + * 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 + * + * An #AnonymousAttributeID is used to identify attributes that are not explicitly named. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AnonymousAttributeID AnonymousAttributeID; + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name); +AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name); +bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id); +const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id); +const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.hh b/source/blender/blenkernel/BKE_anonymous_attribute.hh new file mode 100644 index 00000000000..56e6335c7c0 --- /dev/null +++ b/source/blender/blenkernel/BKE_anonymous_attribute.hh @@ -0,0 +1,169 @@ +/* + * 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 <atomic> +#include <string> + +#include "BLI_hash.hh" +#include "BLI_string_ref.hh" + +#include "BKE_anonymous_attribute.h" + +namespace blender::bke { + +/** + * Wrapper for #AnonymousAttributeID with RAII semantics. + * This class should typically not be used directly. Instead use #StrongAnonymousAttributeID or + * #WeakAnonymousAttributeID. + */ +template<bool IsStrongReference> class OwnedAnonymousAttributeID { + private: + const AnonymousAttributeID *data_ = nullptr; + + template<bool OtherIsStrongReference> friend class OwnedAnonymousAttributeID; + + public: + OwnedAnonymousAttributeID() = default; + + /** Create a new anonymous attribute id. */ + explicit OwnedAnonymousAttributeID(StringRefNull debug_name) + { + if constexpr (IsStrongReference) { + data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str()); + } + else { + data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str()); + } + } + + /** + * This transfers ownership, so no incref is necessary. + * The caller has to make sure that it owned the anonymous id. + */ + explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id) + : data_(anonymous_id) + { + } + + template<bool OtherIsStrong> + OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID<OtherIsStrong> &other) + { + data_ = other.data_; + this->incref(); + } + + template<bool OtherIsStrong> + OwnedAnonymousAttributeID(OwnedAnonymousAttributeID<OtherIsStrong> &&other) + { + data_ = other.data_; + this->incref(); + other.decref(); + other.data_ = nullptr; + } + + ~OwnedAnonymousAttributeID() + { + this->decref(); + } + + template<bool OtherIsStrong> + OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID<OtherIsStrong> &other) + { + if (this == &other) { + return *this; + } + this->~OwnedAnonymousAttributeID(); + new (this) OwnedAnonymousAttributeID(other); + return *this; + } + + template<bool OtherIsStrong> + OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID<OtherIsStrong> &&other) + { + if (this == &other) { + return *this; + } + this->~OwnedAnonymousAttributeID(); + new (this) OwnedAnonymousAttributeID(std::move(other)); + return *this; + } + + operator bool() const + { + return data_ != nullptr; + } + + StringRefNull debug_name() const + { + BLI_assert(data_ != nullptr); + return BKE_anonymous_attribute_id_debug_name(data_); + } + + bool has_strong_references() const + { + BLI_assert(data_ != nullptr); + return BKE_anonymous_attribute_id_has_strong_references(data_); + } + + /** Extract the ownership of the currently wrapped anonymous id. */ + const AnonymousAttributeID *extract() + { + const AnonymousAttributeID *extracted_data = data_; + /* Don't decref because the caller becomes the new owner. */ + data_ = nullptr; + return extracted_data; + } + + /** Get the wrapped anonymous id, without taking ownership. */ + const AnonymousAttributeID *get() const + { + return data_; + } + + private: + void incref() + { + if (data_ == nullptr) { + return; + } + if constexpr (IsStrongReference) { + BKE_anonymous_attribute_id_increment_strong(data_); + } + else { + BKE_anonymous_attribute_id_increment_weak(data_); + } + } + + void decref() + { + if (data_ == nullptr) { + return; + } + if constexpr (IsStrongReference) { + BKE_anonymous_attribute_id_decrement_strong(data_); + } + else { + BKE_anonymous_attribute_id_decrement_weak(data_); + } + } +}; + +using StrongAnonymousAttributeID = OwnedAnonymousAttributeID<true>; +using WeakAnonymousAttributeID = OwnedAnonymousAttributeID<false>; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 5fda30224c6..7476474258b 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -66,6 +66,11 @@ bool BKE_id_attribute_remove(struct ID *id, struct CustomDataLayer *layer, struct ReportList *reports); +struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id, + const char *name, + const int type, + const AttributeDomain domain); + AttributeDomain BKE_id_attribute_domain(struct ID *id, struct CustomDataLayer *layer); int BKE_id_attribute_data_length(struct ID *id, struct CustomDataLayer *layer); bool BKE_id_attribute_required(struct ID *id, struct CustomDataLayer *layer); diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index c3f7dbd4bd9..cf54e7efa0d 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -22,6 +22,7 @@ #include "FN_generic_span.hh" #include "FN_generic_virtual_array.hh" +#include "BKE_anonymous_attribute.hh" #include "BKE_attribute.h" #include "BLI_color.hh" @@ -29,6 +30,81 @@ #include "BLI_float3.hh" #include "BLI_function_ref.hh" +namespace blender::bke { + +/** + * Identifies an attribute that is either named or anonymous. + * It does not own the identifier, so it is just a reference. + */ +class AttributeIDRef { + private: + StringRef name_; + const AnonymousAttributeID *anonymous_id_ = nullptr; + + public: + AttributeIDRef() = default; + + AttributeIDRef(StringRef name) : name_(name) + { + } + + AttributeIDRef(StringRefNull name) : name_(name) + { + } + + AttributeIDRef(const char *name) : name_(name) + { + } + + AttributeIDRef(const std::string &name) : name_(name) + { + } + + /* The anonymous id is only borrowed, the caller has to keep a reference to it. */ + AttributeIDRef(const AnonymousAttributeID *anonymous_id) : anonymous_id_(anonymous_id) + { + } + + operator bool() const + { + return this->is_named() || this->is_anonymous(); + } + + friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) + { + return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; + } + + uint64_t hash() const + { + return get_default_hash_2(name_, anonymous_id_); + } + + bool is_named() const + { + return !name_.is_empty(); + } + + bool is_anonymous() const + { + return anonymous_id_ != nullptr; + } + + StringRef name() const + { + BLI_assert(this->is_named()); + return name_; + } + + const AnonymousAttributeID &anonymous_id() const + { + BLI_assert(this->is_anonymous()); + return *anonymous_id_; + } +}; + +} // namespace blender::bke + /** * 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 @@ -104,8 +180,8 @@ struct AttributeInitMove : public AttributeInit { }; /* Returns false when the iteration should be stopped. */ -using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name, - const AttributeMetaData &meta_data)>; +using AttributeForeachCallback = blender::FunctionRef<bool( + const blender::bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>; namespace blender::bke { @@ -171,7 +247,7 @@ class OutputAttribute { GVMutableArrayPtr varray_; AttributeDomain domain_; SaveFn save_; - std::optional<fn::GVMutableArray_GSpan> optional_span_varray_; + std::unique_ptr<fn::GVMutableArray_GSpan> optional_span_varray_; bool ignore_old_values_ = false; bool save_has_been_called_ = false; @@ -230,9 +306,10 @@ class OutputAttribute { fn::GMutableSpan as_span() { - if (!optional_span_varray_.has_value()) { + if (!optional_span_varray_) { const bool materialize_old_values = !ignore_old_values_; - optional_span_varray_.emplace(*varray_, materialize_old_values); + optional_span_varray_ = std::make_unique<fn::GVMutableArray_GSpan>(*varray_, + materialize_old_values); } fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; return span_varray; @@ -333,26 +410,28 @@ class CustomDataAttributes { void reallocate(const int size); - std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const; + std::optional<blender::fn::GSpan> get_for_read(const AttributeIDRef &attribute_id) const; - blender::fn::GVArrayPtr get_for_read(const StringRef name, + blender::fn::GVArrayPtr get_for_read(const AttributeIDRef &attribute_id, const CustomDataType data_type, const void *default_value) const; template<typename T> - blender::fn::GVArray_Typed<T> get_for_read(const blender::StringRef name, + blender::fn::GVArray_Typed<T> get_for_read(const AttributeIDRef &attribute_id, 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); - GVArrayPtr varray = this->get_for_read(name, type, &default_value); + GVArrayPtr varray = this->get_for_read(attribute_id, type, &default_value); return blender::fn::GVArray_Typed<T>(std::move(varray)); } - std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name); - bool create(const blender::StringRef name, const CustomDataType data_type); - bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer); - bool remove(const blender::StringRef name); + std::optional<blender::fn::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id); + bool create(const AttributeIDRef &attribute_id, const CustomDataType data_type); + bool create_by_move(const AttributeIDRef &attribute_id, + const CustomDataType data_type, + void *buffer); + bool remove(const AttributeIDRef &attribute_id); bool foreach_attribute(const AttributeForeachCallback callback, const AttributeDomain domain) const; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 315a77499e5..d71cb559911 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 19 +#define BLENDER_FILE_SUBVERSION 22 /* 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_context.h b/source/blender/blenkernel/BKE_context.h index eda6a03fa1a..b0705ff411f 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -256,9 +256,11 @@ int /*eContextResult*/ CTX_data_get( const bContext *C, const char *member, PointerRNA *r_ptr, ListBase *r_lb, short *r_type); void CTX_data_id_pointer_set(bContextDataResult *result, struct ID *id); +void CTX_data_pointer_set_ptr(bContextDataResult *result, const PointerRNA *ptr); void CTX_data_pointer_set(bContextDataResult *result, struct ID *id, StructRNA *type, void *data); void CTX_data_id_list_add(bContextDataResult *result, struct ID *id); +void CTX_data_list_add_ptr(bContextDataResult *result, const PointerRNA *ptr); void CTX_data_list_add(bContextDataResult *result, struct ID *id, StructRNA *type, void *data); void CTX_data_dir_set(bContextDataResult *result, const char **dir); diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 7a44553c565..0732f1e5190 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -33,6 +33,7 @@ extern "C" { #endif +struct AnonymousAttributeID; struct BMesh; struct BlendDataReader; struct BlendWriter; @@ -193,6 +194,12 @@ void *CustomData_add_layer_named(struct CustomData *data, void *layer, int totelem, const char *name); +void *CustomData_add_layer_anonymous(struct CustomData *data, + int type, + eCDAllocType alloctype, + void *layer, + int totelem, + const struct AnonymousAttributeID *anonymous_id); /* frees the active or first data layer with the give type. * returns 1 on success, 0 if no layer with the given type is found @@ -231,6 +238,11 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data, const int type, const char *name, const int totelem); +void *CustomData_duplicate_referenced_layer_anonymous( + CustomData *data, + const int type, + const struct AnonymousAttributeID *anonymous_id, + const int totelem); bool CustomData_is_referenced_layer(struct CustomData *data, int type); /* Duplicate all the layers with flag NOFREE, and remove the flag from duplicated layers. */ diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index 0f37ba6c4ce..db5663fcc94 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -82,7 +82,6 @@ DispList *BKE_displist_find(struct ListBase *lb, int type); void BKE_displist_normals_add(struct ListBase *lb); void BKE_displist_count(const struct ListBase *lb, int *totvert, int *totface, int *tottri); void BKE_displist_free(struct ListBase *lb); -bool BKE_displist_has_faces(const struct ListBase *lb); void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph, const struct Scene *scene, @@ -94,12 +93,8 @@ void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph, struct ListBase *dispbase, struct Mesh **r_final); void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); -void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - struct ListBase *dispbase); -bool BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph, +void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph, const struct Scene *scene, struct Object *ob, struct ListBase *source_nurb, diff --git a/source/blender/blenkernel/BKE_duplilist.h b/source/blender/blenkernel/BKE_duplilist.h index c142d5338d1..989b68f4ccb 100644 --- a/source/blender/blenkernel/BKE_duplilist.h +++ b/source/blender/blenkernel/BKE_duplilist.h @@ -31,6 +31,7 @@ struct ListBase; struct Object; struct ParticleSystem; struct Scene; +struct ID; /* ---------------------------------------------------- */ /* Dupli-Geometry */ @@ -42,7 +43,10 @@ void free_object_duplilist(struct ListBase *lb); typedef struct DupliObject { struct DupliObject *next, *prev; + /* Object whose geometry is instanced. */ struct Object *ob; + /* Data owned by the object above that is instanced. This might not be the same as `ob->data`. */ + struct ID *ob_data; float mat[4][4]; float orco[3], uv[2]; diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index ffd8ac42c63..2c24b1a5487 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -79,6 +79,11 @@ typedef struct BMEditMesh { int mirror_cdlayer; /** + * Enable for evaluated copies, causes the edit-mesh to free the memory, not it's contents. + */ + char is_shallow_copy; + + /** * ID data is older than edit-mode data. * Set #Main.is_memfile_undo_flush_needed when enabling. */ diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h index 5f6a9ec7b91..17cdb9d6a42 100644 --- a/source/blender/blenkernel/BKE_geometry_set.h +++ b/source/blender/blenkernel/BKE_geometry_set.h @@ -41,7 +41,7 @@ typedef enum GeometryComponentType { void BKE_geometry_set_free(struct GeometrySet *geometry_set); -bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set); +bool BKE_object_has_geometry_set_instances(const struct Object *ob); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 42e9ce82278..bf38294257a 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -31,9 +31,12 @@ #include "BLI_user_counter.hh" #include "BLI_vector_set.hh" +#include "BKE_anonymous_attribute.hh" #include "BKE_attribute_access.hh" #include "BKE_geometry_set.h" +#include "FN_field.hh" + struct Collection; struct Curve; struct CurveEval; @@ -88,11 +91,11 @@ class GeometryComponent { GeometryComponentType type() const; /* Return true when any attribute with this name exists, including built in attributes. */ - bool attribute_exists(const blender::StringRef attribute_name) const; + bool attribute_exists(const blender::bke::AttributeIDRef &attribute_id) 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; + const blender::bke::AttributeIDRef &attribute_id) const; /* Returns true when the geometry component supports this attribute domain. */ bool attribute_domain_supported(const AttributeDomain domain) const; @@ -100,16 +103,17 @@ class GeometryComponent { virtual int attribute_domain_size(const AttributeDomain domain) const; bool attribute_is_builtin(const blender::StringRef attribute_name) const; + bool attribute_is_builtin(const blender::bke::AttributeIDRef &attribute_id) const; /* Get read-only access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ blender::bke::ReadAttributeLookup attribute_try_get_for_read( - const blender::StringRef attribute_name) const; + const blender::bke::AttributeIDRef &attribute_id) 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::WriteAttributeLookup attribute_try_get_for_write( - const blender::StringRef attribute_name); + const blender::bke::AttributeIDRef &attribute_id); /* Get a read-only attribute for the domain based on the given attribute. This can be used to * interpolate from one domain to another. @@ -120,10 +124,10 @@ class GeometryComponent { const AttributeDomain to_domain) const; /* Returns true when the attribute has been deleted. */ - bool attribute_try_delete(const blender::StringRef attribute_name); + bool attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id); /* Returns true when the attribute has been created. */ - bool attribute_try_create(const blender::StringRef attribute_name, + bool attribute_try_create(const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer); @@ -133,7 +137,7 @@ class GeometryComponent { bool attribute_try_create_builtin(const blender::StringRef attribute_name, const AttributeInit &initializer); - blender::Set<std::string> attribute_names() const; + blender::Set<blender::bke::AttributeIDRef> attribute_ids() const; bool attribute_foreach(const AttributeForeachCallback callback) const; virtual bool is_empty() const; @@ -142,7 +146,7 @@ class GeometryComponent { * 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 blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) const; @@ -150,18 +154,18 @@ class GeometryComponent { * 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; + const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) 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; + const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type) const; /* 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 blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value = nullptr) const; @@ -169,14 +173,15 @@ class GeometryComponent { /* 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 + blender::fn::GVArray_Typed<T> attribute_get_for_read( + const blender::bke::AttributeIDRef &attribute_id, + 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); std::unique_ptr varray = this->attribute_get_for_read( - attribute_name, domain, type, &default_value); + attribute_id, domain, type, &default_value); return blender::fn::GVArray_Typed<T>(std::move(varray)); } @@ -191,7 +196,7 @@ class GeometryComponent { * 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 blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value = nullptr); @@ -200,28 +205,30 @@ class GeometryComponent { * 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 blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type); /* Statically typed method corresponding to the equally named generic one. */ template<typename T> blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output( - const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value) + const blender::bke::AttributeIDRef &attribute_id, + const AttributeDomain domain, + const T default_value) { 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(attribute_name, domain, data_type, &default_value); + return this->attribute_try_get_for_output(attribute_id, domain, data_type, &default_value); } /* 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::bke::AttributeIDRef &attribute_id, 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); + return this->attribute_try_get_for_output_only(attribute_id, domain, data_type); } private: @@ -283,6 +290,7 @@ struct GeometrySet { void clear(); + bool owns_direct_data() const; void ensure_owns_direct_data(); /* Utility methods for creation. */ @@ -447,12 +455,14 @@ class InstanceReference { None, Object, Collection, + GeometrySet, }; private: Type type_ = Type::None; /** Depending on the type this is either null, an Object or Collection pointer. */ void *data_ = nullptr; + std::unique_ptr<GeometrySet> geometry_set_; public: InstanceReference() = default; @@ -465,6 +475,19 @@ class InstanceReference { { } + InstanceReference(GeometrySet geometry_set) + : type_(Type::GeometrySet), + geometry_set_(std::make_unique<GeometrySet>(std::move(geometry_set))) + { + } + + InstanceReference(const InstanceReference &src) : type_(src.type_), data_(src.data_) + { + if (src.type_ == Type::GeometrySet) { + geometry_set_ = std::make_unique<GeometrySet>(*src.geometry_set_); + } + } + Type type() const { return type_; @@ -482,14 +505,37 @@ class InstanceReference { return *(Collection *)data_; } + const GeometrySet &geometry_set() const + { + BLI_assert(type_ == Type::GeometrySet); + return *geometry_set_; + } + + bool owns_direct_data() const + { + if (type_ != Type::GeometrySet) { + /* The object and collection instances are not direct data. */ + return true; + } + return geometry_set_->owns_direct_data(); + } + + void ensure_owns_direct_data() + { + if (type_ != Type::GeometrySet) { + return; + } + geometry_set_->ensure_owns_direct_data(); + } + uint64_t hash() const { - return blender::get_default_hash(data_); + return blender::get_default_hash_2(data_, geometry_set_.get()); } friend bool operator==(const InstanceReference &a, const InstanceReference &b) { - return a.data_ == b.data_; + return a.data_ == b.data_ && a.geometry_set_.get() == b.geometry_set_.get(); } }; @@ -529,7 +575,7 @@ class InstancesComponent : public GeometryComponent { void reserve(int min_capacity); void resize(int capacity); - int add_reference(InstanceReference reference); + int add_reference(const InstanceReference &reference); void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1); blender::Span<InstanceReference> references() const; @@ -577,3 +623,78 @@ class VolumeComponent : public GeometryComponent { static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME; }; + +namespace blender::bke { + +class GeometryComponentFieldContext : public fn::FieldContext { + private: + const GeometryComponent &component_; + const AttributeDomain domain_; + + public: + GeometryComponentFieldContext(const GeometryComponent &component, const AttributeDomain domain) + : component_(component), domain_(domain) + { + } + + const GeometryComponent &geometry_component() const + { + return component_; + } + + AttributeDomain domain() const + { + return domain_; + } +}; + +class AttributeFieldInput : public fn::FieldInput { + private: + std::string name_; + + public: + AttributeFieldInput(std::string name, const CPPType &type) + : fn::FieldInput(type, name), name_(std::move(name)) + { + } + + StringRefNull attribute_name() const + { + return name_; + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const override; + + std::string socket_inspection_name() const override; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + +class AnonymousAttributeFieldInput : public fn::FieldInput { + private: + /** + * A strong reference is required to make sure that the referenced attribute is not removed + * automatically. + */ + StrongAnonymousAttributeID anonymous_id_; + + public: + AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, const CPPType &type) + : fn::FieldInput(type, anonymous_id.debug_name()), anonymous_id_(std::move(anonymous_id)) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const override; + + std::string socket_inspection_name() const override; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index 25876296a47..44a0ee30c4c 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -59,9 +59,10 @@ struct AttributeKind { * will contain the highest complexity data type and the highest priority domain among every * attribute with the given name on all of the input components. */ -void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups, - Span<GeometryComponentType> component_types, - const Set<std::string> &ignored_attributes, - Map<std::string, AttributeKind> &r_attributes); +void geometry_set_gather_instances_attribute_info( + Span<GeometryInstanceGroup> set_groups, + Span<GeometryComponentType> component_types, + const Set<std::string> &ignored_attributes, + Map<AttributeIDRef, AttributeKind> &r_attributes); } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 89713e9ad0a..7696b5c0189 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -124,7 +124,10 @@ enum { /** Don't overwrite these flags when reading a file. */ #define G_FLAG_ALL_RUNTIME \ (G_FLAG_SCRIPT_AUTOEXEC | G_FLAG_SCRIPT_OVERRIDE_PREF | G_FLAG_EVENT_SIMULATE | \ - G_FLAG_USERPREF_NO_SAVE_ON_EXIT) + G_FLAG_USERPREF_NO_SAVE_ON_EXIT | \ +\ + /* #BPY_python_reset is responsible for resetting these flags on file load. */ \ + G_FLAG_SCRIPT_AUTOEXEC_FAIL | G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET) /** Flags to read from blend file. */ #define G_FLAG_ALL_READFILE 0 diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index 29e3a74b1b2..d472fd6f02b 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -101,7 +101,7 @@ bool BKE_gpencil_stroke_sample(struct bGPdata *gpd, struct bGPDstroke *gps, const float dist, const bool select); -bool BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, int i, float inf); +bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps, int i, float inf); bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence); bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence); bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence); @@ -151,7 +151,8 @@ void BKE_gpencil_stroke_set_random_color(struct bGPDstroke *gps); void BKE_gpencil_stroke_join(struct bGPDstroke *gps_a, struct bGPDstroke *gps_b, const bool leave_gaps, - const bool fit_thickness); + const bool fit_thickness, + const bool smooth); void BKE_gpencil_stroke_copy_to_keyframes(struct bGPdata *gpd, struct bGPDlayer *gpl, struct bGPDframe *gpf, diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index b0939ec884d..7136a3fd7af 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -45,8 +45,10 @@ enum { IDTYPE_FLAGS_NO_COPY = 1 << 0, /** Indicates that the given IDType does not support linking/appending from a library file. */ IDTYPE_FLAGS_NO_LIBLINKING = 1 << 1, - /** Indicates that the given IDType does not support making a library-linked ID local. */ - IDTYPE_FLAGS_NO_MAKELOCAL = 1 << 2, + /** Indicates that the given IDType should not be directly linked from a library file, but may be + * appended. + * NOTE: Mutually exclusive with `IDTYPE_FLAGS_NO_LIBLINKING`. */ + IDTYPE_FLAGS_ONLY_APPEND = 1 << 2, /** Indicates that the given IDType does not have animation data. */ IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3, }; @@ -283,9 +285,14 @@ const struct IDTypeInfo *BKE_idtype_get_info_from_id(const struct ID *id); const char *BKE_idtype_idcode_to_name(const short idcode); const char *BKE_idtype_idcode_to_name_plural(const short idcode); const char *BKE_idtype_idcode_to_translation_context(const short idcode); -bool BKE_idtype_idcode_is_linkable(const short idcode); + bool BKE_idtype_idcode_is_valid(const short idcode); +bool BKE_idtype_idcode_is_linkable(const short idcode); +bool BKE_idtype_idcode_is_only_appendable(const short idcode); +/* Macro currently, since any linkable IDtype should be localizable. */ +#define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable + short BKE_idtype_idcode_from_name(const char *idtype_name); uint64_t BKE_idtype_idcode_to_idfilter(const short idcode); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index a50faedcc3c..36f57209e33 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -230,20 +230,25 @@ void id_us_plus(struct ID *id); void id_us_min(struct ID *id); void id_fake_user_set(struct ID *id); void id_fake_user_clear(struct ID *id); -void BKE_id_clear_newpoin(struct ID *id); +void BKE_id_newptr_and_tag_clear(struct ID *id); /** Flags to control make local code behavior. */ enum { /** Making that ID local is part of making local a whole library. */ LIB_ID_MAKELOCAL_FULL_LIBRARY = 1 << 0, + /** In case caller code already knows this ID should be made local without copying. */ + LIB_ID_MAKELOCAL_FORCE_LOCAL = 1 << 1, + /** In case caller code already knows this ID should be made local using copying. */ + LIB_ID_MAKELOCAL_FORCE_COPY = 1 << 2, + /* Special type-specific options. */ /** For Objects, do not clear the proxy pointers while making the data-block local. */ LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING = 1 << 16, }; void BKE_lib_id_make_local_generic(struct Main *bmain, struct ID *id, const int flags); -bool BKE_lib_id_make_local(struct Main *bmain, struct ID *id, const bool test, const int flags); +bool BKE_lib_id_make_local(struct Main *bmain, struct ID *id, const int flags); bool id_single_user(struct bContext *C, struct ID *id, struct PointerRNA *ptr, diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index c90a284c204..c70521f9593 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -112,6 +112,7 @@ void BKE_libblock_relink_ex(struct Main *bmain, const short remap_flags) ATTR_NONNULL(1, 2); void BKE_libblock_relink_to_newid(struct ID *id) ATTR_NONNULL(); +void BKE_libblock_relink_to_newid_new(struct Main *bmain, struct ID *id) ATTR_NONNULL(); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index dbcefb8b6d5..b0a8fee1178 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -123,7 +123,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval); /* Performs copy for use during evaluation, * optional referencing original arrays to reduce memory. */ -struct Mesh *BKE_mesh_copy_for_eval(struct Mesh *source, bool reference); +struct Mesh *BKE_mesh_copy_for_eval(const struct Mesh *source, bool reference); /* These functions construct a new Mesh, * contrary to BKE_mesh_to_curve_nurblist which modifies ob itself. */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 06528cd213c..a57281e4478 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -113,6 +113,7 @@ namespace blender { namespace nodes { class NodeMultiFunctionBuilder; class GeoNodeExecParams; +class NodeDeclarationBuilder; } // namespace nodes namespace fn { class CPPType; @@ -122,6 +123,7 @@ class MFDataType; using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder); using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); +using NodeDeclareFunction = void (*)(blender::nodes::NodeDeclarationBuilder &builder); using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); using SocketGetGeometryNodesCPPTypeFunction = const blender::fn::CPPType *(*)(); @@ -131,6 +133,7 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket #else typedef void *NodeMultiFunctionBuildFunction; typedef void *NodeGeometryExecFunction; +typedef void *NodeDeclareFunction; typedef void *SocketGetCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPValueFunction; @@ -334,6 +337,9 @@ typedef struct bNodeType { NodeGeometryExecFunction geometry_node_execute; bool geometry_node_execute_supports_laziness; + /* Declares which sockets the node has. */ + NodeDeclareFunction declare; + /* RNA integration */ ExtensionRNA rna_ext; } bNodeType; @@ -615,6 +621,10 @@ struct bNodeSocket *nodeInsertStaticSocket(struct bNodeTree *ntree, const char *identifier, const char *name); void nodeRemoveSocket(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *sock); +void nodeRemoveSocketEx(struct bNodeTree *ntree, + struct bNode *node, + struct bNodeSocket *sock, + bool do_id_user); void nodeRemoveAllSockets(struct bNodeTree *ntree, struct bNode *node); void nodeModifySocketType(struct bNodeTree *ntree, struct bNode *node, @@ -721,6 +731,8 @@ void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available); int nodeSocketLinkLimit(const struct bNodeSocket *sock); +void nodeDeclarationEnsure(struct bNodeTree *ntree, struct bNode *node); + /* Node Clipboard */ void BKE_node_clipboard_init(const struct bNodeTree *ntree); void BKE_node_clipboard_clear(void); @@ -1245,6 +1257,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree, #define CMP_NODE_DENOISE 324 #define CMP_NODE_EXPOSURE 325 #define CMP_NODE_CRYPTOMATTE 326 +#define CMP_NODE_POSTERIZE 327 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -1401,34 +1414,34 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_EDGE_SPLIT 1001 #define GEO_NODE_TRANSFORM 1002 #define GEO_NODE_BOOLEAN 1003 -#define GEO_NODE_POINT_DISTRIBUTE 1004 -#define GEO_NODE_POINT_INSTANCE 1005 +#define GEO_NODE_LEGACY_POINT_DISTRIBUTE 1004 +#define GEO_NODE_LEGACY_POINT_INSTANCE 1005 #define GEO_NODE_SUBDIVISION_SURFACE 1006 #define GEO_NODE_OBJECT_INFO 1007 -#define GEO_NODE_ATTRIBUTE_RANDOMIZE 1008 -#define GEO_NODE_ATTRIBUTE_MATH 1009 +#define GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE 1008 +#define GEO_NODE_LEGACY_ATTRIBUTE_MATH 1009 #define GEO_NODE_JOIN_GEOMETRY 1010 -#define GEO_NODE_ATTRIBUTE_FILL 1011 -#define GEO_NODE_ATTRIBUTE_MIX 1012 -#define GEO_NODE_ATTRIBUTE_COLOR_RAMP 1013 -#define GEO_NODE_POINT_SEPARATE 1014 -#define GEO_NODE_ATTRIBUTE_COMPARE 1015 -#define GEO_NODE_POINT_ROTATE 1016 -#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017 -#define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018 -#define GEO_NODE_POINT_TRANSLATE 1019 -#define GEO_NODE_POINT_SCALE 1020 -#define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021 -#define GEO_NODE_POINTS_TO_VOLUME 1022 +#define GEO_NODE_LEGACY_ATTRIBUTE_FILL 1011 +#define GEO_NODE_LEGACY_ATTRIBUTE_MIX 1012 +#define GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP 1013 +#define GEO_NODE_LEGACY_POINT_SEPARATE 1014 +#define GEO_NODE_LEGACY_ATTRIBUTE_COMPARE 1015 +#define GEO_NODE_LEGACY_POINT_ROTATE 1016 +#define GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH 1017 +#define GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR 1018 +#define GEO_NODE_LEGACY_POINT_TRANSLATE 1019 +#define GEO_NODE_LEGACY_POINT_SCALE 1020 +#define GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE 1021 +#define GEO_NODE_LEGACY_POINTS_TO_VOLUME 1022 #define GEO_NODE_COLLECTION_INFO 1023 #define GEO_NODE_IS_VIEWPORT 1024 -#define GEO_NODE_ATTRIBUTE_PROXIMITY 1025 +#define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025 #define GEO_NODE_VOLUME_TO_MESH 1026 -#define GEO_NODE_ATTRIBUTE_COMBINE_XYZ 1027 -#define GEO_NODE_ATTRIBUTE_SEPARATE_XYZ 1028 +#define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027 +#define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_MESH_SUBDIVIDE 1029 #define GEO_NODE_ATTRIBUTE_REMOVE 1030 -#define GEO_NODE_ATTRIBUTE_CONVERT 1031 +#define GEO_NODE_LEGACY_ATTRIBUTE_CONVERT 1031 #define GEO_NODE_MESH_PRIMITIVE_CUBE 1032 #define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1033 #define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1034 @@ -1437,28 +1450,28 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MESH_PRIMITIVE_CONE 1037 #define GEO_NODE_MESH_PRIMITIVE_LINE 1038 #define GEO_NODE_MESH_PRIMITIVE_GRID 1039 -#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040 -#define GEO_NODE_ATTRIBUTE_CLAMP 1041 +#define GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE 1040 +#define GEO_NODE_LECAGY_ATTRIBUTE_CLAMP 1041 #define GEO_NODE_BOUNDING_BOX 1042 #define GEO_NODE_SWITCH 1043 -#define GEO_NODE_ATTRIBUTE_TRANSFER 1044 +#define GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER 1044 #define GEO_NODE_CURVE_TO_MESH 1045 -#define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046 +#define GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP 1046 #define GEO_NODE_CURVE_RESAMPLE 1047 #define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048 -#define GEO_NODE_MATERIAL_ASSIGN 1049 +#define GEO_NODE_LEGACY_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_MATERIAL_REPLACE 1051 -#define GEO_NODE_MESH_TO_CURVE 1052 -#define GEO_NODE_DELETE_GEOMETRY 1053 +#define GEO_NODE_LEGACY_MESH_TO_CURVE 1052 +#define GEO_NODE_LEGACY_DELETE_GEOMETRY 1053 #define GEO_NODE_CURVE_LENGTH 1054 -#define GEO_NODE_SELECT_BY_MATERIAL 1055 +#define GEO_NODE_LEGACY_SELECT_BY_MATERIAL 1055 #define GEO_NODE_CONVEX_HULL 1056 #define GEO_NODE_CURVE_TO_POINTS 1057 -#define GEO_NODE_CURVE_REVERSE 1058 +#define GEO_NODE_LEGACY_CURVE_REVERSE 1058 #define GEO_NODE_SEPARATE_COMPONENTS 1059 -#define GEO_NODE_CURVE_SUBDIVIDE 1060 -#define GEO_NODE_RAYCAST 1061 +#define GEO_NODE_LEGACY_CURVE_SUBDIVIDE 1060 +#define GEO_NODE_LEGACY_RAYCAST 1061 #define GEO_NODE_CURVE_PRIMITIVE_STAR 1062 #define GEO_NODE_CURVE_PRIMITIVE_SPIRAL 1063 #define GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER 1064 @@ -1469,9 +1482,17 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 #define GEO_NODE_CURVE_TRIM 1071 -#define GEO_NODE_CURVE_SET_HANDLES 1072 -#define GEO_NODE_CURVE_SPLINE_TYPE 1073 -#define GEO_NODE_CURVE_SELECT_HANDLES 1074 +#define GEO_NODE_LEGACY_CURVE_SET_HANDLES 1072 +#define GEO_NODE_LEGACY_CURVE_SPLINE_TYPE 1073 +#define GEO_NODE_LEGACY_CURVE_SELECT_HANDLES 1074 +#define GEO_NODE_CURVE_FILL 1075 +#define GEO_NODE_INPUT_POSITION 1076 +#define GEO_NODE_SET_POSITION 1077 +#define GEO_NODE_INPUT_INDEX 1078 +#define GEO_NODE_INPUT_NORMAL 1079 +#define GEO_NODE_ATTRIBUTE_CAPTURE 1080 +#define GEO_NODE_MATERIAL_SELECTION 1081 +#define GEO_NODE_MATERIAL_ASSIGN 1082 /** \} */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index a823602e341..0e153c5a82a 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -458,9 +458,13 @@ void BKE_object_modifiers_lib_link_common(void *userData, struct ID **idpoin, int cb_flag); +void BKE_object_replace_data_on_shallow_copy(struct Object *ob, struct ID *new_data); + struct PartEff; struct PartEff *BKE_object_do_version_give_parteff_245(struct Object *ob); +bool BKE_object_supports_material_slots(struct Object *ob); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_packedFile.h b/source/blender/blenkernel/BKE_packedFile.h index c45a0bc857d..8cb0c78d9aa 100644 --- a/source/blender/blenkernel/BKE_packedFile.h +++ b/source/blender/blenkernel/BKE_packedFile.h @@ -74,6 +74,12 @@ char *BKE_packedfile_unpack_to_file(struct ReportList *reports, const char *local_name, struct PackedFile *pf, enum ePF_FileStatus how); +char *BKE_packedfile_unpack(struct Main *bmain, + struct ReportList *reports, + struct ID *id, + const char *orig_file_path, + struct PackedFile *pf, + enum ePF_FileStatus how); int BKE_packedfile_unpack_vfont(struct Main *bmain, struct ReportList *reports, struct VFont *vfont, diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index fc145f1ddf1..0fbf39a52fa 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -131,6 +131,11 @@ class Spline { virtual void transform(const blender::float4x4 &matrix); /** + * Change the direction of the spline (switch the start and end) without changing its shape. + */ + void reverse(); + + /** * 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. */ @@ -210,6 +215,7 @@ class Spline { virtual void correct_end_tangents() const = 0; virtual void copy_settings(Spline &dst) const = 0; virtual void copy_data(Spline &dst) const = 0; + virtual void reverse_impl() = 0; }; /** @@ -353,6 +359,9 @@ class BezierSpline final : public Spline { void correct_end_tangents() const final; void copy_settings(Spline &dst) const final; void copy_data(Spline &dst) const final; + + protected: + void reverse_impl() override; }; /** @@ -469,6 +478,7 @@ class NURBSpline final : public Spline { void correct_end_tangents() const final; void copy_settings(Spline &dst) const final; void copy_data(Spline &dst) const final; + void reverse_impl() override; void calculate_knots() const; blender::Span<BasisCache> calculate_basis_cache() const; @@ -519,6 +529,7 @@ class PolySpline final : public Spline { void correct_end_tangents() const final; void copy_settings(Spline &dst) const final; void copy_data(Spline &dst) const final; + void reverse_impl() override; }; /** diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 64cfbd8f491..0b082bf1c5a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -76,6 +76,7 @@ set(SRC intern/anim_path.c intern/anim_sys.c intern/anim_visualization.c + intern/anonymous_attribute.cc intern/appdir.c intern/armature.c intern/armature_deform.c @@ -185,7 +186,7 @@ set(SRC intern/mball_tessellate.c intern/mesh.c intern/mesh_boolean_convert.cc - intern/mesh_convert.c + intern/mesh_convert.cc intern/mesh_evaluate.cc intern/mesh_fair.cc intern/mesh_iterators.c @@ -295,6 +296,8 @@ set(SRC BKE_anim_path.h BKE_anim_visualization.h BKE_animsys.h + BKE_anonymous_attribute.h + BKE_anonymous_attribute.hh BKE_appdir.h BKE_armature.h BKE_armature.hh diff --git a/source/blender/blenkernel/intern/action_bones.cc b/source/blender/blenkernel/intern/action_bones.cc index b8d185e6a81..1f2b7360b70 100644 --- a/source/blender/blenkernel/intern/action_bones.cc +++ b/source/blender/blenkernel/intern/action_bones.cc @@ -28,6 +28,7 @@ #include "DNA_action_types.h" #include "DNA_anim_types.h" +#include "DNA_armature_types.h" #include "MEM_guardedalloc.h" @@ -36,12 +37,11 @@ namespace blender::bke { void BKE_action_find_fcurves_with_bones(const bAction *action, FoundFCurveCallback callback) { LISTBASE_FOREACH (FCurve *, fcu, &action->curves) { - char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (!bone_name) { + char bone_name[MAXBONENAME]; + if (!BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { continue; } callback(fcu, bone_name); - MEM_freeN(bone_name); } } diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc new file mode 100644 index 00000000000..67611053d83 --- /dev/null +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -0,0 +1,118 @@ +/* + * 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_anonymous_attribute.hh" + +using namespace blender::bke; + +/** + * A struct that identifies an attribute. It's lifetime is managed by an atomic reference count. + * + * Additionally, this struct can be strongly or weakly owned. The difference is that strong + * ownership means that attributes with this id will be kept around. Weak ownership just makes sure + * that the struct itself stays alive, but corresponding attributes might still be removed + * automatically. + */ +struct AnonymousAttributeID { + /** + * Total number of references to this attribute id. Once this reaches zero, the struct can be + * freed. This includes strong and weak references. + */ + mutable std::atomic<int> refcount_tot = 0; + + /** + * Number of strong references to this attribute id. When this is zero, the corresponding + * attributes can be removed from geometries automatically. + */ + mutable std::atomic<int> refcount_strong = 0; + + /** + * Only used to identify this struct in a debugging session. + */ + std::string debug_name; + + /** + * Unique name of the this attribute id during the current session. + */ + std::string internal_name; +}; + +/** Every time this function is called, it outputs a different name. */ +static std::string get_new_internal_name() +{ + static std::atomic<int> index = 0; + const int next_index = index.fetch_add(1); + return "anonymous_attribute_" + std::to_string(next_index); +} + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name) +{ + AnonymousAttributeID *anonymous_id = new AnonymousAttributeID(); + anonymous_id->debug_name = debug_name; + anonymous_id->internal_name = get_new_internal_name(); + anonymous_id->refcount_tot.store(1); + return anonymous_id; +} + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name) +{ + AnonymousAttributeID *anonymous_id = new AnonymousAttributeID(); + anonymous_id->debug_name = debug_name; + anonymous_id->internal_name = get_new_internal_name(); + anonymous_id->refcount_tot.store(1); + anonymous_id->refcount_strong.store(1); + return anonymous_id; +} + +bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->refcount_strong.load() >= 1; +} + +void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_tot.fetch_add(1); +} + +void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_tot.fetch_add(1); + anonymous_id->refcount_strong.fetch_add(1); +} + +void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id) +{ + const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1; + if (new_refcount == 0) { + delete anonymous_id; + } +} + +void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_strong.fetch_sub(1); + BKE_anonymous_attribute_id_decrement_weak(anonymous_id); +} + +const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->debug_name.c_str(); +} + +const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->internal_name.c_str(); +} diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 99eb064d061..2994563175f 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -176,9 +176,9 @@ class BKE_armature_find_selected_bones_test : public testing::Test { bone2.childbase = {nullptr, nullptr}; bone3.childbase = {nullptr, nullptr}; - BLI_addtail(&arm.bonebase, &bone1); // bone1 is root bone - BLI_addtail(&arm.bonebase, &bone2); // bone2 is root bone - BLI_addtail(&bone2.childbase, &bone3); // bone3 has bone2 as parent + BLI_addtail(&arm.bonebase, &bone1); /* bone1 is root bone. */ + BLI_addtail(&arm.bonebase, &bone2); /* bone2 is root bone. */ + BLI_addtail(&bone2.childbase, &bone3); /* bone3 has bone2 as parent. */ /* Make sure the armature & its bones are visible, to make them selectable. */ arm.layer = bone1.layer = bone2.layer = bone3.layer = 1; diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index e9444cf88a6..ee8ef5e97f7 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -51,7 +51,7 @@ typedef struct DomainInfo { int length; } DomainInfo; -static void get_domains(ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) +static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) { memset(info, 0, sizeof(DomainInfo) * ATTR_DOMAIN_NUM); @@ -223,6 +223,29 @@ bool BKE_id_attribute_remove(ID *id, CustomDataLayer *layer, ReportList *reports return true; } +CustomDataLayer *BKE_id_attribute_find(const ID *id, + const char *name, + const int type, + const AttributeDomain domain) +{ + DomainInfo info[ATTR_DOMAIN_NUM]; + get_domains(id, info); + + CustomData *customdata = info[domain].customdata; + if (customdata == NULL) { + return NULL; + } + + for (int i = 0; i < customdata->totlayer; i++) { + CustomDataLayer *layer = &customdata->layers[i]; + if (layer->type == type && STREQ(layer->name, name)) { + return layer; + } + } + + return NULL; +} + int BKE_id_attributes_length(ID *id, const CustomDataMask mask) { DomainInfo info[ATTR_DOMAIN_NUM]; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index aa0af294bc3..8c4f87be91f 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -32,6 +32,8 @@ #include "BLI_float2.hh" #include "BLI_span.hh" +#include "BLT_translation.h" + #include "CLG_log.h" #include "NOD_type_conversions.hh" @@ -44,6 +46,8 @@ using blender::float3; using blender::Set; using blender::StringRef; using blender::StringRefNull; +using blender::bke::AttributeIDRef; +using blender::bke::OutputAttribute; using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; @@ -186,7 +190,7 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains) void OutputAttribute::save() { save_has_been_called_ = true; - if (optional_span_varray_.has_value()) { + if (optional_span_varray_) { optional_span_varray_->save(); } if (save_) { @@ -334,8 +338,20 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return data != nullptr; } +static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, + const AttributeIDRef &attribute_id) +{ + if (!attribute_id) { + return false; + } + if (attribute_id.is_anonymous()) { + return layer.anonymous_id == &attribute_id.anonymous_id(); + } + return layer.name == attribute_id.name(); +} + ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( - const GeometryComponent &component, const StringRef attribute_name) const + const GeometryComponent &component, const AttributeIDRef &attribute_id) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); if (custom_data == nullptr) { @@ -343,7 +359,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( } const int domain_size = component.attribute_domain_size(domain_); for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { - if (layer.name != attribute_name) { + if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } const CustomDataType data_type = (CustomDataType)layer.type; @@ -368,7 +384,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( } WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( - GeometryComponent &component, const StringRef attribute_name) const + GeometryComponent &component, const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -376,10 +392,17 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( } const int domain_size = component.attribute_domain_size(domain_); for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { - if (layer.name != attribute_name) { + if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } - CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size); + if (attribute_id.is_named()) { + CustomData_duplicate_referenced_layer_named( + custom_data, layer.type, layer.name, domain_size); + } + else { + CustomData_duplicate_referenced_layer_anonymous( + custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); + } const CustomDataType data_type = (CustomDataType)layer.type; switch (data_type) { case CD_PROP_FLOAT: @@ -402,7 +425,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( } bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -411,7 +434,8 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, const int domain_size = component.attribute_domain_size(domain_); for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; - if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) { + if (this->type_is_supported((CustomDataType)layer.type) && + custom_data_layer_matches_attribute_id(layer, attribute_id)) { CustomData_free_layer(custom_data, layer.type, domain_size, i); return true; } @@ -419,24 +443,39 @@ 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) +static void *add_generic_custom_data_layer(CustomData &custom_data, + const CustomDataType data_type, + const eCDAllocType alloctype, + void *layer_data, + const int domain_size, + const AttributeIDRef &attribute_id) { - char attribute_name_c[MAX_NAME]; - attribute_name.copy(attribute_name_c); + if (attribute_id.is_named()) { + char attribute_name_c[MAX_NAME]; + attribute_id.name().copy(attribute_name_c); + return CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + } + const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); + return CustomData_add_layer_anonymous( + &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); +} +static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, + 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_named( - &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); 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); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); if (data == nullptr) { return false; } @@ -446,8 +485,8 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr } 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); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -461,7 +500,7 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr } bool CustomDataAttributeProvider::try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const @@ -477,13 +516,13 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { return false; } } const int domain_size = component.attribute_domain_size(domain_); - add_named_custom_data_layer_from_attribute_init( - attribute_name, *custom_data, data_type, domain_size, initializer); + add_custom_data_layer_from_attribute_init( + attribute_id, *custom_data, data_type, domain_size, initializer); return true; } @@ -498,7 +537,14 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com const CustomDataType data_type = (CustomDataType)layer.type; if (this->type_is_supported(data_type)) { AttributeMetaData meta_data{domain_, data_type}; - if (!callback(layer.name, meta_data)) { + AttributeIDRef attribute_id; + if (layer.anonymous_id != nullptr) { + attribute_id = layer.anonymous_id; + } + else { + attribute_id = layer.name; + } + if (!callback(attribute_id, meta_data)) { return false; } } @@ -507,7 +553,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com } ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( - const GeometryComponent &component, const StringRef attribute_name) const + const GeometryComponent &component, const AttributeIDRef &attribute_id) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); if (custom_data == nullptr) { @@ -515,7 +561,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( } for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); return {as_read_attribute_(layer.data, domain_size), domain_}; } @@ -525,7 +571,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( } WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( - GeometryComponent &component, const StringRef attribute_name) const + GeometryComponent &component, const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -533,7 +579,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( } for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( @@ -549,7 +595,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( } bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -558,7 +604,7 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); CustomData_free_layer(custom_data, stored_type_, domain_size, i); custom_data_access_.update_custom_data_pointers(component); @@ -627,11 +673,11 @@ CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes return *this; } -std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const +std::optional<GSpan> CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id) const { BLI_assert(size_ != 0); for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type); BLI_assert(cpp_type != nullptr); return GSpan(*cpp_type, layer.data, size_); @@ -645,13 +691,13 @@ std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) co * value if the attribute doesn't exist. If no default value is provided, the default value for the * type will be used. */ -GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name, +GVArrayPtr CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id, const CustomDataType data_type, const void *default_value) const { const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); - std::optional<GSpan> attribute = this->get_for_read(name); + std::optional<GSpan> attribute = this->get_for_read(attribute_id); if (!attribute) { const int domain_size = this->size_; return std::make_unique<GVArray_For_SingleValue>( @@ -666,12 +712,12 @@ GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name, return conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), *type); } -std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name) +std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const AttributeIDRef &attribute_id) { /* If this assert hits, it most likely means that #reallocate was not called at some point. */ BLI_assert(size_ != 0); for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) { - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type); BLI_assert(cpp_type != nullptr); return GMutableSpan(*cpp_type, layer.data, size_); @@ -680,30 +726,29 @@ std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef return {}; } -bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type) +bool CustomDataAttributes::create(const AttributeIDRef &attribute_id, + const CustomDataType data_type) { - char name_c[MAX_NAME]; - name.copy(name_c); - void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c); + void *result = add_generic_custom_data_layer( + data, data_type, CD_DEFAULT, nullptr, size_, attribute_id); return result != nullptr; } -bool CustomDataAttributes::create_by_move(const blender::StringRef name, +bool CustomDataAttributes::create_by_move(const AttributeIDRef &attribute_id, const CustomDataType data_type, void *buffer) { - char name_c[MAX_NAME]; - name.copy(name_c); - void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c); + void *result = add_generic_custom_data_layer( + data, data_type, CD_ASSIGN, buffer, size_, attribute_id); return result != nullptr; } -bool CustomDataAttributes::remove(const blender::StringRef name) +bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id) { bool result = false; for (const int i : IndexRange(data.totlayer)) { const CustomDataLayer &layer = data.layers[i]; - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { CustomData_free_layer(&data, layer.type, size_, i); result = true; } @@ -722,7 +767,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { AttributeMetaData meta_data{domain, (CustomDataType)layer.type}; - if (!callback(layer.name, meta_data)) { + AttributeIDRef attribute_id; + if (layer.anonymous_id != nullptr) { + attribute_id = layer.anonymous_id; + } + else { + attribute_id = layer.name; + } + if (!callback(attribute_id, meta_data)) { return false; } } @@ -765,22 +817,30 @@ bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_ return providers->builtin_attribute_providers().contains_as(attribute_name); } +bool GeometryComponent::attribute_is_builtin(const AttributeIDRef &attribute_id) const +{ + /* Anonymous attributes cannot be built-in. */ + return attribute_id.is_named() && this->attribute_is_builtin(attribute_id.name()); +} + blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - 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), builtin_provider->domain()}; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; + } } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name); + ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_id); if (attribute) { return attribute; } @@ -800,21 +860,23 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_dom } blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write( - const StringRef attribute_name) + const AttributeIDRef &attribute_id) { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - 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), builtin_provider->domain()}; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; + } } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name); + WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_id); if (attribute) { return attribute; } @@ -822,53 +884,57 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ return {}; } -bool GeometryComponent::attribute_try_delete(const StringRef attribute_name) +bool GeometryComponent::attribute_try_delete(const AttributeIDRef &attribute_id) { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider != nullptr) { - return builtin_provider->try_delete(*this); + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return builtin_provider->try_delete(*this); + } } bool success = false; for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - success = dynamic_provider->try_delete(*this, attribute_name) || success; + success = dynamic_provider->try_delete(*this, attribute_id) || success; } return success; } -bool GeometryComponent::attribute_try_create(const StringRef attribute_name, +bool GeometryComponent::attribute_try_create(const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) { using namespace blender::bke; - if (attribute_name.is_empty()) { + if (!attribute_id) { 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) { - if (builtin_provider->domain() != domain) { - return false; - } - if (builtin_provider->data_type() != data_type) { - return false; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + if (builtin_provider->domain() != domain) { + return false; + } + if (builtin_provider->data_type() != data_type) { + return false; + } + return builtin_provider->try_create(*this, initializer); } - 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, initializer)) { + if (dynamic_provider->try_create(*this, attribute_id, domain, data_type, initializer)) { return true; } } @@ -894,13 +960,14 @@ bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef at return builtin_provider->try_create(*this, initializer); } -Set<std::string> GeometryComponent::attribute_names() const +Set<AttributeIDRef> GeometryComponent::attribute_ids() const { - Set<std::string> attributes; - this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { - attributes.add(name); - return true; - }); + Set<AttributeIDRef> attributes; + this->attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + attributes.add(attribute_id); + return true; + }); return attributes; } @@ -931,9 +998,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac } for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) { const bool continue_loop = provider->foreach_attribute( - *this, [&](StringRefNull name, const AttributeMetaData &meta_data) { - if (handled_attribute_names.add(name)) { - return callback(name, meta_data); + *this, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_anonymous() || handled_attribute_names.add(attribute_id.name())) { + return callback(attribute_id, meta_data); } return true; }); @@ -945,9 +1012,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac return true; } -bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const +bool GeometryComponent::attribute_exists(const AttributeIDRef &attribute_id) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (attribute) { return true; } @@ -955,16 +1022,17 @@ bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name } std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { 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; - }); + this->attribute_foreach( + [&](const AttributeIDRef ¤t_attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id == current_attribute_id) { + result = meta_data; + return false; + } + return true; + }); return result; } @@ -977,11 +1045,11 @@ static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type( } std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1007,13 +1075,13 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_r } std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name, const AttributeDomain domain) const + const AttributeIDRef &attribute_id, const AttributeDomain domain) const { if (!this->attribute_domain_supported(domain)) { return {}; } - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1026,9 +1094,9 @@ std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_ } blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const blender::StringRef attribute_name, const CustomDataType data_type) const + const AttributeIDRef &attribute_id, const CustomDataType data_type) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1043,13 +1111,13 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( } std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read( - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value) const { std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read( - attribute_name, domain, data_type); + attribute_id, domain, data_type); if (varray) { return varray; } @@ -1065,15 +1133,22 @@ class GVMutableAttribute_For_OutputAttribute : public blender::fn::GVMutableArray_For_GMutableSpan { public: GeometryComponent *component; - std::string final_name; + std::string attribute_name; + blender::bke::WeakAnonymousAttributeID anonymous_attribute_id; 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)) + const AttributeIDRef &attribute_id) + : blender::fn::GVMutableArray_For_GMutableSpan(data), component(&component) { + if (attribute_id.is_named()) { + this->attribute_name = attribute_id.name(); + } + else { + const AnonymousAttributeID *anonymous_id = &attribute_id.anonymous_id(); + BKE_anonymous_attribute_id_increment_weak(anonymous_id); + this->anonymous_attribute_id = blender::bke::WeakAnonymousAttributeID{anonymous_id}; + } } ~GVMutableAttribute_For_OutputAttribute() override @@ -1083,7 +1158,7 @@ class GVMutableAttribute_For_OutputAttribute } }; -static void save_output_attribute(blender::bke::OutputAttribute &output_attribute) +static void save_output_attribute(OutputAttribute &output_attribute) { using namespace blender; using namespace blender::fn; @@ -1093,21 +1168,28 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray()); GeometryComponent &component = *varray.component; - const StringRefNull name = varray.final_name; + AttributeIDRef attribute_id; + if (!varray.attribute_name.empty()) { + attribute_id = varray.attribute_name; + } + else { + attribute_id = varray.anonymous_attribute_id.extract(); + } const AttributeDomain domain = output_attribute.domain(); const CustomDataType data_type = output_attribute.custom_data_type(); const CPPType &cpp_type = output_attribute.cpp_type(); - 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()); + component.attribute_try_delete(attribute_id); + if (!component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault())) { + if (!varray.attribute_name.empty()) { + CLOG_WARN(&LOG, + "Could not create the '%s' attribute with type '%s'.", + varray.attribute_name.c_str(), + cpp_type.name().c_str()); + } return; } - WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name); + WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer); for (const int i : IndexRange(varray.size())) { varray.get(i, buffer); @@ -1115,19 +1197,18 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut } } -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) +static OutputAttribute create_output_attribute(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const CustomDataType data_type, + const bool ignore_old_values, + const void *default_value) { using namespace blender; using namespace blender::fn; using namespace blender::bke; - if (attribute_name.is_empty()) { + if (!attribute_id) { return {}; } @@ -1135,7 +1216,8 @@ static blender::bke::OutputAttribute create_output_attribute( BLI_assert(cpp_type != nullptr); const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - if (component.attribute_is_builtin(attribute_name)) { + if (component.attribute_is_builtin(attribute_id)) { + const StringRef attribute_name = attribute_id.name(); WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); if (!attribute) { if (default_value) { @@ -1169,18 +1251,18 @@ static blender::bke::OutputAttribute create_output_attribute( const int domain_size = component.attribute_domain_size(domain); - WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); 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)); + attribute_id, domain, data_type, AttributeInitVArray(&default_varray)); } else { - component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault()); + component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); } - attribute = component.attribute_try_get_for_write(attribute_name); + attribute = component.attribute_try_get_for_write(attribute_id); if (!attribute) { /* Can't create the attribute. */ return {}; @@ -1202,28 +1284,104 @@ static blender::bke::OutputAttribute create_output_attribute( 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); + attribute_id, 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); + GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id); return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } -blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output( - const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) +OutputAttribute GeometryComponent::attribute_try_get_for_output(const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value) { - return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value); + return create_output_attribute(*this, attribute_id, domain, data_type, false, default_value); } -blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only( - const blender::StringRef attribute_name, +OutputAttribute GeometryComponent::attribute_try_get_for_output_only( + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) { - return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr); + return create_output_attribute(*this, attribute_id, domain, data_type, true, nullptr); +} + +namespace blender::bke { + +const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const +{ + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast<const GeometryComponentFieldContext *>(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + const CustomDataType data_type = cpp_type_to_custom_data_type(*type_); + GVArrayPtr attribute = component.attribute_try_get_for_read(name_, domain, data_type); + if (attribute) { + return scope.add(std::move(attribute)); + } + } + return nullptr; +} + +std::string AttributeFieldInput::socket_inspection_name() const +{ + std::stringstream ss; + ss << TIP_("Attribute: ") << name_; + return ss.str(); +} + +uint64_t AttributeFieldInput::hash() const +{ + return get_default_hash_2(name_, type_); +} + +bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) { + return name_ == other_typed->name_ && type_ == other_typed->type_; + } + return false; } + +const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( + const fn::FieldContext &context, IndexMask UNUSED(mask), ResourceScope &scope) const +{ + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast<const GeometryComponentFieldContext *>(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + const CustomDataType data_type = cpp_type_to_custom_data_type(*type_); + GVArrayPtr attribute = component.attribute_try_get_for_read( + anonymous_id_.get(), domain, data_type); + return scope.add(std::move(attribute)); + } + return nullptr; +} + +std::string AnonymousAttributeFieldInput::socket_inspection_name() const +{ + std::stringstream ss; + ss << TIP_("Anonymous Attribute: ") << debug_name_; + return ss.str(); +} + +uint64_t AnonymousAttributeFieldInput::hash() const +{ + return get_default_hash_2(anonymous_id_.get(), type_); +} + +bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + if (const AnonymousAttributeFieldInput *other_typed = + dynamic_cast<const AnonymousAttributeFieldInput *>(&other)) { + return anonymous_id_.get() == other_typed->anonymous_id_.get() && type_ == other_typed->type_; + } + return false; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index b3a795faa30..261cb26d4e5 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -116,12 +116,13 @@ class BuiltinAttributeProvider { class DynamicAttributesProvider { public: virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const = 0; + const AttributeIDRef &attribute_id) 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; + const AttributeIDRef &attribute_id) const = 0; + virtual bool try_delete(GeometryComponent &component, + const AttributeIDRef &attribute_id) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), - const StringRef UNUSED(attribute_name), + const AttributeIDRef &UNUSED(attribute_id), const AttributeDomain UNUSED(domain), const CustomDataType UNUSED(data_type), const AttributeInit &UNUSED(initializer)) const @@ -154,15 +155,15 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { } ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) const final; WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) const final; - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; bool try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const final; @@ -231,10 +232,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { } ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) 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; + const AttributeIDRef &attribute_id) const final; + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final; diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 97a54f289ee..97f8bddc043 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -362,7 +362,9 @@ void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *use DATA_SWAP(app_flag); /* We could add others. */ - FLAG_SWAP(uiflag, int, USER_SAVE_PROMPT); + FLAG_SWAP(uiflag, int, USER_SAVE_PROMPT | USER_SPLASH_DISABLE | USER_SHOW_GIZMO_NAVIGATE); + + DATA_SWAP(ui_scale); #undef SWAP_TYPELESS #undef DATA_SWAP diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 050c5f75e41..0f3442ca0a9 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -146,8 +146,16 @@ static void brush_free_data(ID *id) static void brush_make_local(Main *bmain, ID *id, const int flags) { + if (!ID_IS_LINKED(id)) { + return; + } + Brush *brush = (Brush *)id; const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; + bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + BLI_assert(force_copy == false || force_copy != force_local); + bool is_local = false, is_lib = false; /* - only lib users: do nothing (unless force_local is set) @@ -155,36 +163,46 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) * - mixed: make copy */ - if (!ID_IS_LINKED(brush)) { - return; - } - if (brush->clone.image) { /* Special case: ima always local immediately. Clone image should only have one user anyway. */ - BKE_lib_id_make_local(bmain, &brush->clone.image->id, false, 0); + /* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided + * in IDType callbacks. Higher-level ID management code usually does not expect such things and + * does not deal properly with it. */ + /* NOTE: assert below ensures that the comment above is valid, and that that exception is + * acceptable for the time being. */ + BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0); + BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL); + } + + if (!force_local && !force_copy) { + BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib); + if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; + } + } } - BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib); + if (force_local) { + BKE_lib_id_clear_library_data(bmain, &brush->id); + BKE_lib_id_expand_local(bmain, &brush->id); - if (lib_local || is_local) { - if (!is_lib) { - BKE_lib_id_clear_library_data(bmain, &brush->id); - BKE_lib_id_expand_local(bmain, &brush->id); - - /* enable fake user by default */ - id_fake_user_set(&brush->id); - } - else { - Brush *brush_new = (Brush *)BKE_id_copy(bmain, &brush->id); /* Ensures FAKE_USER is set */ + /* enable fake user by default */ + id_fake_user_set(&brush->id); + } + else if (force_copy) { + Brush *brush_new = (Brush *)BKE_id_copy(bmain, &brush->id); /* Ensures FAKE_USER is set */ - brush_new->id.us = 0; + brush_new->id.us = 0; - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(brush, brush_new); + /* setting newid is mandatory for complex make_lib_local logic... */ + ID_NEW_SET(brush, brush_new); - if (!lib_local) { - BKE_libblock_remap(bmain, brush, brush_new, ID_REMAP_SKIP_INDIRECT_USAGE); - } + if (!lib_local) { + BKE_libblock_remap(bmain, brush, brush_new, ID_REMAP_SKIP_INDIRECT_USAGE); } } } @@ -1181,7 +1199,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_strength = 0.3f; brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_flag = GP_SCULPT_FLAG_SMOOTH_PRESSURE; + brush->gpencil_settings->sculpt_flag = GP_SCULPT_FLAGMODE_APPLY_THICKNESS; brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; break; @@ -1195,7 +1213,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_strength = 0.3f; brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_flag = GP_SCULPT_FLAG_SMOOTH_PRESSURE; brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; break; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 72f14d94833..b9b15eba6a4 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -3900,7 +3900,11 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar unit_m4(targetMatrix); INIT_MINMAX(curveMin, curveMax); - /* XXX(campbell): don't think this is good calling this here. */ + /* XXX(@campbellbarton): don't think this is good calling this here because + * the other object's data is lazily initializing bounding-box information. + * This could cause issues when evaluating from a thread. + * If the depsgraph ensures the bound-box is always available, a code-path could + * be used that doesn't lazy initialize to avoid thread safety issues in the future. */ BKE_object_minmax(ct->tar, curveMin, curveMax, true); /* get targetmatrix */ diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 7763bb9ca08..c235a1bbb6a 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -655,6 +655,11 @@ void CTX_data_pointer_set(bContextDataResult *result, ID *id, StructRNA *type, v RNA_pointer_create(id, type, data, &result->ptr); } +void CTX_data_pointer_set_ptr(bContextDataResult *result, const PointerRNA *ptr) +{ + result->ptr = *ptr; +} + void CTX_data_id_list_add(bContextDataResult *result, ID *id) { CollectionPointerLink *link = MEM_callocN(sizeof(CollectionPointerLink), "CTX_data_id_list_add"); @@ -671,6 +676,14 @@ void CTX_data_list_add(bContextDataResult *result, ID *id, StructRNA *type, void BLI_addtail(&result->list, link); } +void CTX_data_list_add_ptr(bContextDataResult *result, const PointerRNA *ptr) +{ + CollectionPointerLink *link = MEM_callocN(sizeof(CollectionPointerLink), "CTX_data_list_add"); + link->ptr = *ptr; + + BLI_addtail(&result->list, link); +} + int ctx_data_list_count(const bContext *C, int (*func)(const bContext *, ListBase *)) { ListBase list; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 397838e6904..f22c3b13efc 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -5269,6 +5269,8 @@ void BKE_curve_transform_ex(Curve *cu, BezTriple *bezt; int i; + const bool is_uniform_scaled = is_uniform_scaled_m4(mat); + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { if (nu->type == CU_BEZIER) { i = nu->pntsu; @@ -5279,6 +5281,11 @@ void BKE_curve_transform_ex(Curve *cu, if (do_props) { bezt->radius *= unit_scale; } + if (!is_uniform_scaled) { + if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { + bezt->h1 = bezt->h2 = HD_ALIGN; + } + } } BKE_nurb_handles_calc(nu); } diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 5c18f6f3807..1c4f9c5a6ab 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -25,6 +25,7 @@ #include "DNA_curve_types.h" +#include "BKE_anonymous_attribute.hh" #include "BKE_curve.h" #include "BKE_spline.hh" @@ -37,6 +38,7 @@ using blender::MutableSpan; using blender::Span; using blender::StringRefNull; using blender::Vector; +using blender::bke::AttributeIDRef; blender::Span<SplinePtr> CurveEval::splines() const { @@ -330,13 +332,13 @@ void CurveEval::assert_valid_point_attributes() const return; } const int layer_len = splines_.first()->attributes.data.totlayer; - Map<StringRefNull, AttributeMetaData> map; + Map<AttributeIDRef, AttributeMetaData> map; for (const SplinePtr &spline : splines_) { BLI_assert(spline->attributes.data.totlayer == layer_len); spline->attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { map.add_or_modify( - name, + attribute_id, [&](AttributeMetaData *map_data) { /* All unique attribute names should be added on the first spline. */ BLI_assert(spline == splines_.first()); diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 1a3200a9b6c..ad2d5d267d5 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -46,6 +46,7 @@ #include "BLT_translation.h" +#include "BKE_anonymous_attribute.h" #include "BKE_customdata.h" #include "BKE_customdata_file.h" #include "BKE_deform.h" @@ -2127,6 +2128,11 @@ bool CustomData_merge(const struct CustomData *source, if (flag & CD_FLAG_NOCOPY) { continue; } + if (layer->anonymous_id && + !BKE_anonymous_attribute_id_has_strong_references(layer->anonymous_id)) { + /* This attribute is not referenced anymore, so it can be treated as if it didn't exist. */ + continue; + } if (!(mask & CD_TYPE_AS_MASK(type))) { continue; } @@ -2166,6 +2172,11 @@ bool CustomData_merge(const struct CustomData *source, newlayer->active_mask = lastmask; newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY); changed = true; + + if (layer->anonymous_id != NULL) { + BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id); + newlayer->anonymous_id = layer->anonymous_id; + } } } @@ -2206,6 +2217,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, int totelem) { const LayerTypeInfo *typeInfo; + if (layer->anonymous_id != NULL) { + BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id); + layer->anonymous_id = NULL; + } if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) { typeInfo = layerType_getInfo(layer->type); @@ -2649,6 +2664,27 @@ void *CustomData_add_layer_named(CustomData *data, return NULL; } +void *CustomData_add_layer_anonymous(struct CustomData *data, + int type, + eCDAllocType alloctype, + void *layerdata, + int totelem, + const AnonymousAttributeID *anonymous_id) +{ + const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id); + CustomDataLayer *layer = customData_add_layer__internal( + data, type, alloctype, layerdata, totelem, name); + CustomData_update_typemap(data); + + if (layer == NULL) { + return NULL; + } + + BKE_anonymous_attribute_id_increment_weak(anonymous_id); + layer->anonymous_id = anonymous_id; + return layer->data; +} + bool CustomData_free_layer(CustomData *data, int type, int totelem, int index) { const int index_first = CustomData_get_layer_index(data, type); @@ -2812,6 +2848,20 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data, return customData_duplicate_referenced_layer_index(data, layer_index, totelem); } +void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data, + const int UNUSED(type), + const AnonymousAttributeID *anonymous_id, + const int totelem) +{ + for (int i = 0; i < data->totlayer; i++) { + if (data->layers[i].anonymous_id == anonymous_id) { + return customData_duplicate_referenced_layer_index(data, i, totelem); + } + } + BLI_assert_unreachable(); + return NULL; +} + void CustomData_duplicate_referenced_layers(CustomData *data, int totelem) { for (int i = 0; i < data->totlayer; i++) { @@ -4244,7 +4294,8 @@ void CustomData_blend_write_prepare(CustomData *data, for (i = 0, j = 0; i < totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; - if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */ + /* Layers with this flag set are not written to file. */ + if ((layer->flag & CD_FLAG_NOCOPY) || layer->anonymous_id != NULL) { data->totlayer--; // CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); } diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 58509e95de6..e756daa1156 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -40,6 +40,7 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_scanfill.h" +#include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -47,6 +48,7 @@ #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_font.h" +#include "BKE_geometry_set.hh" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_lib_id.h" @@ -55,6 +57,7 @@ #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_object.h" +#include "BKE_spline.hh" #include "BLI_sys_types.h" /* For #intptr_t support. */ @@ -101,17 +104,6 @@ DispList *BKE_displist_find(ListBase *lb, int type) return nullptr; } -bool BKE_displist_has_faces(const ListBase *lb) -{ - LISTBASE_FOREACH (const DispList *, dl, lb) { - if (ELEM(dl->type, DL_INDEX3, DL_INDEX4, DL_SURF)) { - return true; - } - } - - return false; -} - void BKE_displist_copy(ListBase *lbn, const ListBase *lb) { BKE_displist_free(lbn); @@ -688,23 +680,9 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob) BKE_mball_texspace_calc(ob); object_deform_mball(ob, &ob->runtime.curve_cache->disp); - - /* No-op for MBALLs anyway... */ - boundbox_displist_object(ob); } } -void BKE_displist_make_mball_forRender(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - ListBase *dispbase) -{ - BKE_mball_polygonize(depsgraph, scene, ob, dispbase); - BKE_mball_texspace_calc(ob); - - object_deform_mball(ob, dispbase); -} - static ModifierData *curve_get_tessellate_point(const Scene *scene, const Object *ob, const bool for_render, @@ -745,10 +723,7 @@ static ModifierData *curve_get_tessellate_point(const Scene *scene, return pretessellatePoint; } -/** - * \return True if any modifier was applied. - */ -bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, +void BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, const Scene *scene, Object *ob, ListBase *source_nurb, @@ -793,7 +768,6 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, const ModifierEvalContext mectx = {depsgraph, ob, apply_flag}; ModifierData *pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode); - bool modified = false; if (pretessellatePoint) { VirtualModifierData virtualModifierData; @@ -813,7 +787,6 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, } mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts); - modified = true; if (md == pretessellatePoint) { break; @@ -832,48 +805,59 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, if (keyVerts) { MEM_freeN(keyVerts); } - return modified; } -static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[3] +/** + * \return True if the deformed curve control point data should be implicitly + * converted directly to a mesh, or false if it can be left as curve data via #CurveEval. + */ +static bool do_curve_implicit_mesh_conversion(const Curve *curve, + ModifierData *first_modifier, + const Scene *scene, + const ModifierMode required_mode) { - *r_vert_len = 0; + /* Skip implicit filling and conversion to mesh when using "fast text editing". */ + if (curve->flag & CU_FAST) { + return false; + } - LISTBASE_FOREACH (DispList *, dl, dispbase) { - *r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr; + /* Do implicit conversion to mesh with the object bevel mode. */ + if (curve->bevel_mode == CU_BEV_MODE_OBJECT && curve->bevobj != nullptr) { + return true; } - float(*allverts)[3] = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__); - float *fp = (float *)allverts; - LISTBASE_FOREACH (DispList *, dl, dispbase) { - const int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr); - memcpy(fp, dl->verts, sizeof(float) * ofs); - fp += ofs; + /* 2D curves are sometimes implicitly filled and converted to a mesh. */ + if (CU_DO_2DFILL(curve)) { + return true; } - return allverts; -} + /* Curve objects with implicit "tube" meshes should convert implicitly to a mesh. */ + if (curve->ext1 != 0.0f || curve->ext2 != 0.0f) { + return true; + } -static void displist_vert_coords_apply(ListBase *dispbase, const float (*allverts)[3]) -{ - const float *fp = (float *)allverts; - LISTBASE_FOREACH (DispList *, dl, dispbase) { - int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr); - memcpy(dl->verts, fp, sizeof(float) * ofs); - fp += ofs; + /* If a non-geometry-nodes modifier is enabled before a nodes modifier, + * force conversion to mesh, since only the nodes modifier supports curve data. */ + ModifierData *md = first_modifier; + for (; md; md = md->next) { + if (BKE_modifier_is_enabled(scene, md, required_mode)) { + if (md->type == eModifierType_Nodes) { + break; + } + return true; + } } + + return false; } -static void curve_calc_modifiers_post(Depsgraph *depsgraph, - const Scene *scene, - Object *ob, - ListBase *dispbase, - const bool for_render, - const bool force_mesh_conversion, - Mesh **r_final) +static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph, + const Scene *scene, + Object *ob, + const ListBase *dispbase, + const bool for_render) { const Curve *cu = (const Curve *)ob->data; - const bool editmode = (!for_render && (cu->editnurb || cu->editfont)); const bool use_cache = !for_render; @@ -897,166 +881,64 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData) : pretessellatePoint->next; - if (r_final && *r_final) { - BKE_id_free(nullptr, *r_final); + GeometrySet geometry_set; + if (ob->type == OB_SURF || do_curve_implicit_mesh_conversion(cu, md, scene, required_mode)) { + Mesh *mesh = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); + geometry_set.replace_mesh(mesh); + } + else { + std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve( + *cu, ob->runtime.curve_cache->deformed_nurbs); + geometry_set.replace_curve(curve_eval.release()); } - Mesh *modified = nullptr; - float(*vertCos)[3] = nullptr; - int totvert = 0; for (; md; md = md->next) { 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. */ - const 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 == nullptr /* && need_normal */) { - if (vertCos != nullptr) { - displist_vert_coords_apply(dispbase, vertCos); - } - - if (ELEM(ob->type, OB_CURVE, OB_FONT) && (cu->flag & CU_DEFORM_FILL)) { - curve_to_filledpoly(cu, dispbase); - } + if (md->type == eModifierType_Nodes) { + mti->modifyGeometrySet(md, &mectx_apply, &geometry_set); + continue; + } - modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); + if (!geometry_set.has_mesh()) { + geometry_set.replace_mesh(BKE_mesh_new_nomain(0, 0, 0, 0, 0)); } + Mesh *mesh = geometry_set.get_mesh_for_write(); - if (mti->type == eModifierTypeType_OnlyDeform || - (mti->type == eModifierTypeType_DeformOrConstruct && !modified)) { - if (modified) { - if (!vertCos) { - vertCos = BKE_mesh_vert_coords_alloc(modified, &totvert); - } - if (need_normal) { - BKE_mesh_ensure_normals(modified); - } - mti->deformVerts(md, &mectx_deform, modified, vertCos, totvert); - } - else { - if (!vertCos) { - vertCos = displist_vert_coords_alloc(dispbase, &totvert); - } - mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert); + if (mti->type == eModifierTypeType_OnlyDeform) { + int totvert; + float(*vertex_coords)[3] = BKE_mesh_vert_coords_alloc(mesh, &totvert); + if (mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md)) { + BKE_mesh_ensure_normals(mesh); } + mti->deformVerts(md, &mectx_deform, mesh, vertex_coords, totvert); + BKE_mesh_vert_coords_apply(mesh, vertex_coords); + MEM_freeN(vertex_coords); } else { - if (!r_final) { - /* makeDisplistCurveTypes could be used for beveling, where mesh - * is totally unnecessary, so we could stop modifiers applying - * when we found constructive modifier but mesh is unwanted. */ - break; - } - - if (modified) { - if (vertCos) { - 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); - } - } - else { - if (vertCos) { - displist_vert_coords_apply(dispbase, vertCos); - } - - if (ELEM(ob->type, OB_CURVE, OB_FONT) && (cu->flag & CU_DEFORM_FILL)) { - curve_to_filledpoly(cu, dispbase); - } - - modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); - } - - if (vertCos) { - /* Vertex coordinates were applied to necessary data, could free it */ - MEM_freeN(vertCos); - vertCos = nullptr; - } - - if (need_normal) { - BKE_mesh_ensure_normals(modified); + if (mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md)) { + BKE_mesh_ensure_normals(mesh); } - Mesh *mesh_applied = mti->modifyMesh(md, &mectx_apply, modified); - - if (mesh_applied) { - if (modified && modified != mesh_applied) { - BKE_id_free(nullptr, modified); - } - modified = mesh_applied; + Mesh *output_mesh = mti->modifyMesh(md, &mectx_apply, mesh); + if (mesh != output_mesh) { + geometry_set.replace_mesh(output_mesh); } } } - if (vertCos) { - if (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; + if (geometry_set.has_mesh()) { + Mesh *final_mesh = geometry_set.get_mesh_for_write(); - BKE_mesh_vert_coords_apply(modified, vertCos); - BKE_mesh_calc_normals(modified); + BKE_mesh_calc_normals(final_mesh); - MEM_freeN(vertCos); - } - else { - displist_vert_coords_apply(dispbase, vertCos); - MEM_freeN(vertCos); - vertCos = nullptr; - } + BLI_strncpy(final_mesh->id.name, cu->id.name, sizeof(final_mesh->id.name)); + *((short *)final_mesh->id.name) = ID_ME; } - if (r_final) { - if (force_mesh_conversion && !modified) { - /* XXX 2.8 : This is a workaround for by some deeper technical debts: - * - DRW Batch cache is stored inside the ob->data. - * - Curve data is not COWed for instances that use different modifiers. - * This can causes the modifiers to be applied on all user of the same data-block - * (see T71055) - * - * The easy workaround is to force to generate a Mesh that will be used for display data - * since a Mesh output is already used for generative modifiers. - * However it does not fix problems with actual edit data still being shared. - * - * The right solution would be to COW the Curve data block at the input of the modifier - * stack just like what the mesh modifier does. - */ - modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); - } - - if (modified) { - - /* XXX2.8(Sybren): make sure the face normals are recalculated as well */ - BKE_mesh_ensure_normals(modified); - - /* Special tweaks, needed since neither BKE_mesh_new_nomain_from_template() nor - * BKE_mesh_new_nomain_from_curve_displist() properly duplicate mat info... */ - BLI_strncpy(modified->id.name, cu->id.name, sizeof(modified->id.name)); - *((short *)modified->id.name) = ID_ME; - 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 = (Material **)MEM_dupallocN(cu->mat); - modified->totcol = cu->totcol; - - (*r_final) = modified; - } - else { - (*r_final) = nullptr; - } - } - else if (modified != nullptr) { - /* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */ - BKE_id_free(nullptr, modified); - } + return geometry_set; } static void displist_surf_indices(DispList *dl) @@ -1109,8 +991,7 @@ static void evaluate_surface_object(Depsgraph *depsgraph, BKE_nurbList_duplicate(deformed_nurbs, &cu->nurb); } - bool force_mesh_conversion = BKE_curve_calc_modifiers_pre( - depsgraph, scene, ob, deformed_nurbs, deformed_nurbs, for_render); + BKE_curve_calc_modifiers_pre(depsgraph, scene, ob, deformed_nurbs, deformed_nurbs, for_render); LISTBASE_FOREACH (const Nurb *, nu, deformed_nurbs) { if (!(for_render || nu->hide == 0) || !BKE_nurb_check_valid_uv(nu)) { @@ -1173,8 +1054,14 @@ static void evaluate_surface_object(Depsgraph *depsgraph, } } - curve_calc_modifiers_post( - depsgraph, scene, ob, r_dispbase, for_render, force_mesh_conversion, r_final); + curve_to_filledpoly(cu, r_dispbase); + GeometrySet geometry_set = curve_calc_modifiers_post( + depsgraph, scene, ob, r_dispbase, for_render); + if (!geometry_set.has_mesh()) { + geometry_set.replace_mesh(BKE_mesh_new_nomain(0, 0, 0, 0, 0)); + } + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + *r_final = mesh_component.release(); } static void rotateBevelPiece(const Curve *cu, @@ -1394,12 +1281,11 @@ static void calc_bevfac_mapping(const Curve *cu, } } -static void evaluate_curve_type_object(Depsgraph *depsgraph, - const Scene *scene, - Object *ob, - const bool for_render, - ListBase *r_dispbase, - Mesh **r_final) +static GeometrySet evaluate_curve_type_object(Depsgraph *depsgraph, + const Scene *scene, + Object *ob, + const bool for_render, + ListBase *r_dispbase) { BLI_assert(ELEM(ob->type, OB_CURVE, OB_FONT)); const Curve *cu = (const Curve *)ob->data; @@ -1413,8 +1299,7 @@ static void evaluate_curve_type_object(Depsgraph *depsgraph, BKE_nurbList_duplicate(deformed_nurbs, BKE_curve_nurbs_get_for_read(cu)); } - bool force_mesh_conversion = BKE_curve_calc_modifiers_pre( - depsgraph, scene, ob, deformed_nurbs, deformed_nurbs, for_render); + BKE_curve_calc_modifiers_pre(depsgraph, scene, ob, deformed_nurbs, deformed_nurbs, for_render); BKE_curve_bevelList_make(ob, deformed_nurbs, for_render); @@ -1603,16 +1488,8 @@ static void evaluate_curve_type_object(Depsgraph *depsgraph, BKE_displist_free(&dlbev); - if (!(cu->flag & CU_DEFORM_FILL)) { - curve_to_filledpoly(cu, r_dispbase); - } - - curve_calc_modifiers_post( - depsgraph, scene, ob, r_dispbase, for_render, force_mesh_conversion, r_final); - - if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) { - curve_to_filledpoly(cu, r_dispbase); - } + curve_to_filledpoly(cu, r_dispbase); + return curve_calc_modifiers_post(depsgraph, scene, ob, r_dispbase, for_render); } void BKE_displist_make_curveTypes(Depsgraph *depsgraph, @@ -1621,25 +1498,43 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, const bool for_render) { BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)); + Curve &cow_curve = *(Curve *)ob->data; BKE_object_free_derived_caches(ob); + cow_curve.curve_eval = nullptr; - if (!ob->runtime.curve_cache) { - ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__); - } - - ListBase *dispbase = &(ob->runtime.curve_cache->disp); + ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__); + ListBase *dispbase = &ob->runtime.curve_cache->disp; - Mesh *mesh_eval = nullptr; if (ob->type == OB_SURF) { + Mesh *mesh_eval; evaluate_surface_object(depsgraph, scene, ob, for_render, dispbase, &mesh_eval); + BKE_object_eval_assign_data(ob, &mesh_eval->id, true); } else { - evaluate_curve_type_object(depsgraph, scene, ob, for_render, dispbase, &mesh_eval); - } + GeometrySet geometry = evaluate_curve_type_object(depsgraph, scene, ob, for_render, dispbase); + + if (geometry.has_curve()) { + /* Assign the evaluated curve to the object's "data_eval". In addition to the curve_eval + * added to the curve here, it will also contain a copy of the original curve's data. This is + * essential, because it maintains the expected behavior for evaluated curve data from before + * the CurveEval data type was introduced, when an evaluated object's curve data was just a + * copy of the original curve and everything else ended up in #CurveCache. */ + CurveComponent &curve_component = geometry.get_component_for_write<CurveComponent>(); + cow_curve.curve_eval = curve_component.get_for_write(); + BKE_object_eval_assign_data(ob, &cow_curve.id, false); + } + else if (geometry.has_mesh()) { + /* Most areas of Blender don't yet know how to look in #geometry_set_eval for evaluated mesh + * data, and look in #data_eval instead. When the object evaluates to a curve, that field + * must be used for the evaluated curve data, but otherwise we can use the field to store a + * pointer to the mesh, so more areas can retrieve the mesh. */ + MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>(); + Mesh *mesh_eval = mesh_component.get_for_write(); + BKE_object_eval_assign_data(ob, &mesh_eval->id, false); + } - if (mesh_eval != nullptr) { - BKE_object_eval_assign_data(ob, &mesh_eval->id, true); + ob->runtime.geometry_set_eval = new GeometrySet(std::move(geometry)); } boundbox_displist_object(ob); @@ -1656,7 +1551,9 @@ void BKE_displist_make_curveTypes_forRender( evaluate_surface_object(depsgraph, scene, ob, true, r_dispbase, r_final); } else { - evaluate_curve_type_object(depsgraph, scene, ob, true, r_dispbase, r_final); + GeometrySet geometry_set = evaluate_curve_type_object(depsgraph, scene, ob, true, r_dispbase); + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + *r_final = mesh_component.release(); } } @@ -1684,27 +1581,26 @@ void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3]) /* this is confusing, there's also min_max_object, applying the obmat... */ static void boundbox_displist_object(Object *ob) { - if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { - /* Curve's BB is already calculated as a part of modifier stack, - * here we only calculate object BB based on final display list. */ + BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)); + /* Curve's BB is already calculated as a part of modifier stack, + * here we only calculate object BB based on final display list. */ - /* object's BB is calculated from final displist */ - if (ob->runtime.bb == nullptr) { - ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), __func__); - } + /* object's BB is calculated from final displist */ + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), __func__); + } - const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval) { - BKE_object_boundbox_calc_from_mesh(ob, mesh_eval); - } - else { - float min[3], max[3]; + const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); + if (mesh_eval) { + BKE_object_boundbox_calc_from_mesh(ob, mesh_eval); + } + else { + float min[3], max[3]; - INIT_MINMAX(min, max); - BKE_displist_minmax(&ob->runtime.curve_cache->disp, min, max); - BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); + INIT_MINMAX(min, max); + BKE_displist_minmax(&ob->runtime.curve_cache->disp, min, max); + BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); - ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY; - } + ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY; } } diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 8f94c407cae..d75b3259148 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -2064,7 +2064,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * } if (update_normals) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } } /* make a copy of mesh to use as brush data */ @@ -3978,7 +3978,7 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( total_sample = gaussianTotal; } - /* Supersampling */ + /* Super-sampling */ for (ss = 0; ss < samples; ss++) { float ray_start[3], ray_dir[3]; float sample_factor = 0.0f; @@ -3999,7 +3999,7 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( float hitCoord[3]; int hitTri = -1; - /* Supersampling factor */ + /* Super-sampling factor. */ if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { sample_factor = gaussianFactors[ss]; } @@ -4240,25 +4240,25 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( numOfHits++; } - /* apply sample strength */ + /* Apply sample strength. */ brushStrength += sampleStrength; - } // end supersampling + } /* End super-sampling. */ - /* if any sample was inside paint range */ + /* If any sample was inside paint range. */ if (brushStrength > 0.0f || depth > 0.0f) { - /* apply supersampling results */ + /* Apply super-sampling results. */ if (samples > 1) { brushStrength /= total_sample; } CLAMP(brushStrength, 0.0f, 1.0f); if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - /* Get final pixel color and alpha */ + /* Get final pixel color and alpha. */ paintColor[0] /= numOfHits; paintColor[1] /= numOfHits; paintColor[2] /= numOfHits; } - /* get final object space depth */ + /* Get final object space depth. */ else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { depth /= bData->bNormal[index].normal_scale * total_sample; } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 334118ddf3f..a88339082fe 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -481,7 +481,9 @@ static void eff_tri_ray_hit(void *UNUSED(userData), hit->index = 1; } -// get visibility of a wind ray +/** + * Get visibility of a wind ray. + */ static float eff_calc_visibility(ListBase *colliders, EffectorCache *eff, EffectorData *efd, diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 5decc7a1792..8e9c504dcbf 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -346,30 +346,30 @@ int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, con return 0; } + const size_t quotedName_size = strlen(dataName) + 1; + char *quotedName = alloca(quotedName_size); + /* Search each F-Curve one by one. */ for (fcu = src->first; fcu; fcu = fcu->next) { /* Check if quoted string matches the path. */ - if (fcu->rna_path == NULL || !strstr(fcu->rna_path, dataPrefix)) { + if (fcu->rna_path == NULL) { continue; } - - char *quotedName = BLI_str_quoted_substrN(fcu->rna_path, dataPrefix); - if (quotedName == NULL) { + /* Skipping names longer than `quotedName_size` is OK since we're after an exact match. */ + if (!BLI_str_quoted_substr(fcu->rna_path, dataPrefix, quotedName, quotedName_size)) { + continue; + } + if (!STREQ(quotedName, dataName)) { continue; } /* Check if the quoted name matches the required name. */ - if (STREQ(quotedName, dataName)) { - LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); - - ld->data = fcu; - BLI_addtail(dst, ld); + LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); - matches++; - } + ld->data = fcu; + BLI_addtail(dst, ld); - /* Always free the quoted string, since it needs freeing. */ - MEM_freeN(quotedName); + matches++; } /* Return the number of matches. */ return matches; diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 87c1f99fd73..1324b37f39c 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -39,6 +39,7 @@ #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" +#include "BKE_attribute.h" #include "BKE_effect.h" #include "BKE_fluid.h" #include "BKE_global.h" @@ -529,8 +530,7 @@ static bool BKE_fluid_modifier_init( copy_v3_v3_int(fds->res_max, res); /* Set time, frame length = 0.1 is at 25fps. */ - float fps = scene->r.frs_sec / scene->r.frs_sec_base; - fds->frame_length = DT_DEFAULT * (25.0f / fps) * fds->time_scale; + fds->frame_length = DT_DEFAULT * (25.0f / FPS) * fds->time_scale; /* Initially dt is equal to frame length (dt can change with adaptive-time stepping though). */ fds->dt = fds->frame_length; fds->time_per_frame = 0; @@ -1580,7 +1580,7 @@ static void emit_from_particles(Object *flow_ob, /* initialize particle cache */ if (psys->part->type == PART_HAIR) { - // TODO: PART_HAIR not supported whatsoever + /* TODO: PART_HAIR not supported whatsoever. */ totchild = 0; } else { @@ -1674,9 +1674,9 @@ static void emit_from_particles(Object *flow_ob, if (ffs->flags & FLUID_FLOW_INITVELOCITY && (psys->part->phystype != PART_PHYS_NO)) { madd_v3_v3fl(&bb->velocity[index * 3], &particle_vel[p * 3], ffs->vel_multi); } - } // particles loop + } /* particles loop */ } - else if (valid_particles > 0) { // FLUID_FLOW_USE_PART_SIZE + else if (valid_particles > 0) { /* #FLUID_FLOW_USE_PART_SIZE */ int min[3], max[3], res[3]; /* setup loop bounds */ @@ -3171,7 +3171,7 @@ static void update_effectors_task_cb(void *__restrict userdata, if ((data->fuel && MAX2(data->density[index], data->fuel[index]) < FLT_EPSILON) || (data->density && data->density[index] < FLT_EPSILON) || (data->phi_obs_in && data->phi_obs_in[index] < 0.0f) || - data->flags[index] & 2) // mantaflow convention: 2 == FlagObstacle + data->flags[index] & 2) /* Manta-flow convention: `2 == FlagObstacle`. */ { continue; } @@ -3256,7 +3256,10 @@ static void update_effectors( BKE_effectors_free(effectors); } -static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Object *ob) +static Mesh *create_liquid_geometry(FluidDomainSettings *fds, + Scene *scene, + Mesh *orgmesh, + Object *ob) { Mesh *me; MVert *mverts; @@ -3303,22 +3306,6 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obj /* Normals are per vertex, so these must match. */ BLI_assert(num_verts == num_normals); - /* If needed, vertex velocities will be read too. */ - bool use_speedvectors = fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS; - FluidDomainVertexVelocity *velarray = NULL; - float time_mult = 25.0f * DT_DEFAULT; - - if (use_speedvectors) { - if (fds->mesh_velocities) { - MEM_freeN(fds->mesh_velocities); - } - - fds->mesh_velocities = MEM_calloc_arrayN( - num_verts, sizeof(FluidDomainVertexVelocity), "fluid_mesh_vertvelocities"); - fds->totvert = num_verts; - velarray = fds->mesh_velocities; - } - me = BKE_mesh_new_nomain(num_verts, 0, 0, num_faces * 3, num_faces); if (!me) { return NULL; @@ -3350,6 +3337,18 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obj /* Normals. */ normals = MEM_callocN(sizeof(short[3]) * num_normals, "Fluidmesh_tmp_normals"); + /* Velocities. */ + /* If needed, vertex velocities will be read too. */ + bool use_speedvectors = fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS; + float(*velarray)[3] = NULL; + float time_mult = fds->dx / (DT_DEFAULT * (25.0f / FPS)); + + if (use_speedvectors) { + CustomDataLayer *velocity_layer = BKE_id_attribute_new( + &me->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT, NULL); + velarray = velocity_layer->data; + } + /* Loop for vertices and normals. */ for (i = 0, no_s = normals; i < num_verts && i < num_normals; i++, mverts++, no_s += 3) { @@ -3389,18 +3388,18 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obj # endif if (use_speedvectors) { - velarray[i].vel[0] = manta_liquid_get_vertvel_x_at(fds->fluid, i) * (fds->dx / time_mult); - velarray[i].vel[1] = manta_liquid_get_vertvel_y_at(fds->fluid, i) * (fds->dx / time_mult); - velarray[i].vel[2] = manta_liquid_get_vertvel_z_at(fds->fluid, i) * (fds->dx / time_mult); + velarray[i][0] = manta_liquid_get_vertvel_x_at(fds->fluid, i) * time_mult; + velarray[i][1] = manta_liquid_get_vertvel_y_at(fds->fluid, i) * time_mult; + velarray[i][2] = manta_liquid_get_vertvel_z_at(fds->fluid, i) * time_mult; # ifdef DEBUG_PRINT /* Debugging: Print velocities of vertices. */ - printf("velarray[%d].vel[0]: %f, velarray[%d].vel[1]: %f, velarray[%d].vel[2]: %f\n", + printf("velarray[%d][0]: %f, velarray[%d][1]: %f, velarray[%d][2]: %f\n", i, - velarray[i].vel[0], + velarray[i][0], i, - velarray[i].vel[1], + velarray[i][1], i, - velarray[i].vel[2]); + velarray[i][2]); # endif } } @@ -3574,7 +3573,7 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obje } BKE_mesh_calc_edges(result, false, false); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } @@ -3670,8 +3669,7 @@ static void manta_guiding( Depsgraph *depsgraph, Scene *scene, Object *ob, FluidModifierData *fmd, int frame) { FluidDomainSettings *fds = fmd->domain; - float fps = scene->r.frs_sec / scene->r.frs_sec_base; - float dt = DT_DEFAULT * (25.0f / fps) * fds->time_scale; + float dt = DT_DEFAULT * (25.0f / FPS) * fds->time_scale; BLI_mutex_lock(&object_update_lock); @@ -3842,8 +3840,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, copy_v3_v3_int(o_shift, fds->shift); /* Ensure that time parameters are initialized correctly before every step. */ - float fps = scene->r.frs_sec / scene->r.frs_sec_base; - fds->frame_length = DT_DEFAULT * (25.0f / fps) * fds->time_scale; + fds->frame_length = DT_DEFAULT * (25.0f / FPS) * fds->time_scale; fds->dt = fds->frame_length; fds->time_per_frame = 0; @@ -4216,7 +4213,7 @@ struct Mesh *BKE_fluid_modifier_do( if (needs_viewport_update) { /* Return generated geometry depending on domain type. */ if (fmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) { - result = create_liquid_geometry(fmd->domain, me, ob); + result = create_liquid_geometry(fmd->domain, scene, me, ob); } if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { result = create_smoke_geometry(fmd->domain, me, ob); @@ -4253,7 +4250,7 @@ static float calc_voxel_transp( { const size_t index = manta_get_index(pixel[0], res[0], pixel[1], res[1], pixel[2]); - // T_ray *= T_vox + /* `T_ray *= T_vox`. */ *t_ray *= expf(input[index] * correct); if (result[index] < 0.0f) { @@ -4773,8 +4770,6 @@ static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd) fmd->domain->point_cache[0] = NULL; } - MEM_SAFE_FREE(fmd->domain->mesh_velocities); - if (fmd->domain->coba) { MEM_freeN(fmd->domain->coba); } @@ -5010,16 +5005,12 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd, tfds->viscosity_exponent = fds->viscosity_exponent; /* mesh options */ - if (fds->mesh_velocities) { - tfds->mesh_velocities = MEM_dupallocN(fds->mesh_velocities); - } tfds->mesh_concave_upper = fds->mesh_concave_upper; tfds->mesh_concave_lower = fds->mesh_concave_lower; tfds->mesh_particle_radius = fds->mesh_particle_radius; tfds->mesh_smoothen_pos = fds->mesh_smoothen_pos; tfds->mesh_smoothen_neg = fds->mesh_smoothen_neg; tfds->mesh_scale = fds->mesh_scale; - tfds->totvert = fds->totvert; tfds->mesh_generator = fds->mesh_generator; /* secondary particle options */ diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c index 641c003d456..121927513cc 100644 --- a/source/blender/blenkernel/intern/fmodifier.c +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -580,7 +580,7 @@ int BKE_fcm_envelope_find_index(FCM_EnvelopeData array[], if (loopbreaker == (maxloop - 1)) { CLOG_ERROR(&LOG, "binary search was taking too long"); - // include debug info + /* Include debug info. */ CLOG_ERROR(&LOG, "\tround = %d: start = %d, end = %d, arraylen = %d", loopbreaker, @@ -1419,17 +1419,19 @@ static float eval_fmodifier_influence(FModifier *fcm, float evaltime) /* restricted range or full range? */ if (fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) { - if ((evaltime <= fcm->sfra) || (evaltime >= fcm->efra)) { + if ((evaltime < fcm->sfra) || (evaltime > fcm->efra)) { /* out of range */ return 0.0f; } - if ((evaltime > fcm->sfra) && (evaltime < fcm->sfra + fcm->blendin)) { + if ((fcm->blendin != 0.0f) && (evaltime >= fcm->sfra) && + (evaltime <= fcm->sfra + fcm->blendin)) { /* blend in range */ float a = fcm->sfra; float b = fcm->sfra + fcm->blendin; return influence * (evaltime - a) / (b - a); } - if ((evaltime < fcm->efra) && (evaltime > fcm->efra - fcm->blendout)) { + if ((fcm->blendout != 0.0f) && (evaltime <= fcm->efra) && + (evaltime >= fcm->efra - fcm->blendout)) { /* blend out range */ float a = fcm->efra; float b = fcm->efra - fcm->blendout; diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index c1765967238..842a701f525 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -719,6 +719,9 @@ typedef struct VFontToCurveIter { * * Currently only disabled when scale-to-fit is enabled, * so floating-point error doesn't cause unexpected wrapping, see T89241. + * + * \note This should only be set once, in the #VFONT_TO_CURVE_INIT pass + * otherwise iterations wont behave predictably, see T91401. */ bool word_wrap; int status; @@ -750,8 +753,15 @@ enum { * * The em_height here is relative to FT_Face->bbox. */ -#define ASCENT(vfd) ((vfd)->ascender * (vfd)->em_height) -#define DESCENT(vfd) ((vfd)->em_height - ASCENT(vfd)) + +static float vfont_ascent(const VFontData *vfd) +{ + return vfd->ascender * vfd->em_height; +} +static float vfont_descent(const VFontData *vfd) +{ + return vfd->em_height - vfont_ascent(vfd); +} static bool vfont_to_curve(Object *ob, Curve *cu, @@ -1234,17 +1244,17 @@ static bool vfont_to_curve(Object *ob, case CU_ALIGN_Y_TOP_BASELINE: break; case CU_ALIGN_Y_TOP: - yoff = textbox_y_origin - ASCENT(vfd); + yoff = textbox_y_origin - vfont_ascent(vfd); break; case CU_ALIGN_Y_CENTER: - yoff = ((((vfd->em_height + (lines - 1) * linedist) * 0.5f) - ASCENT(vfd)) - + yoff = ((((vfd->em_height + (lines - 1) * linedist) * 0.5f) - vfont_ascent(vfd)) - (tb_scale.h * 0.5f) + textbox_y_origin); break; case CU_ALIGN_Y_BOTTOM_BASELINE: yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h; break; case CU_ALIGN_Y_BOTTOM: - yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h + DESCENT(vfd); + yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h + vfont_descent(vfd); break; } @@ -1265,16 +1275,16 @@ static bool vfont_to_curve(Object *ob, case CU_ALIGN_Y_TOP_BASELINE: break; case CU_ALIGN_Y_TOP: - yoff = -ASCENT(vfd); + yoff = -vfont_ascent(vfd); break; case CU_ALIGN_Y_CENTER: - yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - ASCENT(vfd); + yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - vfont_ascent(vfd); break; case CU_ALIGN_Y_BOTTOM_BASELINE: yoff = (lnr - 1) * linedist; break; case CU_ALIGN_Y_BOTTOM: - yoff = (lnr - 1) * linedist + DESCENT(vfd); + yoff = (lnr - 1) * linedist + vfont_descent(vfd); break; } @@ -1640,7 +1650,6 @@ static bool vfont_to_curve(Object *ob, else { iter_data->scale_to_fit = iter_data->bisect.min; iter_data->status = VFONT_TO_CURVE_SCALE_ONCE; - iter_data->word_wrap = false; } } } diff --git a/source/blender/blenkernel/intern/freestyle.c b/source/blender/blenkernel/intern/freestyle.c index aa3b4f1ef5e..d9b3faf8623 100644 --- a/source/blender/blenkernel/intern/freestyle.c +++ b/source/blender/blenkernel/intern/freestyle.c @@ -34,7 +34,7 @@ #include "BKE_lib_id.h" #include "BKE_linestyle.h" -// function declarations +/* Function declarations. */ static FreestyleLineSet *alloc_lineset(void); static void copy_lineset(FreestyleLineSet *new_lineset, FreestyleLineSet *lineset, const int flag); static FreestyleModuleConfig *alloc_module(void); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 0b6ba966974..7d0537178ef 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -64,6 +64,8 @@ void CurveComponent::clear() delete curve_; } if (curve_for_render_ != nullptr) { + /* The curve created by this component should not have any edit mode data. */ + BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr); BKE_id_free(nullptr, curve_for_render_); curve_for_render_ = nullptr; } @@ -220,6 +222,37 @@ static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, mixer.finalize(); } +/** + * A spline is selected if all of its control points were selected. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<> +void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + const int splines_len = curve.splines().size(); + Array<int> offsets = curve.control_point_offsets(); + BLI_assert(r_values.size() == splines_len); + + r_values.fill(true); + + for (const int i_spline : IndexRange(splines_len)) { + const int spline_offset = offsets[i_spline]; + const int spline_point_len = offsets[i_spline + 1] - spline_offset; + + for (const int i_point : IndexRange(spline_point_len)) { + if (!old_values[spline_offset + i_point]) { + r_values[i_spline] = false; + break; + } + } + } +} + static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -892,7 +925,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { public: ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { const CurveEval *curve = get_curve_from_component_for_read(component); if (curve == nullptr || curve->splines().size() == 0) { @@ -902,13 +935,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { Span<SplinePtr> splines = curve->splines(); Vector<GSpan> spans; /* GSpan has no default constructor. */ spans.reserve(splines.size()); - std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_name); + std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_id); if (!first_span) { return {}; } spans.append(*first_span); for (const int i : IndexRange(1, splines.size() - 1)) { - std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_name); + std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_id); if (!span) { /* All splines should have the same set of data layers. It would be possible to recover * here and return partial data instead, but that would add a lot of complexity for a @@ -945,7 +978,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* This function is almost the same as #try_get_for_read, but without const. */ WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr || curve->splines().size() == 0) { @@ -955,13 +988,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { MutableSpan<SplinePtr> splines = curve->splines(); Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */ spans.reserve(splines.size()); - std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_name); + std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_id); if (!first_span) { return {}; } spans.append(*first_span); for (const int i : IndexRange(1, splines.size() - 1)) { - std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_name); + std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_id); if (!span) { /* All splines should have the same set of data layers. It would be possible to recover * here and return partial data instead, but that would add a lot of complexity for a @@ -996,7 +1029,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return attribute; } - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { @@ -1006,7 +1039,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ bool layer_freed = false; for (SplinePtr &spline : curve->splines()) { - layer_freed = spline->attributes.remove(attribute_name); + layer_freed = spline->attributes.remove(attribute_id); } return layer_freed; } @@ -1034,7 +1067,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { } bool try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const final @@ -1053,7 +1086,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* First check the one case that allows us to avoid copying the input data. */ if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; - if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) { + if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) { MEM_freeN(source_data); return false; } @@ -1062,7 +1095,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* Otherwise just create a custom data layer on each of the splines. */ for (const int i : splines.index_range()) { - if (!splines[i]->attributes.create(attribute_name, data_type)) { + if (!splines[i]->attributes.create(attribute_id, data_type)) { /* If attribute creation fails on one of the splines, we cannot leave the custom data * layers in the previous splines around, so delete them before returning. However, * this is not an expected case. */ @@ -1076,7 +1109,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return true; } - WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name); + WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id); /* We just created the attribute, it should exist. */ BLI_assert(write_attribute); diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 3b1b7456162..26ef827d36d 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -122,7 +122,7 @@ blender::Span<int> InstancesComponent::instance_ids() const * 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) +int InstancesComponent::add_reference(const InstanceReference &reference) { return references_.index_of_or_add_as(reference); } @@ -144,14 +144,23 @@ bool InstancesComponent::is_empty() const bool InstancesComponent::owns_direct_data() const { - /* The object and collection instances are not direct data. Instance transforms are direct data - * and are always owned. Therefore, instance components always own all their direct data. */ + for (const InstanceReference &reference : references_) { + if (!reference.owns_direct_data()) { + return false; + } + } return true; } void InstancesComponent::ensure_owns_direct_data() { BLI_assert(this->is_mutable()); + for (const InstanceReference &const_reference : references_) { + /* Const cast is fine because we are not changing anything that would change the hash of the + * reference. */ + InstanceReference &reference = const_cast<InstanceReference &>(const_reference); + reference.ensure_owns_direct_data(); + } } static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids) diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index ef93a3f9b3f..0c98aa5551b 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -175,6 +175,34 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if all connected face corners were selected and it is not loose. */ +template<> +void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + Array<bool> loose_verts(mesh.totvert, true); + + r_values.fill(true); + for (const int loop_index : IndexRange(mesh.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int point_index = loop.v; + + loose_verts[point_index] = false; + if (!old_values[loop_index]) { + r_values[point_index] = false; + } + } + + /* Deselect loose vertices without corners that are still selected from the 'true' default. */ + for (const int vert_index : IndexRange(mesh.totvert)) { + if (loose_verts[vert_index]) { + r_values[vert_index] = false; + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -191,6 +219,13 @@ static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr return new_varray; } +/** + * Each corner's value is simply a copy of the value at its vertex. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ template<typename T> static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, const VArray<T> &old_values, @@ -209,10 +244,6 @@ static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr GVArrayPtr new_varray; attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); - /* It is not strictly necessary to compute the value for all corners here. Instead one could - * lazily lookup the mesh topology when a specific index accessed. This can be more efficient - * 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, varray->typed<T>(), values); new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); @@ -244,6 +275,26 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its corners were selected. */ +template<> +void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + if (!old_values[loop_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -282,6 +333,41 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if all corners on adjacent faces were selected. */ +template<> +void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + /* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */ + Array<bool> loose_edges(mesh.totedge, true); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1); + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + loose_edges[edge_index] = false; + + if (!old_values[loop_index] || !old_values[loop_index_next]) { + r_values[edge_index] = false; + } + } + } + + /* Deselect loose edges without corners that are still selected from the 'true' default. */ + for (const int edge_index : IndexRange(mesh.totedge)) { + if (loose_edges[edge_index]) { + r_values[edge_index] = false; + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -317,6 +403,27 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if any of the connected faces were selected. */ +template<> +void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + + r_values.fill(false); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + if (old_values[poly_index]) { + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int vert_index = loop.v; + r_values[vert_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -331,6 +438,7 @@ static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr v return new_varray; } +/* Each corner's value is simply a copy of the value at its face. */ template<typename T> void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, const VArray<T> &old_values, @@ -378,6 +486,27 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if any connected face was selected. */ +template<> +void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + r_values.fill(false); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + if (old_values[poly_index]) { + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + r_values[edge_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -416,6 +545,28 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its vertices were selected too. */ +template<> +void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + MLoop &loop = mesh.mloop[loop_index]; + const int vert_index = loop.v; + if (!old_values[vert_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -452,6 +603,20 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if both of its vertices were selected. */ +template<> +void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + for (const int edge_index : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[edge_index]; + r_values[edge_index] = old_values[edge.v1] && old_values[edge.v2]; + } +} + static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -490,6 +655,29 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, mixer.finalize(); } +/* A corner is selected if its two adjacent edges were selected. */ +template<> +void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totloop); + + r_values.fill(false); + + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop; + const MLoop &loop = mesh.mloop[loop_index]; + const MLoop &loop_prev = mesh.mloop[loop_index_prev]; + if (old_values[loop.e] && old_values[loop_prev.e]) { + r_values[loop_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -522,6 +710,24 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if any connected edge was selected. */ +template<> +void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + + r_values.fill(false); + for (const int edge_index : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[edge_index]; + if (old_values[edge_index]) { + r_values[edge.v1] = true; + r_values[edge.v2] = true; + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -560,6 +766,28 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its edges are selected. */ +template<> +void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + if (!old_values[edge_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -698,7 +926,7 @@ static void tag_normals_dirty_when_writing_position(GeometryComponent &component { Mesh *mesh = get_mesh_from_component_for_write(component); if (mesh != nullptr) { - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } } @@ -818,16 +1046,20 @@ class VArray_For_VertexWeights final : public VArray<float> { class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return {}; + } const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); const Mesh *mesh = mesh_component.get_for_read(); if (mesh == nullptr) { return {}; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return {}; } @@ -843,17 +1075,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { } WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return {}; + } MeshComponent &mesh_component = static_cast<MeshComponent &>(component); Mesh *mesh = mesh_component.get_for_write(); if (mesh == nullptr) { return {}; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return {}; } @@ -872,17 +1108,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { ATTR_DOMAIN_POINT}; } - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return false; + } MeshComponent &mesh_component = static_cast<MeshComponent &>(component); Mesh *mesh = mesh_component.get_for_write(); if (mesh == nullptr) { return true; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return false; } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 07b4e715ea9..e717d289894 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -218,6 +218,16 @@ void GeometrySet::ensure_owns_direct_data() } } +bool GeometrySet::owns_direct_data() const +{ + for (const GeometryComponentPtr &component : components_.values()) { + if (!component->owns_direct_data()) { + return false; + } + } + return true; +} + /* Returns a read-only mesh or null. */ const Mesh *GeometrySet::get_mesh_for_read() const { @@ -376,9 +386,32 @@ void BKE_geometry_set_free(GeometrySet *geometry_set) delete geometry_set; } -bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set) +bool BKE_object_has_geometry_set_instances(const Object *ob) { - return geometry_set->get_component_for_read<InstancesComponent>() != nullptr; + const GeometrySet *geometry_set = ob->runtime.geometry_set_eval; + if (geometry_set == nullptr) { + return false; + } + if (geometry_set->has_instances()) { + return true; + } + const bool has_mesh = geometry_set->has_mesh(); + const bool has_pointcloud = geometry_set->has_pointcloud(); + const bool has_volume = geometry_set->has_volume(); + const bool has_curve = geometry_set->has_curve(); + if (ob->type == OB_MESH) { + return has_pointcloud || has_volume || has_curve; + } + if (ob->type == OB_POINTCLOUD) { + return has_mesh || has_volume || has_curve; + } + if (ob->type == OB_VOLUME) { + return has_mesh || has_pointcloud || has_curve; + } + if (ELEM(ob->type, OB_CURVE, OB_FONT)) { + return has_mesh || has_pointcloud || has_volume; + } + return false; } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 32a65ab47bf..9dca2c2907e 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -51,16 +51,6 @@ 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(*(const 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. */ @@ -84,9 +74,6 @@ 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. */ @@ -168,6 +155,11 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set, collection, instance_transform, r_sets); break; } + case InstanceReference::Type::GeometrySet: { + const GeometrySet &geometry_set = reference.geometry_set(); + geometry_set_collect_recursive(geometry_set, instance_transform, r_sets); + break; + } case InstanceReference::Type::None: { break; } @@ -290,6 +282,13 @@ static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_se } break; } + case InstanceReference::Type::GeometrySet: { + const GeometrySet &geometry_set = reference.geometry_set(); + if (!instances_attribute_foreach_recursive(geometry_set, callback, limit, count)) { + return false; + } + break; + } case InstanceReference::Type::None: { break; } @@ -319,7 +318,7 @@ void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups, Span<GeometryComponentType> component_types, const Set<std::string> &ignored_attributes, - Map<std::string, AttributeKind> &r_attributes) + Map<AttributeIDRef, AttributeKind> &r_attributes) { for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; @@ -329,23 +328,24 @@ void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> se } const GeometryComponent &component = *set.get_component_for_read(component_type); - component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (ignored_attributes.contains(name)) { - return true; - } - auto add_info = [&](AttributeKind *attribute_kind) { - attribute_kind->domain = meta_data.domain; - attribute_kind->data_type = meta_data.data_type; - }; - auto modify_info = [&](AttributeKind *attribute_kind) { - attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ - attribute_kind->data_type = bke::attribute_data_type_highest_complexity( - {attribute_kind->data_type, meta_data.data_type}); - }; - - r_attributes.add_or_modify(name, add_info, modify_info); - return true; - }); + component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { + return true; + } + auto add_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = meta_data.domain; + attribute_kind->data_type = meta_data.data_type; + }; + auto modify_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ + attribute_kind->data_type = bke::attribute_data_type_highest_complexity( + {attribute_kind->data_type, meta_data.data_type}); + }; + + r_attributes.add_or_modify(attribute_id, add_info, modify_info); + return true; + }); } } } @@ -500,11 +500,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou static void join_attributes(Span<GeometryInstanceGroup> set_groups, Span<GeometryComponentType> component_types, - const Map<std::string, AttributeKind> &attribute_info, + const Map<AttributeIDRef, AttributeKind> &attribute_info, GeometryComponent &result) { - for (Map<std::string, AttributeKind>::Item entry : attribute_info.items()) { - StringRef name = entry.key; + for (Map<AttributeIDRef, AttributeKind>::Item entry : attribute_info.items()) { + const AttributeIDRef attribute_id = entry.key; const AttributeDomain domain_output = entry.value.domain; const CustomDataType data_type_output = entry.value.data_type; const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); @@ -512,7 +512,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, result.attribute_try_create( entry.key, domain_output, data_type_output, AttributeInitDefault()); - WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name); + WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(attribute_id); if (!write_attribute || &write_attribute.varray->type() != cpp_type || write_attribute.domain != domain_output) { continue; @@ -531,7 +531,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, continue; /* Domain size is 0, so no need to increment the offset. */ } GVArrayPtr source_attribute = component.attribute_try_get_for_read( - name, domain_output, data_type_output); + attribute_id, domain_output, data_type_output); if (source_attribute) { fn::GVArray_GSpan src_span{*source_attribute}; @@ -641,7 +641,7 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, } /* Don't copy attributes that are stored directly in the mesh data structs. */ - Map<std::string, AttributeKind> attributes; + Map<AttributeIDRef, AttributeKind> attributes; geometry_set_gather_instances_attribute_info( set_groups, component_types, @@ -662,7 +662,7 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>(); dst_component.replace(new_pointcloud); - Map<std::string, AttributeKind> attributes; + Map<AttributeIDRef, AttributeKind> attributes; geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {"position"}, attributes); join_attributes(set_groups, @@ -696,7 +696,7 @@ static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, G CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); dst_component.replace(curve); - Map<std::string, AttributeKind> attributes; + Map<AttributeIDRef, AttributeKind> attributes; geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_CURVE}, diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index a143645c2ee..82a44afbbb1 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -3063,13 +3063,12 @@ void BKE_gpencil_update_layer_transforms(const Depsgraph *depsgraph, Object *ob) Object *ob_parent = DEG_get_evaluated_object(depsgraph, gpl->parent); /* calculate new matrix */ if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) { - copy_m4_m4(cur_mat, ob_parent->obmat); + mul_m4_m4m4(cur_mat, ob->imat, ob_parent->obmat); } else if (gpl->partype == PARBONE) { bPoseChannel *pchan = BKE_pose_channel_find_name(ob_parent->pose, gpl->parsubstr); if (pchan != NULL) { - copy_m4_m4(cur_mat, ob->imat); - mul_m4_m4m4(cur_mat, ob_parent->obmat, pchan->pose_mat); + mul_m4_series(cur_mat, ob->imat, ob_parent->obmat, pchan->pose_mat); } else { unit_m4(cur_mat); diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 5bca20ecd44..8ff026231f5 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -800,7 +800,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo * \param i: Point index * \param inf: Amount of smoothing to apply */ -bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf) +bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, int i, float inf) { bGPDspoint *pt = &gps->points[i]; float sco[3] = {0.0f}; @@ -3248,7 +3248,8 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, void BKE_gpencil_stroke_join(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps, - const bool fit_thickness) + const bool fit_thickness, + const bool smooth) { bGPDspoint point; bGPDspoint *pt; @@ -3326,16 +3327,51 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a, gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, deltatime); } + /* Ratio to apply in the points to keep the same thickness in the joined stroke using the + * destination stroke thickness. */ const float ratio = (fit_thickness && gps_a->thickness > 0.0f) ? (float)gps_b->thickness / (float)gps_a->thickness : 1.0f; /* 3rd: add all points */ + const int totpoints_a = gps_a->totpoints; for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : nullptr; gpencil_stroke_copy_point( gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime); } + /* Smooth the join to avoid hard thickness changes. */ + if (smooth) { + const int sample_points = 8; + /* Get the segment to smooth using n points on each side of the join. */ + int start = MAX2(0, totpoints_a - sample_points); + int end = MIN2(gps_a->totpoints - 1, start + (sample_points * 2)); + const int len = (end - start); + float step = 1.0f / ((len / 2) + 1); + + /* Calc the average pressure. */ + float avg_pressure = 0.0f; + for (i = start; i < end; i++) { + pt = &gps_a->points[i]; + avg_pressure += pt->pressure; + } + avg_pressure = avg_pressure / len; + + /* Smooth segment thickness and position. */ + float ratio = step; + for (i = start; i < end; i++) { + pt = &gps_a->points[i]; + pt->pressure += (avg_pressure - pt->pressure) * ratio; + BKE_gpencil_stroke_smooth_point(gps_a, i, ratio * 0.6f); + + ratio += step; + /* In the center, reverse the ratio. */ + if (ratio > 1.0f) { + ratio = ratio - step - step; + step *= -1.0f; + } + } + } } /* Copy the stroke of the frame to all frames selected (except current). */ diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index eac6a05d33a..6be03bffb3c 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -719,7 +719,7 @@ static void gpencil_copy_activeframe_to_eval( bGPDframe *gpf_orig = gpl_orig->actframe; int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl_orig); - if (gpf_orig && gpf_orig->framenum != remap_cfra) { + if ((gpf_orig == NULL) || (gpf_orig && gpf_orig->framenum != remap_cfra)) { gpf_orig = BKE_gpencil_layer_frame_get(gpl_orig, remap_cfra, GP_GETFRAME_USE_PREV); } @@ -938,6 +938,11 @@ void BKE_gpencil_modifier_blend_write(BlendWriter *writer, ListBase *modbase) BKE_curvemapping_blend_write(writer, gpmd->curve_intensity); } } + else if (md->type == eGpencilModifierType_Dash) { + DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md; + BLO_write_struct_array( + writer, DashGpencilModifierSegment, gpmd->segments_len, gpmd->segments); + } } } @@ -1017,6 +1022,13 @@ void BKE_gpencil_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb) BKE_curvemapping_init(gpmd->curve_intensity); } } + else if (md->type == eGpencilModifierType_Dash) { + DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md; + BLO_read_data_address(reader, &gpmd->segments); + for (int i = 0; i < gpmd->segments_len; i++) { + gpmd->segments[i].dmd = gpmd; + } + } } } diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 5a4b2448a73..97c742b1ec1 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -633,12 +633,6 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) } PreviewImage prv_copy = *prv; - /* don't write out large previews if not requested */ - if (!(U.flag & USER_SAVE_PREVIEWS)) { - prv_copy.w[1] = 0; - prv_copy.h[1] = 0; - prv_copy.rect[1] = nullptr; - } BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy); if (prv_copy.rect[0]) { BLO_write_uint32_array(writer, prv_copy.w[0] * prv_copy.h[0], prv_copy.rect[0]); diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index fee70922570..b2efccc53c4 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -224,10 +224,10 @@ bool BKE_idtype_idcode_is_valid(const short idcode) } /** - * Return non-zero when an ID type is linkable. + * Check if an ID type is linkable. * - * \param idcode: The code to check. - * \return Boolean, 0 when non linkable. + * \param idcode: The IDType code to check. + * \return Boolean, false when non linkable, true otherwise. */ bool BKE_idtype_idcode_is_linkable(const short idcode) { @@ -237,6 +237,24 @@ bool BKE_idtype_idcode_is_linkable(const short idcode) } /** + * Check if an ID type is only appendable. + * + * \param idcode: The IDType code to check. + * \return Boolean, false when also linkable, true when only appendable. + */ +bool BKE_idtype_idcode_is_only_appendable(const short idcode) +{ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(idcode); + BLI_assert(id_type != NULL); + if (id_type != NULL && (id_type->flags & IDTYPE_FLAGS_ONLY_APPEND) != 0) { + /* Only appendable ID types should also always be linkable. */ + BLI_assert((id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0); + return true; + } + return false; +} + +/** * Convert an \a idcode into an \a idfilter (e.g. ID_OB -> FILTER_ID_OB). */ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index d87290e1eb4..33f007c6dee 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -155,7 +155,9 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - image_dst->gputexture[i][eye] = NULL; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + image_dst->gputexture[i][eye][resolution] = NULL; + } } } @@ -208,9 +210,11 @@ static void image_foreach_cache(ID *id, for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { - key.offset_in_ID = offsetof(Image, gputexture[a][eye]); - key.cache_v = image->gputexture[a][eye]; - function_callback(id, &key, (void **)&image->gputexture[a][eye], 0, user_data); + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + key.offset_in_ID = offsetof(Image, gputexture[a][eye][resolution]); + key.cache_v = image->gputexture[a][eye]; + function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); + } } } @@ -239,7 +243,9 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres BLI_listbase_clear(&ima->gpu_refresh_areas); for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - ima->gputexture[i][j] = NULL; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + ima->gputexture[i][j][resolution] = NULL; + } } } @@ -677,8 +683,10 @@ bool BKE_image_has_opengl_texture(Image *ima) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye] != NULL) { - return true; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + if (ima->gputexture[i][eye][resolution] != NULL) { + return true; + } } } } @@ -3531,9 +3539,11 @@ static void image_free_tile(Image *ima, ImageTile *tile) } for (int eye = 0; eye < 2; eye++) { - if (ima->gputexture[i][eye] != NULL) { - GPU_texture_free(ima->gputexture[i][eye]); - ima->gputexture[i][eye] = NULL; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + if (ima->gputexture[i][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[i][eye][resolution]); + ima->gputexture[i][eye][resolution] = NULL; + } } } } @@ -3801,14 +3811,16 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } for (int eye = 0; eye < 2; eye++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = NULL; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = NULL; + } } } @@ -3863,14 +3875,17 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu } for (int eye = 0; eye < 2; eye++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = NULL; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = NULL; + } } } } diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index d179dd40c33..9712e912bed 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -49,6 +49,7 @@ /* Prototypes. */ static void gpu_free_unused_buffers(void); static void image_free_gpu(Image *ima, const bool immediate); +static void image_free_gpu_limited_scale(Image *ima); static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h); @@ -97,9 +98,11 @@ static int smaller_power_of_2_limit(int num, bool limit_gl_texture_size) return power_of_2_min_i(GPU_texture_size_with_limit(num, limit_gl_texture_size)); } -static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) +static GPUTexture *gpu_texture_create_tile_mapping( + Image *ima, const int multiview_eye, const eImageTextureResolution texture_resolution) { - GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; + const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; + GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye][resolution]; if (tilearray == NULL) { return 0; @@ -121,13 +124,14 @@ static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multivi } LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { int i = tile->tile_number - 1001; - data[4 * i] = tile->runtime.tilearray_layer; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + data[4 * i] = tile_runtime->tilearray_layer; float *tile_info = &data[4 * width + 4 * i]; - tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; - tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; - tile_info[2] = tile->runtime.tilearray_size[0] / array_w; - tile_info[3] = tile->runtime.tilearray_size[1] / array_h; + tile_info[0] = tile_runtime->tilearray_offset[0] / array_w; + tile_info[1] = tile_runtime->tilearray_offset[1] / array_h; + tile_info[2] = tile_runtime->tilearray_size[0] / array_w; + tile_info[3] = tile_runtime->tilearray_size[1] / array_h; } GPUTexture *tex = GPU_texture_create_1d_array(ima->id.name + 2, width, 2, 1, GPU_RGBA32F, data); @@ -152,9 +156,12 @@ static int compare_packtile(const void *a, const void *b) return tile_a->pack_score < tile_b->pack_score; } -static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) +static GPUTexture *gpu_texture_create_tile_array(Image *ima, + ImBuf *main_ibuf, + const eImageTextureResolution texture_resolution) { - const bool limit_gl_texture_size = (ima->gpuflag & IMA_GPU_MAX_RESOLUTION) == 0; + const bool limit_gl_texture_size = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED; + const int resolution = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED ? 1 : 0; int arraywidth = 0, arrayheight = 0; ListBase boxes = {NULL}; @@ -200,14 +207,15 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) LISTBASE_FOREACH (PackTile *, packtile, &packed) { ImageTile *tile = packtile->tile; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int *tileoffset = tile_runtime->tilearray_offset; + int *tilesize = tile_runtime->tilearray_size; tileoffset[0] = packtile->boxpack.x; tileoffset[1] = packtile->boxpack.y; tilesize[0] = packtile->boxpack.w; tilesize[1] = packtile->boxpack.h; - tile->runtime.tilearray_layer = arraylayers; + tile_runtime->tilearray_layer = arraylayers; } BLI_freelistN(&packed); @@ -221,9 +229,10 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) /* Upload each tile one by one. */ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int tilelayer = tile->runtime.tilearray_layer; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int tilelayer = tile_runtime->tilearray_layer; + int *tileoffset = tile_runtime->tilearray_offset; + int *tilesize = tile_runtime->tilearray_size; if (tilesize[0] == 0 || tilesize[1] == 0) { continue; @@ -268,16 +277,33 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) /** \name Regular gpu texture * \{ */ +static bool image_max_resolution_texture_fits_in_limited_scale(Image *ima, + eGPUTextureTarget textarget, + const int multiview_eye) +{ + BLI_assert_msg(U.glreslimit != 0, + "limited scale function called without limited scale being set."); + GPUTexture *max_resolution_texture = + ima->gputexture[textarget][multiview_eye][IMA_TEXTURE_RESOLUTION_FULL]; + if (max_resolution_texture && GPU_texture_width(max_resolution_texture) <= U.glreslimit && + GPU_texture_height(max_resolution_texture) <= U.glreslimit) { + return true; + } + return false; +} + static GPUTexture **get_image_gpu_texture_ptr(Image *ima, eGPUTextureTarget textarget, - const int multiview_eye) + const int multiview_eye, + const eImageTextureResolution texture_resolution) { const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); BLI_assert(in_range); BLI_assert(multiview_eye == 0 || multiview_eye == 1); + const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; if (in_range) { - return &(ima->gputexture[textarget][multiview_eye]); + return &(ima->gputexture[textarget][multiview_eye][resolution]); } return NULL; } @@ -296,6 +322,21 @@ static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget) } } +static void image_update_reusable_textures(Image *ima, + eGPUTextureTarget textarget, + const int multiview_eye) +{ + if ((ima->gpuflag & IMA_GPU_HAS_LIMITED_SCALE_TEXTURES) == 0) { + return; + } + + if (ELEM(textarget, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { + if (image_max_resolution_texture_fits_in_limited_scale(ima, textarget, multiview_eye)) { + image_free_gpu_limited_scale(ima); + } + } +} + static GPUTexture *image_get_gpu_texture(Image *ima, ImageUser *iuser, ImBuf *ibuf, @@ -315,24 +356,17 @@ static GPUTexture *image_get_gpu_texture(Image *ima, short requested_pass = iuser ? iuser->pass : 0; short requested_layer = iuser ? iuser->layer : 0; short requested_view = iuser ? iuser->multi_index : 0; - const bool limit_resolution = U.glreslimit != 0 && - ((iuser && (iuser->flag & IMA_SHOW_MAX_RESOLUTION) == 0) || - (iuser == NULL)); - short requested_gpu_flags = limit_resolution ? 0 : IMA_GPU_MAX_RESOLUTION; -#define GPU_FLAGS_TO_CHECK (IMA_GPU_MAX_RESOLUTION) /* There is room for 2 multiview textures. When a higher number is requested we should always * target the first view slot. This is fine as multi view images aren't used together. */ if (requested_view < 2) { requested_view = 0; } if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer || - ima->gpu_view != requested_view || - ((ima->gpuflag & GPU_FLAGS_TO_CHECK) != requested_gpu_flags)) { + ima->gpu_view != requested_view) { ima->gpu_pass = requested_pass; ima->gpu_layer = requested_layer; ima->gpu_view = requested_view; - ima->gpuflag &= ~GPU_FLAGS_TO_CHECK; - ima->gpuflag |= requested_gpu_flags | IMA_GPU_REFRESH; + ima->gpuflag |= IMA_GPU_REFRESH; } #undef GPU_FLAGS_TO_CHECK @@ -369,7 +403,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (current_view >= 2) { current_view = 0; } - GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view); + const bool limit_resolution = U.glreslimit != 0 && + ((iuser && (iuser->flag & IMA_SHOW_MAX_RESOLUTION) == 0) || + (iuser == NULL)) && + ((ima->gpuflag & IMA_GPU_REUSE_MAX_RESOLUTION) == 0); + const eImageTextureResolution texture_resolution = limit_resolution ? + IMA_TEXTURE_RESOLUTION_LIMITED : + IMA_TEXTURE_RESOLUTION_FULL; + GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view, texture_resolution); if (*tex) { return *tex; } @@ -392,22 +433,19 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } if (textarget == TEXTARGET_2D_ARRAY) { - *tex = gpu_texture_create_tile_array(ima, ibuf_intern); + *tex = gpu_texture_create_tile_array(ima, ibuf_intern, texture_resolution); } else if (textarget == TEXTARGET_TILE_MAPPING) { - *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); + *tex = gpu_texture_create_tile_mapping( + ima, iuser ? iuser->multiview_eye : 0, texture_resolution); } else { const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); const bool store_premultiplied = BKE_image_has_gpu_texture_premultiplied_alpha(ima, ibuf_intern); - const bool limit_gl_texture_size = (ima->gpuflag & IMA_GPU_MAX_RESOLUTION) == 0; - *tex = IMB_create_gpu_texture(ima->id.name + 2, - ibuf_intern, - use_high_bitdepth, - store_premultiplied, - limit_gl_texture_size); + *tex = IMB_create_gpu_texture( + ima->id.name + 2, ibuf_intern, use_high_bitdepth, store_premultiplied, limit_resolution); if (*tex) { GPU_texture_wrap_mode(*tex, true, false); @@ -425,6 +463,20 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } } + switch (texture_resolution) { + case IMA_TEXTURE_RESOLUTION_LIMITED: + ima->gpuflag |= IMA_GPU_HAS_LIMITED_SCALE_TEXTURES; + break; + + case IMA_TEXTURE_RESOLUTION_FULL: + image_update_reusable_textures(ima, textarget, current_view); + break; + + case IMA_TEXTURE_RESOLUTION_LEN: + BLI_assert_unreachable(); + break; + } + /* if `ibuf` was given, we don't own the `ibuf_intern` */ if (ibuf == NULL) { BKE_image_release_ibuf(ima, ibuf_intern, NULL); @@ -497,22 +549,39 @@ static void image_free_gpu(Image *ima, const bool immediate) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye] != NULL) { - if (immediate) { - GPU_texture_free(ima->gputexture[i][eye]); - } - else { - BLI_mutex_lock(&gpu_texture_queue_mutex); - BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]); - BLI_mutex_unlock(&gpu_texture_queue_mutex); + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + if (ima->gputexture[i][eye][resolution] != NULL) { + if (immediate) { + GPU_texture_free(ima->gputexture[i][eye][resolution]); + } + else { + BLI_mutex_lock(&gpu_texture_queue_mutex); + BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye][resolution]); + BLI_mutex_unlock(&gpu_texture_queue_mutex); + } + + ima->gputexture[i][eye][resolution] = NULL; } + } + } + } + + ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_HAS_LIMITED_SCALE_TEXTURES); +} - ima->gputexture[i][eye] = NULL; +static void image_free_gpu_limited_scale(Image *ima) +{ + const eImageTextureResolution resolution = IMA_TEXTURE_RESOLUTION_LIMITED; + for (int eye = 0; eye < 2; eye++) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + if (ima->gputexture[i][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[i][eye][resolution]); + ima->gputexture[i][eye][resolution] = NULL; } } } - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_HAS_LIMITED_SCALE_TEXTURES); } void BKE_image_free_gputextures(Image *ima) @@ -689,12 +758,21 @@ static void gpu_texture_update_unscaled(GPUTexture *tex, GPU_unpack_row_length_set(0); } -static void gpu_texture_update_from_ibuf( - GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) +static void gpu_texture_update_from_ibuf(GPUTexture *tex, + Image *ima, + ImBuf *ibuf, + ImageTile *tile, + int x, + int y, + int w, + int h, + eImageTextureResolution texture_resolution) { + const int resolution = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED ? 1 : 0; bool scaled; if (tile != NULL) { - int *tilesize = tile->runtime.tilearray_size; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int *tilesize = tile_runtime->tilearray_size; scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); } else { @@ -758,9 +836,10 @@ static void gpu_texture_update_from_ibuf( if (scaled) { /* Slower update where we first have to scale the input pixels. */ if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - int tilelayer = tile->runtime.tilearray_layer; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int *tileoffset = tile_runtime->tilearray_offset; + int *tilesize = tile_runtime->tilearray_size; + int tilelayer = tile_runtime->tilearray_layer; gpu_texture_update_scaled( tex, rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); } @@ -772,8 +851,9 @@ static void gpu_texture_update_from_ibuf( else { /* Fast update at same resolution. */ if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int tilelayer = tile->runtime.tilearray_layer; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int *tileoffset = tile_runtime->tilearray_offset; + int tilelayer = tile_runtime->tilearray_layer; gpu_texture_update_unscaled( tex, rect, rect_float, x, y, tilelayer, tileoffset, w, h, tex_stride, tex_offset); } @@ -804,16 +884,20 @@ static void gpu_texture_update_from_ibuf( static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h) { - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; - /* Check if we need to update the main gputexture. */ - if (tex != NULL && tile == ima->tiles.first) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); - } + const int eye = 0; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][eye][resolution]; + eImageTextureResolution texture_resolution = resolution; + /* Check if we need to update the main gputexture. */ + if (tex != NULL && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h, texture_resolution); + } - /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; - if (tex != NULL) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]; + if (tex != NULL) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h, texture_resolution); + } } } @@ -917,12 +1001,14 @@ void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) LISTBASE_FOREACH (Image *, ima, &bmain->images) { if (BKE_image_has_opengl_texture(ima)) { if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_mipmap_mode(tex, mipmap, true); + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { + for (int eye = 0; eye < 2; eye++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + GPUTexture *tex = ima->gputexture[a][eye][resolution]; + if (tex != NULL) { + GPU_texture_mipmap_mode(tex, mipmap, true); + } } } } diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index aac081991e3..9b72a2d1a72 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -184,8 +184,7 @@ IDTypeInfo IDType_ID_IP = { .name = "Ipo", .name_plural = "ipos", .translation_context = "", - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL | - IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index f79058dcf21..44fc86877a7 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -212,7 +212,7 @@ IDTypeInfo IDType_ID_KE = { .name = "Key", .name_plural = "shape_keys", .translation_context = BLT_I18NCONTEXT_ID_SHAPEKEY, - .flags = IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL, + .flags = IDTYPE_FLAGS_NO_LIBLINKING, .init_data = NULL, .copy_data = shapekey_copy_data, diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 11e9053df43..60b6d7ad66d 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -98,7 +98,7 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = { .name = "LinkPlaceholder", .name_plural = "link_placeholders", .translation_context = BLT_I18NCONTEXT_ID_ID, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING, .init_data = NULL, .copy_data = NULL, @@ -336,12 +336,34 @@ void id_fake_user_clear(ID *id) } } -void BKE_id_clear_newpoin(ID *id) +void BKE_id_newptr_and_tag_clear(ID *id) { - if (id->newid) { - id->newid->tag &= ~LIB_TAG_NEW; + /* We assume that if this ID has no new ID, its embedded data has not either. */ + if (id->newid == NULL) { + return; } + + id->newid->tag &= ~LIB_TAG_NEW; id->newid = NULL; + + /* Deal with embedded data too. */ + /* NOTE: even though ShapeKeys are not technically embedded data currently, they behave as such + * in most cases, so for sake of consistency treat them as such here. Also mirrors the behavior + * in `BKE_lib_id_make_local`. */ + Key *key = BKE_key_from_id(id); + if (key != NULL) { + BKE_id_newptr_and_tag_clear(&key->id); + } + bNodeTree *ntree = ntreeFromID(id); + if (ntree != NULL) { + BKE_id_newptr_and_tag_clear(&ntree->id); + } + if (GS(id->name) == ID_SCE) { + Collection *master_collection = ((Scene *)id)->master_collection; + if (master_collection != NULL) { + BKE_id_newptr_and_tag_clear(&master_collection->id); + } + } } static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) @@ -406,7 +428,15 @@ static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id) */ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) { + if (!ID_IS_LINKED(id)) { + return; + } + const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; + bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + BLI_assert(force_copy == false || force_copy != force_local); + bool is_local = false, is_lib = false; /* - only lib users: do nothing (unless force_local is set) @@ -416,47 +446,51 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) * we always want to localize, and we skip remapping (done later). */ - if (!ID_IS_LINKED(id)) { - return; + if (!force_copy && !force_local) { + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; + } + } } - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + if (force_local) { + BKE_lib_id_clear_library_data(bmain, id); + BKE_lib_id_expand_local(bmain, id); + } + else if (force_copy) { + ID *id_new = BKE_id_copy(bmain, id); - if (lib_local || is_local) { - if (!is_lib) { - BKE_lib_id_clear_library_data(bmain, id); - BKE_lib_id_expand_local(bmain, id); - } - else { - ID *id_new = BKE_id_copy(bmain, id); - - /* Should not fail in expected use cases, - * but a few ID types cannot be copied (LIB, WM, SCR...). */ - if (id_new != NULL) { - id_new->us = 0; - - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(id, id_new); - Key *key = BKE_key_from_id(id), *key_new = BKE_key_from_id(id); - if (key && key_new) { - ID_NEW_SET(key, key_new); - } - bNodeTree *ntree = ntreeFromID(id), *ntree_new = ntreeFromID(id_new); - if (ntree && ntree_new) { - ID_NEW_SET(ntree, ntree_new); - } - if (GS(id->name) == ID_SCE) { - Collection *master_collection = ((Scene *)id)->master_collection, - *master_collection_new = ((Scene *)id_new)->master_collection; - if (master_collection && master_collection_new) { - ID_NEW_SET(master_collection, master_collection_new); - } - } + /* Should not fail in expected use cases, + * but a few ID types cannot be copied (LIB, WM, SCR...). */ + if (id_new != NULL) { + id_new->us = 0; - if (!lib_local) { - BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE); + /* setting newid is mandatory for complex make_lib_local logic... */ + ID_NEW_SET(id, id_new); + Key *key = BKE_key_from_id(id), *key_new = BKE_key_from_id(id); + if (key && key_new) { + ID_NEW_SET(key, key_new); + } + bNodeTree *ntree = ntreeFromID(id), *ntree_new = ntreeFromID(id_new); + if (ntree && ntree_new) { + ID_NEW_SET(ntree, ntree_new); + } + if (GS(id->name) == ID_SCE) { + Collection *master_collection = ((Scene *)id)->master_collection, + *master_collection_new = ((Scene *)id_new)->master_collection; + if (master_collection && master_collection_new) { + ID_NEW_SET(master_collection, master_collection_new); } } + + if (!lib_local) { + BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE); + } } } } @@ -468,10 +502,9 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) * * \param flags: Special flag used when making a whole library's content local, * it needs specific handling. - * - * \return true if the block can be made local. + * \return true is the ID has successfully been made local. */ -bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags) +bool BKE_lib_id_make_local(Main *bmain, ID *id, const int flags) { const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; @@ -483,23 +516,21 @@ bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_id(id); - if (idtype_info != NULL) { - if ((idtype_info->flags & IDTYPE_FLAGS_NO_MAKELOCAL) == 0) { - if (!test) { - if (idtype_info->make_local != NULL) { - idtype_info->make_local(bmain, id, flags); - } - else { - BKE_lib_id_make_local_generic(bmain, id, flags); - } - } - return true; - } + if (idtype_info == NULL) { + BLI_assert_msg(0, "IDType Missing IDTypeInfo"); return false; } - BLI_assert_msg(0, "IDType Missing IDTypeInfo"); - return false; + BLI_assert((idtype_info->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0); + + if (idtype_info->make_local != NULL) { + idtype_info->make_local(bmain, id, flags); + } + else { + BKE_lib_id_make_local_generic(bmain, id, flags); + } + + return true; } struct IDCopyLibManagementData { @@ -1694,7 +1725,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * * Only for local IDs (linked ones already have a unique ID in their library). * - * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked + * \param do_linked_data: if true, also ensure a unique name in case the given \a id is linked * (otherwise, just ensure that it is properly sorted). * * \return true if a new name had to be created. @@ -1754,8 +1785,7 @@ void BKE_main_id_newptr_and_tag_clear(Main *bmain) ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { - id->newid = NULL; - id->tag &= ~LIB_TAG_NEW; + BKE_id_newptr_and_tag_clear(id); } FOREACH_MAIN_ID_END; } @@ -2022,11 +2052,8 @@ void BKE_library_make_local(Main *bmain, * Note that for objects, we don't want proxy pointers to be cleared yet. This will happen * down the road in this function. */ - BKE_lib_id_make_local(bmain, - id, - false, - LIB_ID_MAKELOCAL_FULL_LIBRARY | - LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BKE_lib_id_make_local( + bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); if (id->newid) { if (GS(id->newid->name) == ID_OB) { diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 8c1e04838df..3fead8b0f39 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -333,11 +333,11 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, * main. You can add more local IDs to be remapped to use new overriding ones by setting their * LIB_TAG_DOIT tag. * - * \param reference_library the library from which the linked data being overridden come from + * \param reference_library: the library from which the linked data being overridden come from * (i.e. the library of the linked reference ID). * - * \param do_no_main Create the new override data outside of Main database. Used for resyncing of - * linked overrides. + * \param do_no_main: Create the new override data outside of Main database. + * Used for resyncing of linked overrides. * * \return \a true on success, \a false otherwise. */ @@ -901,7 +901,7 @@ static void lib_override_library_create_post_process(Main *bmain, * \param id_reference: Some reference ID used to do some post-processing after overrides have been * created, may be NULL. Typically, the Empty object instantiating the linked collection we * override, currently. - * \param r_id_root_override if not NULL, the override generated for the given \a id_root. + * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. * \return true if override was successfully created. */ bool BKE_lib_override_library_create(Main *bmain, diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 36cbf35b251..2ac92828cec 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -720,9 +720,9 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, * Valid usages here are defined as ref-counting usages, which are not towards embedded or * loop-back data. * - * \param r_num_tagged If non-NULL, must be a zero-initialized array of #INDEX_ID_MAX integers. - * Number of tagged-as-unused IDs is then set for each type, and as total in - * #INDEX_ID_NULL item. + * \param r_num_tagged: If non-NULL, must be a zero-initialized array of #INDEX_ID_MAX integers. + * Number of tagged-as-unused IDs is then set for each type, and as total in + * #INDEX_ID_NULL item. */ void BKE_lib_query_unused_ids_tag(Main *bmain, const int tag, diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index bba15a3bcdf..250b8d4d515 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -699,6 +699,9 @@ static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) * * Very specific usage, not sure we'll keep it on the long run, * currently only used in Object/Collection duplication code... + * + * WARNING: This is a deprecated version of this function, should not be used by new code. See + * #BKE_libblock_relink_to_newid_new below. */ void BKE_libblock_relink_to_newid(ID *id) { @@ -708,3 +711,53 @@ void BKE_libblock_relink_to_newid(ID *id) BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); } + +/* ************************ + * FIXME: Port all usages of #BKE_libblock_relink_to_newid to this + * #BKE_libblock_relink_to_newid_new new code and remove old one. + ************************** */ +static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) +{ + const int cb_flag = cb_data->cb_flag; + if (cb_flag & IDWALK_CB_EMBEDDED) { + return IDWALK_RET_NOP; + } + + Main *bmain = cb_data->bmain; + ID *id_owner = cb_data->id_owner; + ID **id_pointer = cb_data->id_pointer; + ID *id = *id_pointer; + if (id) { + /* See: NEW_ID macro */ + if (id->newid != NULL) { + BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE); + id = id->newid; + } + if (id->tag & LIB_TAG_NEW) { + id->tag &= ~LIB_TAG_NEW; + BKE_libblock_relink_to_newid_new(bmain, id); + } + } + return IDWALK_RET_NOP; +} + +/** + * Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively + * in the dependency tree of IDs for all data-blocks tagged with `LIB_TAG_NEW`. + * + * NOTE: `LIB_TAG_NEW` is cleared + * + * Very specific usage, not sure we'll keep it on the long run, + * currently only used in Object/Collection duplication code... + */ +void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) +{ + if (ID_IS_LINKED(id)) { + return; + } + /* We do not want to have those cached relationship data here. */ + BLI_assert(bmain->relations == NULL); + + id->tag &= ~LIB_TAG_NEW; + BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0); +} diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 07a3396ad5f..36958e36004 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -68,8 +68,7 @@ IDTypeInfo IDType_ID_LI = { .name = "Library", .name_plural = "libraries", .translation_context = BLT_I18NCONTEXT_ID_LIBRARY, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL | - IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index bb33f5f9f87..981f1d4a623 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -405,11 +405,8 @@ ImBuf *BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data) } if (data) { - /* NOTE: we cannot use IMB_allocFromBuffer(), since it tries to dupalloc passed buffer, - * which will fail here (we do not want to pass the first two ints!). */ - img = IMB_allocImBuf( - (unsigned int)data->width, (unsigned int)data->height, 32, IB_rect | IB_metadata); - memcpy(img->rect, data->rect, BLEN_THUMB_MEMSIZE(data->width, data->height) - sizeof(*data)); + img = IMB_allocFromBuffer( + (const uint *)data->rect, NULL, (uint)data->width, (uint)data->height, 4); } return img; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index e3650e03c8a..d631993c4e8 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -160,6 +160,14 @@ static void mesh_free_data(ID *id) BLI_freelistN(&mesh->vertex_group_names); + if (mesh->edit_mesh) { + if (mesh->edit_mesh->is_shallow_copy == false) { + BKE_editmesh_free_data(mesh->edit_mesh); + } + MEM_freeN(mesh->edit_mesh); + mesh->edit_mesh = NULL; + } + BKE_mesh_runtime_clear_cache(mesh); mesh_clear_geometry(mesh); MEM_SAFE_FREE(mesh->mat); @@ -880,6 +888,18 @@ void BKE_mesh_free_data_for_undo(Mesh *me) mesh_free_data(&me->id); } +/** + * \note on data that this function intentionally doesn't free: + * + * - Materials and shape keys are not freed here (#Mesh.mat & #Mesh.key). + * As freeing shape keys requires tagging the depsgraph for updated relations, + * which is expensive. + * Material slots should be kept in sync with the object. + * + * - Edit-Mesh (#Mesh.edit_mesh) + * Since edit-mesh is tied to the objects mode, + * which crashes when called in edit-mode, see: T90972. + */ static void mesh_clear_geometry(Mesh *mesh) { CustomData_free(&mesh->vdata, mesh->totvert); @@ -889,11 +909,6 @@ static void mesh_clear_geometry(Mesh *mesh) CustomData_free(&mesh->pdata, mesh->totpoly); MEM_SAFE_FREE(mesh->mselect); - MEM_SAFE_FREE(mesh->edit_mesh); - - /* Note that materials and shape keys are not freed here. This is intentional, as freeing - * shape keys requires tagging the depsgraph for updated relations, which is expensive. - * Material slots should be kept in sync with the object. */ mesh->totvert = 0; mesh->totedge = 0; @@ -1096,7 +1111,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval) MEM_freeN(mesh_eval); } -Mesh *BKE_mesh_copy_for_eval(struct Mesh *source, bool reference) +Mesh *BKE_mesh_copy_for_eval(const Mesh *source, bool reference) { int flags = LIB_ID_COPY_LOCALIZE; @@ -1591,7 +1606,7 @@ void BKE_mesh_transform(Mesh *me, const float mat[4][4], bool do_keys) MVert *mvert = CustomData_duplicate_referenced_layer(&me->vdata, CD_MVERT, me->totvert); float(*lnors)[3] = CustomData_duplicate_referenced_layer(&me->ldata, CD_NORMAL, me->totloop); - /* If the referenced l;ayer has been re-allocated need to update pointers stored in the mesh. */ + /* If the referenced layer has been re-allocated need to update pointers stored in the mesh. */ BKE_mesh_update_customdata_pointers(me, false); for (i = 0; i < me->totvert; i++, mvert++) { @@ -1844,7 +1859,7 @@ void BKE_mesh_vert_coords_apply(Mesh *mesh, const float (*vert_coords)[3]) for (int i = 0; i < mesh->totvert; i++, mv++) { copy_v3_v3(mv->co, vert_coords[i]); } - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh, @@ -1857,7 +1872,7 @@ void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh, for (int i = 0; i < mesh->totvert; i++, mv++) { mul_v3_m4v3(mv->co, mat, vert_coords[i]); } - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } void BKE_mesh_vert_normals_apply(Mesh *mesh, const short (*vert_normals)[3]) diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.cc index d5524312612..07dc6db05aa 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -68,7 +68,7 @@ #ifdef VALIDATE_MESH # define ASSERT_IS_VALID_MESH(mesh) \ - (BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true))) + (BLI_assert((mesh == nullptr) || (BKE_mesh_is_valid(mesh) == true))) #else # define ASSERT_IS_VALID_MESH(mesh) #endif @@ -84,15 +84,16 @@ void BKE_mesh_from_metaball(ListBase *lb, Mesh *me) const float *nors, *verts; int a, *index; - dl = lb->first; - if (dl == NULL) { + dl = (DispList *)lb->first; + if (dl == nullptr) { return; } if (dl->type == DL_INDEX4) { - mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, dl->nr); - allloop = mloop = CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, dl->parts * 4); - mpoly = CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, dl->parts); + mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, dl->nr); + allloop = mloop = (MLoop *)CustomData_add_layer( + &me->ldata, CD_MLOOP, CD_CALLOC, nullptr, dl->parts * 4); + mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, nullptr, dl->parts); me->mvert = mvert; me->mloop = mloop; me->mpoly = mpoly; @@ -177,9 +178,10 @@ static void make_edges_mdata_extend( MEdge *medge; uint e_index = totedge; - *r_alledge = medge = (*r_alledge ? - MEM_reallocN(*r_alledge, sizeof(MEdge) * (totedge + totedge_new)) : - MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__)); + *r_alledge = medge = (MEdge *)(*r_alledge ? + MEM_reallocN(*r_alledge, + sizeof(MEdge) * (totedge + totedge_new)) : + MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__)); medge += totedge; totedge += totedge_new; @@ -209,7 +211,7 @@ static void make_edges_mdata_extend( } } - BLI_edgehash_free(eh, NULL); + BLI_edgehash_free(eh, nullptr); } /* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */ @@ -229,7 +231,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, MVert *mvert; MPoly *mpoly; MLoop *mloop; - MLoopUV *mloopuv = NULL; + MLoopUV *mloopuv = nullptr; MEdge *medge; const float *data; int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0; @@ -277,14 +279,15 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, return -1; } - *r_allvert = mvert = MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert"); - *r_alledge = medge = MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge"); - *r_allloop = mloop = MEM_calloc_arrayN( + *r_allvert = mvert = (MVert *)MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert"); + *r_alledge = medge = (MEdge *)MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge"); + *r_allloop = mloop = (MLoop *)MEM_calloc_arrayN( totpoly, sizeof(MLoop[4]), "nurbs_init mloop"); /* totloop */ - *r_allpoly = mpoly = MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop"); + *r_allpoly = mpoly = (MPoly *)MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop"); if (r_alluv) { - *r_alluv = mloopuv = MEM_calloc_arrayN(totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv"); + *r_alluv = mloopuv = (MLoopUV *)MEM_calloc_arrayN( + totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv"); } /* verts and faces */ @@ -483,17 +486,33 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, return 0; } +/** + * Copy evaluated texture space from curve to mesh. + * + * \note We disable auto texture space feature since that will cause texture space to evaluate + * differently for curve and mesh, since curves use control points and handles to calculate the + * bounding box, and mesh uses the tessellated curve. + */ +static void mesh_copy_texture_space_from_curve_type(const Curve *cu, Mesh *me) +{ + me->texflag = cu->texflag & ~CU_AUTOSPACE; + copy_v3_v3(me->loc, cu->loc); + copy_v3_v3(me->size, cu->size); + BKE_mesh_texspace_calc(me); +} + Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *dispbase) { + const Curve *cu = (const Curve *)ob->data; Mesh *mesh; MVert *allvert; MEdge *alledge; MLoop *allloop; MPoly *allpoly; - MLoopUV *alluv = NULL; + MLoopUV *alluv = nullptr; int totvert, totedge, totloop, totpoly; - if (mesh_nurbs_displist_to_mdata(ob->data, + if (mesh_nurbs_displist_to_mdata(cu, dispbase, &allvert, &totvert, @@ -509,7 +528,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * } mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly); - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); if (totvert != 0) { memcpy(mesh->mvert, allvert, totvert * sizeof(MVert)); @@ -529,6 +548,12 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, totloop, uvname); } + mesh_copy_texture_space_from_curve_type(cu, mesh); + + /* Copy curve materials. */ + mesh->mat = (Material **)MEM_dupallocN(cu->mat); + mesh->totcol = cu->totcol; + MEM_freeN(allvert); MEM_freeN(alledge); MEM_freeN(allloop); @@ -539,7 +564,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob) { - ListBase disp = {NULL, NULL}; + ListBase disp = {nullptr, nullptr}; if (ob->runtime.curve_cache) { disp = ob->runtime.curve_cache->disp; @@ -556,16 +581,16 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char Mesh *me_eval = (Mesh *)ob->runtime.data_eval; Mesh *me; - MVert *allvert = NULL; - MEdge *alledge = NULL; - MLoop *allloop = NULL; - MLoopUV *alluv = NULL; - MPoly *allpoly = NULL; + MVert *allvert = nullptr; + MEdge *alledge = nullptr; + MLoop *allloop = nullptr; + MLoopUV *alluv = nullptr; + MPoly *allpoly = nullptr; int totvert, totedge, totloop, totpoly; - Curve *cu = ob->data; + Curve *cu = (Curve *)ob->data; - if (me_eval == NULL) { + if (me_eval == nullptr) { if (mesh_nurbs_displist_to_mdata(cu, dispbase, &allvert, @@ -582,49 +607,43 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char } /* make mesh */ - me = BKE_id_new_nomain(ID_ME, obdata_name); + me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name); me->totvert = totvert; me->totedge = totedge; me->totloop = totloop; me->totpoly = totpoly; - me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert); - me->medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge); - me->mloop = CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop); - me->mpoly = CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly); + me->mvert = (MVert *)CustomData_add_layer( + &me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert); + me->medge = (MEdge *)CustomData_add_layer( + &me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge); + me->mloop = (MLoop *)CustomData_add_layer( + &me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop); + me->mpoly = (MPoly *)CustomData_add_layer( + &me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly); if (alluv) { const char *uvname = "UVMap"; - me->mloopuv = CustomData_add_layer_named( + me->mloopuv = (MLoopUV *)CustomData_add_layer_named( &me->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, me->totloop, uvname); } BKE_mesh_calc_normals(me); } else { - me = BKE_id_new_nomain(ID_ME, obdata_name); + me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name); - ob->runtime.data_eval = NULL; + ob->runtime.data_eval = nullptr; BKE_mesh_nomain_to_mesh(me_eval, me, ob, &CD_MASK_MESH, true); } me->totcol = cu->totcol; me->mat = cu->mat; - /* Copy evaluated texture space from curve to mesh. - * - * Note that we disable auto texture space feature since that will cause - * texture space to evaluate differently for curve and mesh, since curve - * uses CV to calculate bounding box, and mesh uses what is coming from - * tessellated curve. - */ - me->texflag = cu->texflag & ~CU_AUTOSPACE; - copy_v3_v3(me->loc, cu->loc); - copy_v3_v3(me->size, cu->size); - BKE_mesh_texspace_calc(me); + mesh_copy_texture_space_from_curve_type(cu, me); - cu->mat = NULL; + cu->mat = nullptr; cu->totcol = 0; /* Do not decrement ob->data usercount here, @@ -635,29 +654,29 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char /* For temporary objects in BKE_mesh_new_from_object don't remap * the entire scene with associated depsgraph updates, which are * problematic for renderers exporting data. */ - BKE_id_free(NULL, cu); + BKE_id_free(nullptr, cu); } -typedef struct EdgeLink { +struct EdgeLink { struct EdgeLink *next, *prev; void *edge; -} EdgeLink; +}; -typedef struct VertLink { +struct VertLink { Link *next, *prev; uint index; -} VertLink; +}; static void prependPolyLineVert(ListBase *lb, uint index) { - VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink"); + VertLink *vl = (VertLink *)MEM_callocN(sizeof(VertLink), "VertLink"); vl->index = index; BLI_addhead(lb, vl); } static void appendPolyLineVert(ListBase *lb, uint index) { - VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink"); + VertLink *vl = (VertLink *)MEM_callocN(sizeof(VertLink), "VertLink"); vl->index = index; BLI_addtail(lb, vl); } @@ -677,10 +696,10 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed /* only to detect edge polylines */ int *edge_users; - ListBase edges = {NULL, NULL}; + ListBase edges = {nullptr, nullptr}; /* get boundary edges */ - edge_users = MEM_calloc_arrayN(medge_len, sizeof(int), __func__); + edge_users = (int *)MEM_calloc_arrayN(medge_len, sizeof(int), __func__); for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) { MLoop *ml = &mloop[mp->loopstart]; int j; @@ -693,7 +712,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed med = medge; for (i = 0; i < medge_len; i++, med++) { if (edge_users[i] == edge_users_test) { - EdgeLink *edl = MEM_callocN(sizeof(EdgeLink), "EdgeLink"); + EdgeLink *edl = (EdgeLink *)MEM_callocN(sizeof(EdgeLink), "EdgeLink"); edl->edge = med; BLI_addtail(&edges, edl); @@ -706,10 +725,10 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed while (edges.first) { /* each iteration find a polyline and add this as a nurbs poly spline */ - ListBase polyline = {NULL, NULL}; /* store a list of VertLink's */ + ListBase polyline = {nullptr, nullptr}; /* store a list of VertLink's */ bool closed = false; int totpoly = 0; - MEdge *med_current = ((EdgeLink *)edges.last)->edge; + MEdge *med_current = (MEdge *)((EdgeLink *)edges.last)->edge; uint startVert = med_current->v1; uint endVert = med_current->v2; bool ok = true; @@ -722,12 +741,12 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed totedges--; while (ok) { /* while connected edges are found... */ - EdgeLink *edl = edges.last; + EdgeLink *edl = (EdgeLink *)edges.last; ok = false; while (edl) { EdgeLink *edl_prev = edl->prev; - med = edl->edge; + med = (MEdge *)edl->edge; if (med->v1 == endVert) { endVert = med->v2; @@ -791,7 +810,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed nu->bp = (BPoint *)MEM_calloc_arrayN(totpoly, sizeof(BPoint), "bpoints"); /* add points */ - vl = polyline.first; + vl = (VertLink *)polyline.first; for (i = 0, bp = nu->bp; i < totpoly; i++, bp++, vl = (VertLink *)vl->next) { copy_v3_v3(bp->vec, mvert[vl->index].co); bp->f1 = SELECT; @@ -813,7 +832,7 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_MESH); - ListBase nurblist = {NULL, NULL}; + ListBase nurblist = {nullptr, nullptr}; BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 0); BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 1); @@ -834,16 +853,13 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud) { - BLI_assert(me != NULL); + BLI_assert(me != nullptr); pointcloud->totpoint = me->totvert; CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); /* Copy over all attributes. */ - const CustomData_MeshMasks mask = { - .vmask = CD_MASK_PROP_ALL, - }; - CustomData_merge(&me->vdata, &pointcloud->pdata, mask.vmask, CD_DUPLICATE, me->totvert); + CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert); BKE_pointcloud_update_customdata_pointers(pointcloud); CustomData_update_typemap(&pointcloud->pdata); @@ -862,7 +878,7 @@ void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_MESH); - PointCloud *pointcloud = BKE_pointcloud_add(bmain, ob->id.name + 2); + PointCloud *pointcloud = (PointCloud *)BKE_pointcloud_add(bmain, ob->id.name + 2); BKE_pointcloud_from_mesh(me_eval, pointcloud); @@ -877,24 +893,22 @@ void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce void BKE_mesh_from_pointcloud(const PointCloud *pointcloud, Mesh *me) { - BLI_assert(pointcloud != NULL); + BLI_assert(pointcloud != nullptr); me->totvert = pointcloud->totpoint; /* Merge over all attributes. */ - const CustomData_MeshMasks mask = { - .vmask = CD_MASK_PROP_ALL, - }; - CustomData_merge(&pointcloud->pdata, &me->vdata, mask.vmask, CD_DUPLICATE, pointcloud->totpoint); + CustomData_merge( + &pointcloud->pdata, &me->vdata, CD_MASK_PROP_ALL, CD_DUPLICATE, pointcloud->totpoint); /* Convert the Position attribute to a mesh vertex. */ - me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert); + me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, me->totvert); CustomData_update_typemap(&me->vdata); const int layer_idx = CustomData_get_named_layer_index( &me->vdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION); CustomDataLayer *pos_layer = &me->vdata.layers[layer_idx]; - float(*positions)[3] = pos_layer->data; + float(*positions)[3] = (float(*)[3])pos_layer->data; MVert *mvert; mvert = me->mvert; @@ -944,7 +958,8 @@ static Object *object_for_curve_to_mesh_create(Object *object) Curve *curve = (Curve *)object->data; /* Create object itself. */ - Object *temp_object = (Object *)BKE_id_copy_ex(NULL, &object->id, NULL, LIB_ID_COPY_LOCALIZE); + Object *temp_object = (Object *)BKE_id_copy_ex( + nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE); /* Remove all modifiers, since we don't want them to be applied. */ BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT); @@ -953,26 +968,27 @@ 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 (temp_object->runtime.curve_cache == NULL) { - temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), - "CurveCache for curve types"); + if (temp_object->runtime.curve_cache == nullptr) { + temp_object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), + "CurveCache for curve types"); } - if (object->runtime.curve_cache != NULL) { + if (object->runtime.curve_cache != nullptr) { 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) { + if (object->runtime.data_eval != nullptr) { BKE_id_copy_ex( - NULL, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE); + nullptr, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE); } /* Need to create copy of curve itself as well, it will be freed by underlying conversion * functions. * * NOTE: Copies the data, but not the shapekeys. */ - BKE_id_copy_ex(NULL, object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE); + BKE_id_copy_ex( + nullptr, (const ID *)object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE); Curve *temp_curve = (Curve *)temp_object->data; /* Make sure texture space is calculated for a copy of curve, it will be used for the final @@ -999,8 +1015,9 @@ static void curve_to_mesh_eval_ensure(Object *object) remapped_object.data = &remapped_curve; - if (object->runtime.curve_cache == NULL) { - object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); + if (object->runtime.curve_cache == nullptr) { + object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), + "CurveCache for Curve"); } /* Temporarily share the curve-cache with the temporary object, owned by `object`. */ @@ -1013,8 +1030,8 @@ static void curve_to_mesh_eval_ensure(Object *object) * * So we create temporary copy of the object which will use same data as the original bevel, but * will have no modifiers. */ - Object bevel_object = {{NULL}}; - if (remapped_curve.bevobj != NULL) { + Object bevel_object = {{nullptr}}; + if (remapped_curve.bevobj != nullptr) { bevel_object = *remapped_curve.bevobj; BLI_listbase_clear(&bevel_object.modifiers); BKE_object_runtime_reset(&bevel_object); @@ -1022,34 +1039,34 @@ static void curve_to_mesh_eval_ensure(Object *object) } /* Same thing for taper. */ - Object taper_object = {{NULL}}; - if (remapped_curve.taperobj != NULL) { + Object taper_object = {{nullptr}}; + if (remapped_curve.taperobj != nullptr) { taper_object = *remapped_curve.taperobj; BLI_listbase_clear(&taper_object.modifiers); BKE_object_runtime_reset(&taper_object); remapped_curve.taperobj = &taper_object; } - /* NOTE: We don't have dependency graph or scene here, so we pass NULL. This is all fine since + /* NOTE: We don't have dependency graph or scene here, so we pass nullptr. This is all fine since * they are only used for modifier stack, which we have explicitly disabled for all objects. * * TODO(sergey): This is a very fragile logic, but proper solution requires re-writing quite a * bit of internal functions (#mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also * Mesh From Curve operator. * Brecht says hold off with that. */ - Mesh *mesh_eval = NULL; + Mesh *mesh_eval = nullptr; BKE_displist_make_curveTypes_forRender( - NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval); + nullptr, nullptr, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval); /* NOTE: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a * real issue currently, code here is broken in more than one way, fix(es) will be done * separately. */ - if (mesh_eval != NULL) { + if (mesh_eval != nullptr) { BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true); } /* Owned by `object` & needed by the caller to create the mesh. */ - remapped_object.runtime.curve_cache = NULL; + remapped_object.runtime.curve_cache = nullptr; BKE_object_runtime_free_data(&remapped_object); BKE_object_runtime_free_data(&taper_object); @@ -1058,7 +1075,7 @@ static void curve_to_mesh_eval_ensure(Object *object) static Mesh *mesh_new_from_curve_type_object(Object *object) { - Curve *curve = object->data; + Curve *curve = (Curve *)object->data; Object *temp_object = object_for_curve_to_mesh_create(object); Curve *temp_curve = (Curve *)temp_object->data; @@ -1069,8 +1086,8 @@ static Mesh *mesh_new_from_curve_type_object(Object *object) } /* Reset pointers before conversion. */ - temp_curve->editfont = NULL; - temp_curve->editnurb = NULL; + temp_curve->editfont = nullptr; + temp_curve->editnurb = nullptr; /* Convert to mesh. */ mesh_from_nurbs_displist( @@ -1079,14 +1096,14 @@ static Mesh *mesh_new_from_curve_type_object(Object *object) /* #mesh_from_nurbs_displist changes the type to a mesh, check it worked. If it didn't * the curve did not have any segments or otherwise would have generated an empty mesh. */ if (temp_object->type != OB_MESH) { - BKE_id_free(NULL, temp_object->data); - BKE_id_free(NULL, temp_object); - return NULL; + BKE_id_free(nullptr, temp_object->data); + BKE_id_free(nullptr, temp_object); + return nullptr; } - Mesh *mesh_result = temp_object->data; + Mesh *mesh_result = (Mesh *)temp_object->data; - BKE_id_free(NULL, temp_object); + BKE_id_free(nullptr, temp_object); /* NOTE: Materials are copied in #mesh_from_nurbs_displist(). */ @@ -1102,19 +1119,19 @@ static Mesh *mesh_new_from_mball_object(Object *object) * ball). * * We create empty mesh so scripters don't run into None objects. */ - if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == NULL || + if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == nullptr || BLI_listbase_is_empty(&object->runtime.curve_cache->disp)) { - return BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2); + return (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2); } - Mesh *mesh_result = BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2); + Mesh *mesh_result = (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2); BKE_mesh_from_metaball(&object->runtime.curve_cache->disp, mesh_result); BKE_mesh_texspace_copy_from_object(mesh_result, object); /* Copy materials. */ mesh_result->totcol = mball->totcol; - mesh_result->mat = MEM_dupallocN(mball->mat); - if (mball->mat != NULL) { + mesh_result->mat = (Material **)MEM_dupallocN(mball->mat); + if (mball->mat != nullptr) { for (int i = mball->totcol; i-- > 0;) { mesh_result->mat[i] = BKE_object_material_get(object, i + 1); } @@ -1130,7 +1147,7 @@ static Mesh *mesh_new_from_mesh(Object *object, Mesh *mesh) BKE_mesh_wrapper_ensure_mdata(mesh); Mesh *mesh_result = (Mesh *)BKE_id_copy_ex( - NULL, &mesh->id, NULL, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT); + nullptr, &mesh->id, nullptr, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT); /* NOTE: Materials should already be copied. */ /* Copy original mesh name. This is because edit meshes might not have one properly set name. */ BLI_strncpy(mesh_result->id.name, ((ID *)object->data)->name, sizeof(mesh_result->id.name)); @@ -1145,12 +1162,12 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, return mesh_new_from_mesh(object, (Mesh *)object->data); } - if (depsgraph == NULL) { - return NULL; + if (depsgraph == nullptr) { + return nullptr; } Object object_for_eval = *object; - if (object_for_eval.runtime.data_orig != NULL) { + if (object_for_eval.runtime.data_orig != nullptr) { object_for_eval.data = object_for_eval.runtime.data_orig; } @@ -1174,10 +1191,10 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph, if (preserve_all_data_layers || preserve_origindex) { return mesh_new_from_mesh_object_with_layers(depsgraph, object, preserve_origindex); } - Mesh *mesh_input = object->data; + Mesh *mesh_input = (Mesh *)object->data; /* If we are in edit mode, use evaluated mesh from edit structure, matching to what * viewport is using for visualization. */ - if (mesh_input->edit_mesh != NULL && mesh_input->edit_mesh->mesh_eval_final) { + if (mesh_input->edit_mesh != nullptr && mesh_input->edit_mesh->mesh_eval_final) { mesh_input = mesh_input->edit_mesh->mesh_eval_final; } return mesh_new_from_mesh(object, mesh_input); @@ -1188,7 +1205,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, const bool preserve_all_data_layers, const bool preserve_origindex) { - Mesh *new_mesh = NULL; + Mesh *new_mesh = nullptr; switch (object->type) { case OB_FONT: case OB_CURVE: @@ -1204,11 +1221,11 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, break; default: /* Object does not have geometry data. */ - return NULL; + return nullptr; } - if (new_mesh == NULL) { + if (new_mesh == nullptr) { /* Happens in special cases like request of mesh for non-mother meta ball. */ - return NULL; + return nullptr; } /* The result must have 0 users, since it's just a mesh which is free-dangling data-block. @@ -1221,9 +1238,9 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, * ownership. * * Here we are constructing a mesh which is supposed to be independent, which means no shared - * ownership is allowed, so we make sure edit mesh is reset to NULL (which is similar to as if + * ownership is allowed, so we make sure edit mesh is reset to nullptr (which is similar to as if * one duplicates the objects and applies all the modifiers). */ - new_mesh->edit_mesh = NULL; + new_mesh->edit_mesh = nullptr; return new_mesh; } @@ -1231,7 +1248,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, static int foreach_libblock_make_original_callback(LibraryIDLinkCallbackData *cb_data) { ID **id_p = cb_data->id_pointer; - if (*id_p == NULL) { + if (*id_p == nullptr) { return IDWALK_RET_NOP; } *id_p = DEG_get_original_id(*id_p); @@ -1242,7 +1259,7 @@ static int foreach_libblock_make_original_callback(LibraryIDLinkCallbackData *cb static int foreach_libblock_make_usercounts_callback(LibraryIDLinkCallbackData *cb_data) { ID **id_p = cb_data->id_pointer; - if (*id_p == NULL) { + if (*id_p == nullptr) { return IDWALK_RET_NOP; } @@ -1266,7 +1283,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH)); Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false); - if (mesh == NULL) { + if (mesh == nullptr) { /* Unable to convert the object to a mesh, return an empty one. */ Mesh *mesh_in_bmain = BKE_mesh_add(bmain, ((ID *)object->data)->name + 2); id_us_min(&mesh_in_bmain->id); @@ -1282,7 +1299,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, * Note that user-count updates has to be done *after* mesh has been transferred to Main database * (since doing refcounting on non-Main IDs is forbidden). */ BKE_library_foreach_ID_link( - NULL, &mesh->id, foreach_libblock_make_original_callback, NULL, IDWALK_NOP); + nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP); /* Append the mesh to 'bmain'. * We do it a bit longer way since there is no simple and clear way of adding existing data-block @@ -1299,14 +1316,14 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, mesh_in_bmain->totcol = mesh->totcol; mesh_in_bmain->flag = mesh->flag; mesh_in_bmain->smoothresh = mesh->smoothresh; - mesh->mat = NULL; + mesh->mat = nullptr; - BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, NULL, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true); /* User-count is required because so far mesh was in a limbo, where library management does * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ BKE_library_foreach_ID_link( - NULL, &mesh_in_bmain->id, foreach_libblock_make_usercounts_callback, NULL, IDWALK_NOP); + nullptr, &mesh_in_bmain->id, foreach_libblock_make_usercounts_callback, nullptr, IDWALK_NOP); /* Make sure user count from BKE_mesh_add() is the one we expect here and bring it down to 0. */ BLI_assert(mesh_in_bmain->id.us == 1); @@ -1335,7 +1352,7 @@ static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src) return; } - for (i = 0, kb = key->block.first; kb; kb = kb->next, i++) { + for (i = 0, kb = (KeyBlock *)key->block.first; kb; kb = kb->next, i++) { int ci; float *array; @@ -1346,10 +1363,10 @@ static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src) mesh_src->totvert, kb->name, kb->totelem); - array = MEM_calloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__); + array = (float *)MEM_calloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__); } else { - array = MEM_malloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__); + array = (float *)MEM_malloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__); memcpy(array, kb->data, sizeof(float[3]) * (size_t)mesh_src->totvert); } @@ -1367,9 +1384,10 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, ModifierData *md_eval, const bool build_shapekey_layers) { - Mesh *me = ob_eval->runtime.data_orig ? ob_eval->runtime.data_orig : ob_eval->data; - const ModifierTypeInfo *mti = BKE_modifier_get_info(md_eval->type); - Mesh *result = NULL; + Mesh *me = ob_eval->runtime.data_orig ? (Mesh *)ob_eval->runtime.data_orig : + (Mesh *)ob_eval->data; + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type); + Mesh *result = nullptr; KeyBlock *kb; ModifierEvalContext mectx = {depsgraph, ob_eval, MOD_APPLY_TO_BASE_MESH}; @@ -1377,12 +1395,12 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, return result; } - if (mti->isDisabled && mti->isDisabled(scene, md_eval, 0)) { + if (mti->isDisabled && mti->isDisabled(scene, md_eval, false)) { return result; } if (build_shapekey_layers && me->key && - (kb = BLI_findlink(&me->key->block, ob_eval->shapenr - 1))) { + (kb = (KeyBlock *)BLI_findlink(&me->key->block, ob_eval->shapenr - 1))) { BKE_keyblock_convert_to_mesh(kb, me); } @@ -1390,7 +1408,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, int numVerts; float(*deformedVerts)[3] = BKE_mesh_vert_coords_alloc(me, &numVerts); - result = (Mesh *)BKE_id_copy_ex(NULL, &me->id, NULL, LIB_ID_COPY_LOCALIZE); + result = (Mesh *)BKE_id_copy_ex(nullptr, &me->id, nullptr, LIB_ID_COPY_LOCALIZE); mti->deformVerts(md_eval, &mectx, result, deformedVerts, numVerts); BKE_mesh_vert_coords_apply(result, deformedVerts); @@ -1401,7 +1419,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, MEM_freeN(deformedVerts); } else { - Mesh *mesh_temp = (Mesh *)BKE_id_copy_ex(NULL, &me->id, NULL, LIB_ID_COPY_LOCALIZE); + Mesh *mesh_temp = (Mesh *)BKE_id_copy_ex(nullptr, &me->id, nullptr, LIB_ID_COPY_LOCALIZE); if (build_shapekey_layers) { add_shapekey_layers(mesh_temp, me); @@ -1411,7 +1429,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, ASSERT_IS_VALID_MESH(result); if (mesh_temp != result) { - BKE_id_free(NULL, mesh_temp); + BKE_id_free(nullptr, mesh_temp); } } @@ -1434,7 +1452,7 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act &mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)]; float(*cos)[3], (*kbcos)[3]; - for (kb = mesh_dst->key->block.first; kb; kb = kb->next) { + for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) { if (kb->uid == layer->uid) { break; } @@ -1449,10 +1467,10 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act MEM_freeN(kb->data); } - cos = CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i); + cos = (float(*)[3])CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i); kb->totelem = mesh_src->totvert; - kb->data = kbcos = MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__); + kb->data = kbcos = (float(*)[3])MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__); if (kb->uid == actshape_uid) { MVert *mvert = mesh_src->mvert; @@ -1467,7 +1485,7 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act } } - for (kb = mesh_dst->key->block.first; kb; kb = kb->next) { + for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) { if (kb->totelem != mesh_src->totvert) { if (kb->data) { MEM_freeN(kb->data); @@ -1532,7 +1550,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, int uid; if (ob) { - kb = BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1); + kb = (KeyBlock *)BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1); if (kb) { uid = kb->uid; } @@ -1595,11 +1613,11 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* NOTE(nazgul): maybe some other layers should be copied? */ if (CustomData_has_layer(&mesh_dst->ldata, CD_MDISPS)) { if (totloop == mesh_dst->totloop) { - MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS); + MDisps *mdisps = (MDisps *)CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS); CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop); if (alloctype == CD_ASSIGN) { - /* Assign NULL to prevent double-free. */ - CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, NULL); + /* Assign nullptr to prevent double-free. */ + CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, nullptr); } } } @@ -1621,7 +1639,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, if (tmp.key && !(tmp.id.tag & LIB_TAG_NO_MAIN)) { id_us_min(&tmp.key->id); } - tmp.key = NULL; + tmp.key = nullptr; } /* Clear selection history */ @@ -1648,7 +1666,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, CustomData_free_typemask(&mesh_src->ldata, mesh_src->totloop, ~mask->lmask); CustomData_free_typemask(&mesh_src->pdata, mesh_src->totpoly, ~mask->pmask); } - BKE_id_free(NULL, mesh_src); + BKE_id_free(nullptr, mesh_src); } } @@ -1670,7 +1688,7 @@ void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb) kb->data = MEM_malloc_arrayN(mesh_dst->key->elemsize, mesh_dst->totvert, "kb->data"); kb->totelem = totvert; - fp = kb->data; + fp = (float *)kb->data; mvert = mesh_src->mvert; for (a = 0; a < kb->totelem; a++, fp += 3, mvert++) { diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c index fe6af432314..bc1ffeb8cf4 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.c +++ b/source/blender/blenkernel/intern/mesh_wrapper.c @@ -69,7 +69,9 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, /* Use edit-mesh directly where possible. */ me->runtime.is_original = true; + me->edit_mesh = MEM_dupallocN(em); + me->edit_mesh->is_shallow_copy = true; /* Make sure, we crash if these are ever used. */ #ifdef DEBUG diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index b328a31cda5..b55b02c7bf2 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -1473,7 +1473,6 @@ void BKE_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb, Object fmd->domain->tex_velocity_y = NULL; fmd->domain->tex_velocity_z = NULL; fmd->domain->tex_wt = NULL; - fmd->domain->mesh_velocities = NULL; BLO_read_data_address(reader, &fmd->domain->coba); BLO_read_data_address(reader, &fmd->domain->effector_weights); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 2a0e05a2616..b96e98a58ec 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -79,6 +79,7 @@ #include "NOD_composite.h" #include "NOD_function.h" #include "NOD_geometry.h" +#include "NOD_node_declaration.hh" #include "NOD_shader.h" #include "NOD_socket.h" #include "NOD_texture.h" @@ -510,7 +511,8 @@ 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)) { + else if ((ntree->type == NTREE_GEOMETRY) && + (node->type == GEO_NODE_LEGACY_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); @@ -651,6 +653,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) BLO_read_list(reader, &ntree->nodes); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->typeinfo = nullptr; + node->declaration = nullptr; BLO_read_list(reader, &node->inputs); BLO_read_list(reader, &node->outputs); @@ -688,7 +691,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) BKE_curvemapping_blend_read(reader, (CurveMapping *)node->storage); break; } - case GEO_NODE_ATTRIBUTE_CURVE_MAP: { + case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP: { NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; BLO_read_data_address(reader, &data->curve_vec); if (data->curve_vec) { @@ -887,7 +890,7 @@ void ntreeBlendReadLib(struct BlendLibReader *reader, struct bNodeTree *ntree) * to match the static layout. */ if (!BLO_read_lib_is_undo(reader)) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - node_verify_socket_templates(ntree, node); + node_verify_sockets(ntree, node, false); } } } @@ -1011,6 +1014,11 @@ IDTypeInfo IDType_ID_NT = { static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype) { + if (ntype->declare != nullptr) { + nodeDeclarationEnsure(ntree, node); + node->declaration->build(*ntree, *node); + return; + } bNodeSocketTemplate *sockdef; /* bNodeSocket *sock; */ /* UNUSED */ @@ -1916,6 +1924,14 @@ static void node_socket_free(bNodeTree *UNUSED(ntree), void nodeRemoveSocket(bNodeTree *ntree, bNode *node, bNodeSocket *sock) { + nodeRemoveSocketEx(ntree, node, sock, true); +} + +void nodeRemoveSocketEx(struct bNodeTree *ntree, + struct bNode *node, + struct bNodeSocket *sock, + bool do_id_user) +{ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { if (link->fromsock == sock || link->tosock == sock) { nodeRemLink(ntree, link); @@ -1926,7 +1942,7 @@ void nodeRemoveSocket(bNodeTree *ntree, bNode *node, bNodeSocket *sock) BLI_remlink(&node->inputs, sock); BLI_remlink(&node->outputs, sock); - node_socket_free(ntree, sock, node, true); + node_socket_free(ntree, sock, node, do_id_user); MEM_freeN(sock); node->update |= NODE_UPDATE; @@ -2199,6 +2215,10 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, bNodeLink *link_dst, *link_src; *node_dst = *node_src; + + /* Reset the declaration of the new node. */ + node_dst->declaration = nullptr; + /* can be called for nodes outside a node tree (e.g. clipboard) */ if (ntree) { if (unique_name) { @@ -3086,6 +3106,8 @@ static void node_free_node(bNodeTree *ntree, bNode *node) MEM_freeN(node->prop); } + delete node->declaration; + MEM_freeN(node); if (ntree) { @@ -3872,7 +3894,7 @@ void nodeSetActive(bNodeTree *ntree, bNode *node) } } if ((node->typeinfo->nclass == NODE_CLASS_TEXTURE) || - (node->typeinfo->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE)) { + (node->typeinfo->type == GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE)) { tnode->flag &= ~NODE_ACTIVE_TEXTURE; } } @@ -3882,7 +3904,7 @@ void nodeSetActive(bNodeTree *ntree, bNode *node) node->flag |= NODE_ACTIVE_ID; } if ((node->typeinfo->nclass == NODE_CLASS_TEXTURE) || - (node->typeinfo->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE)) { + (node->typeinfo->type == GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE)) { node->flag |= NODE_ACTIVE_TEXTURE; } } @@ -3916,6 +3938,24 @@ int nodeSocketLinkLimit(const bNodeSocket *sock) return sock->limit; } +/** + * If the node implements a `declare` function, this function makes sure that `node->declaration` + * is up to date. + */ +void nodeDeclarationEnsure(bNodeTree *UNUSED(ntree), bNode *node) +{ + if (node->typeinfo->declare == nullptr) { + return; + } + if (node->declaration != nullptr) { + return; + } + + node->declaration = new blender::nodes::NodeDeclaration(); + blender::nodes::NodeDeclarationBuilder builder{*node->declaration}; + node->typeinfo->declare(builder); +} + /* ************** Node Clipboard *********** */ #define USE_NODE_CB_VALIDATE @@ -4903,6 +4943,7 @@ static void registerCompositNodes() register_node_type_cmp_inpaint(); register_node_type_cmp_despeckle(); register_node_type_cmp_defocus(); + register_node_type_cmp_posterize(); register_node_type_cmp_sunbeams(); register_node_type_cmp_denoise(); register_node_type_cmp_antialiasing(); @@ -5115,6 +5156,9 @@ static void registerGeometryNodes() { register_node_type_geo_group(); + register_node_type_geo_legacy_material_assign(); + register_node_type_geo_legacy_select_by_material(); + register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_clamp(); register_node_type_geo_attribute_color_ramp(); @@ -5123,6 +5167,7 @@ static void registerGeometryNodes() register_node_type_geo_attribute_convert(); register_node_type_geo_attribute_curve_map(); register_node_type_geo_attribute_fill(); + register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_map_range(); register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); @@ -5138,6 +5183,7 @@ static void registerGeometryNodes() register_node_type_geo_collection_info(); register_node_type_geo_convex_hull(); register_node_type_geo_curve_endpoints(); + register_node_type_geo_curve_fill(); register_node_type_geo_curve_length(); register_node_type_geo_curve_primitive_bezier_segment(); register_node_type_geo_curve_primitive_circle(); @@ -5156,7 +5202,10 @@ static void registerGeometryNodes() register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); register_node_type_geo_edge_split(); + register_node_type_geo_input_index(); register_node_type_geo_input_material(); + register_node_type_geo_input_normal(); + register_node_type_geo_input_position(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); register_node_type_geo_material_assign(); @@ -5182,8 +5231,9 @@ static void registerGeometryNodes() register_node_type_geo_raycast(); register_node_type_geo_sample_texture(); register_node_type_geo_select_by_handle_type(); - register_node_type_geo_select_by_material(); + register_node_type_geo_material_selection(); register_node_type_geo_separate_components(); + register_node_type_geo_set_position(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); register_node_type_geo_transform(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index c91cf6ed926..465ec9dc665 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -324,9 +324,17 @@ static void object_free_data(ID *id) static void object_make_local(Main *bmain, ID *id, const int flags) { + if (!ID_IS_LINKED(id)) { + return; + } + Object *ob = (Object *)id; const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0; + bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + BLI_assert(force_copy == false || force_copy != force_local); + bool is_local = false, is_lib = false; /* - only lib users: do nothing (unless force_local is set) @@ -336,36 +344,40 @@ static void object_make_local(Main *bmain, ID *id, const int flags) * we always want to localize, and we skip remapping (done later). */ - if (!ID_IS_LINKED(ob)) { - return; + if (!force_local && !force_copy) { + BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); + if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; + } + } } - BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); - - if (lib_local || is_local) { - if (!is_lib) { - BKE_lib_id_clear_library_data(bmain, &ob->id); - BKE_lib_id_expand_local(bmain, &ob->id); - if (clear_proxy) { - if (ob->proxy_from != NULL) { - ob->proxy_from->proxy = NULL; - ob->proxy_from->proxy_group = NULL; - } - ob->proxy = ob->proxy_from = ob->proxy_group = NULL; + if (force_local) { + BKE_lib_id_clear_library_data(bmain, &ob->id); + BKE_lib_id_expand_local(bmain, &ob->id); + if (clear_proxy) { + if (ob->proxy_from != NULL) { + ob->proxy_from->proxy = NULL; + ob->proxy_from->proxy_group = NULL; } + ob->proxy = ob->proxy_from = ob->proxy_group = NULL; } - else { - Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id); - id_us_min(&ob_new->id); + } + else if (force_copy) { + Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id); + id_us_min(&ob_new->id); - ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = NULL; + ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = NULL; - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(ob, ob_new); + /* setting newid is mandatory for complex make_lib_local logic... */ + ID_NEW_SET(ob, ob_new); - if (!lib_local) { - BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE); - } + if (!lib_local) { + BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE); } } } @@ -1329,6 +1341,11 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) { const ModifierTypeInfo *mti = BKE_modifier_get_info(modifier_type); + /* Surface and lattice objects don't output geometry sets. */ + if (mti->modifyGeometrySet != NULL && ELEM(ob->type, OB_SURF, OB_LATTICE)) { + return false; + } + /* Only geometry objects should be able to get modifiers T25291. */ if (ob->type == OB_HAIR) { return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); @@ -1979,8 +1996,7 @@ int BKE_object_visibility(const Object *ob, const int dag_eval_mode) visibility |= OB_VISIBLE_INSTANCES; } - if (ob->runtime.geometry_set_eval != NULL && - BKE_geometry_set_has_instances(ob->runtime.geometry_set_eval)) { + if (BKE_object_has_geometry_set_instances(ob)) { visibility |= OB_VISIBLE_INSTANCES; } @@ -3307,8 +3323,8 @@ static void ob_parbone(Object *ob, Object *par, float r_mat[4][4]) /* Make sure the bone is still valid */ bPoseChannel *pchan = BKE_pose_channel_find_name(par->pose, ob->parsubstr); if (!pchan || !pchan->bone) { - CLOG_ERROR( - &LOG, "Object %s with Bone parent: bone %s doesn't exist", ob->id.name + 2, ob->parsubstr); + CLOG_WARN( + &LOG, "Parent Bone: '%s' for Object: '%s' doesn't exist", ob->parsubstr, ob->id.name + 2); unit_m4(r_mat); return; } @@ -5307,7 +5323,7 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) unsigned int i; Mesh *me_eval = ob->runtime.mesh_deform_eval ? ob->runtime.mesh_deform_eval : - ob->runtime.mesh_deform_eval; + BKE_object_get_evaluated_mesh(ob); const int *index; if (me_eval && (index = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX))) { @@ -5737,3 +5753,21 @@ void BKE_object_modifiers_lib_link_common(void *userData, id_us_plus_no_lib(*idpoin); } } + +void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data) +{ + ob->type = BKE_object_obdata_to_type(new_data); + ob->data = new_data; + ob->runtime.geometry_set_eval = NULL; + ob->runtime.data_eval = NULL; + if (ob->runtime.bb != NULL) { + ob->runtime.bb->flag |= BOUNDBOX_DIRTY; + } + ob->id.py_instance = NULL; +} + +bool BKE_object_supports_material_slots(struct Object *ob) +{ + return ELEM( + ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME); +} diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index a46ac4b1175..04739ec19d3 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -194,6 +194,7 @@ static DupliObject *make_dupli(const DupliContext *ctx, } dob->ob = ob; + dob->ob_data = (ID *)ob->data; mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat); dob->type = ctx->gen->type; @@ -834,14 +835,59 @@ static const DupliGenerator gen_dupli_verts_pointcloud = { /** \name Instances Geometry Component Implementation * \{ */ -static void make_duplis_instances_component(const DupliContext *ctx) +static void make_duplis_geometry_set_impl(const DupliContext *ctx, + const GeometrySet &geometry_set, + const float parent_transform[4][4], + bool geometry_set_is_instance) { - const InstancesComponent *component = - ctx->object->runtime.geometry_set_eval->get_component_for_read<InstancesComponent>(); + int component_index = 0; + if (ctx->object->type != OB_MESH || geometry_set_is_instance) { + const Mesh *mesh = geometry_set.get_mesh_for_read(); + if (mesh != nullptr) { + DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); + dupli->ob_data = (ID *)mesh; + } + } + if (ctx->object->type != OB_VOLUME || geometry_set_is_instance) { + const Volume *volume = geometry_set.get_volume_for_read(); + if (volume != nullptr) { + DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); + dupli->ob_data = (ID *)volume; + } + } + if (!ELEM(ctx->object->type, OB_CURVE, OB_FONT) || geometry_set_is_instance) { + const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); + if (curve_component != nullptr) { + const Curve *curve = curve_component->get_curve_for_render(); + if (curve != nullptr) { + DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); + dupli->ob_data = (ID *)curve; + } + } + } + if (ctx->object->type != OB_POINTCLOUD || geometry_set_is_instance) { + const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read(); + if (pointcloud != nullptr) { + DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); + dupli->ob_data = (ID *)pointcloud; + } + } + const bool creates_duplis_for_components = component_index >= 1; + + const InstancesComponent *component = geometry_set.get_component_for_read<InstancesComponent>(); if (component == nullptr) { return; } + const DupliContext *instances_ctx = ctx; + /* Create a sub-context if some duplis were created above. This is to avoid dupli id collisions + * between the instances component below and the other components above. */ + DupliContext new_instances_ctx; + if (creates_duplis_for_components) { + copy_dupli_context(&new_instances_ctx, ctx, ctx->object, nullptr, component_index); + instances_ctx = &new_instances_ctx; + } + 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(); @@ -855,13 +901,13 @@ static void make_duplis_instances_component(const DupliContext *ctx) 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); + mul_m4_m4m4(matrix, parent_transform, instance_offset_matrices[i].values); + make_dupli(instances_ctx, &object, matrix, id); float space_matrix[4][4]; 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); + mul_m4_m4_pre(space_matrix, parent_transform); + make_recursive_duplis(instances_ctx, &object, space_matrix, id); break; } case InstanceReference::Type::Collection: { @@ -870,23 +916,36 @@ static void make_duplis_instances_component(const DupliContext *ctx) unit_m4(collection_matrix); 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); + mul_m4_m4_pre(collection_matrix, parent_transform); + + DupliContext sub_ctx; + copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id); - eEvaluationMode mode = DEG_get_mode(ctx->depsgraph); + eEvaluationMode mode = DEG_get_mode(instances_ctx->depsgraph); + int object_id = 0; FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) { - if (object == ctx->object) { + if (object == instances_ctx->object) { continue; } float instance_matrix[4][4]; mul_m4_m4m4(instance_matrix, collection_matrix, object->obmat); - make_dupli(ctx, object, instance_matrix, id); - make_recursive_duplis(ctx, object, collection_matrix, id); + make_dupli(&sub_ctx, object, instance_matrix, object_id++); + make_recursive_duplis(&sub_ctx, object, collection_matrix, object_id++); } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; break; } + case InstanceReference::Type::GeometrySet: { + float new_transform[4][4]; + mul_m4_m4m4(new_transform, parent_transform, instance_offset_matrices[i].values); + + DupliContext sub_ctx; + copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id); + make_duplis_geometry_set_impl(&sub_ctx, reference.geometry_set(), new_transform, true); + break; + } case InstanceReference::Type::None: { break; } @@ -894,9 +953,15 @@ static void make_duplis_instances_component(const DupliContext *ctx) } } -static const DupliGenerator gen_dupli_instances_component = { +static void make_duplis_geometry_set(const DupliContext *ctx) +{ + const GeometrySet *geometry_set = ctx->object->runtime.geometry_set_eval; + make_duplis_geometry_set_impl(ctx, *geometry_set, ctx->object->obmat, false); +} + +static const DupliGenerator gen_dupli_geometry_set = { 0, - make_duplis_instances_component, + make_duplis_geometry_set, }; /** \} */ @@ -1567,8 +1632,8 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) } if (ctx->object->runtime.geometry_set_eval != nullptr) { - if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) { - return &gen_dupli_instances_component; + if (BKE_object_has_geometry_set_instances(ctx->object)) { + return &gen_dupli_geometry_set; } } diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 78e7e11c248..baff1bb47cc 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -576,26 +576,42 @@ static void unpack_generate_paths(const char *name, } } +char *BKE_packedfile_unpack(Main *bmain, + ReportList *reports, + ID *id, + const char *orig_file_path, + PackedFile *pf, + enum ePF_FileStatus how) +{ + char localname[FILE_MAX], absname[FILE_MAX]; + char *new_name = NULL; + + if (id != NULL) { + unpack_generate_paths( + orig_file_path, id, absname, localname, sizeof(absname), sizeof(localname)); + new_name = BKE_packedfile_unpack_to_file( + reports, BKE_main_blendfile_path(bmain), absname, localname, pf, how); + } + + return new_name; +} + int BKE_packedfile_unpack_vfont(Main *bmain, ReportList *reports, VFont *vfont, enum ePF_FileStatus how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; int ret_value = RET_ERROR; + if (vfont) { + char *new_file_path = BKE_packedfile_unpack( + bmain, reports, (ID *)vfont, vfont->filepath, vfont->packedfile, how); - if (vfont != NULL) { - unpack_generate_paths( - vfont->filepath, (ID *)vfont, absname, localname, sizeof(absname), sizeof(localname)); - newname = BKE_packedfile_unpack_to_file( - reports, BKE_main_blendfile_path(bmain), absname, localname, vfont->packedfile, how); - if (newname != NULL) { + if (new_file_path != NULL) { ret_value = RET_OK; BKE_packedfile_free(vfont->packedfile); vfont->packedfile = NULL; - BLI_strncpy(vfont->filepath, newname, sizeof(vfont->filepath)); - MEM_freeN(newname); + BLI_strncpy(vfont->filepath, new_file_path, sizeof(vfont->filepath)); + MEM_freeN(new_file_path); } } @@ -607,18 +623,14 @@ int BKE_packedfile_unpack_sound(Main *bmain, bSound *sound, enum ePF_FileStatus how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; int ret_value = RET_ERROR; if (sound != NULL) { - unpack_generate_paths( - sound->filepath, (ID *)sound, absname, localname, sizeof(absname), sizeof(localname)); - newname = BKE_packedfile_unpack_to_file( - reports, BKE_main_blendfile_path(bmain), absname, localname, sound->packedfile, how); - if (newname != NULL) { - BLI_strncpy(sound->filepath, newname, sizeof(sound->filepath)); - MEM_freeN(newname); + char *new_file_path = BKE_packedfile_unpack( + bmain, reports, (ID *)sound, sound->filepath, sound->packedfile, how); + if (new_file_path != NULL) { + BLI_strncpy(sound->filepath, new_file_path, sizeof(sound->filepath)); + MEM_freeN(new_file_path); BKE_packedfile_free(sound->packedfile); sound->packedfile = NULL; @@ -641,16 +653,11 @@ int BKE_packedfile_unpack_image(Main *bmain, if (ima != NULL) { while (ima->packedfiles.last) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; ImagePackedFile *imapf = ima->packedfiles.last; + char *new_file_path = BKE_packedfile_unpack( + bmain, reports, (ID *)ima, imapf->filepath, imapf->packedfile, how); - unpack_generate_paths( - imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname)); - newname = BKE_packedfile_unpack_to_file( - reports, BKE_main_blendfile_path(bmain), absname, localname, imapf->packedfile, how); - - if (newname != NULL) { + if (new_file_path != NULL) { ImageView *iv; ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK; @@ -660,14 +667,14 @@ int BKE_packedfile_unpack_image(Main *bmain, /* update the new corresponding view filepath */ iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath)); if (iv) { - BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath)); + BLI_strncpy(iv->filepath, new_file_path, sizeof(imapf->filepath)); } /* keep the new name in the image for non-pack specific reasons */ if (how != PF_REMOVE) { - BLI_strncpy(ima->filepath, newname, sizeof(imapf->filepath)); + BLI_strncpy(ima->filepath, new_file_path, sizeof(imapf->filepath)); } - MEM_freeN(newname); + MEM_freeN(new_file_path); } else { ret_value = RET_ERROR; @@ -690,18 +697,14 @@ int BKE_packedfile_unpack_volume(Main *bmain, Volume *volume, enum ePF_FileStatus how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newfilepath; int ret_value = RET_ERROR; if (volume != NULL) { - unpack_generate_paths( - volume->filepath, (ID *)volume, absname, localname, sizeof(absname), sizeof(localname)); - newfilepath = BKE_packedfile_unpack_to_file( - reports, BKE_main_blendfile_path(bmain), absname, localname, volume->packedfile, how); - if (newfilepath != NULL) { - BLI_strncpy(volume->filepath, newfilepath, sizeof(volume->filepath)); - MEM_freeN(newfilepath); + char *new_file_path = BKE_packedfile_unpack( + bmain, reports, (ID *)volume, volume->filepath, volume->packedfile, how); + if (new_file_path != NULL) { + BLI_strncpy(volume->filepath, new_file_path, sizeof(volume->filepath)); + MEM_freeN(new_file_path); BKE_packedfile_free(volume->packedfile); volume->packedfile = NULL; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 93cffcf7164..c30f94a4cf6 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -914,7 +914,7 @@ static void long_edge_queue_edge_add_recursive( for (int i = 0; i < ARRAY_SIZE(l_adjacent); i++) { float len_sq_other = BM_edge_calc_length_squared(l_adjacent[i]->e); if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) { - // edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other); + // edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other); long_edge_queue_edge_add_recursive( eq_ctx, l_adjacent[i]->radial_next, l_adjacent[i], len_sq_other, limit_len); } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 065240bddbc..73e25a22225 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -312,7 +312,7 @@ IDTypeInfo IDType_ID_SCR = { .name = "Screen", .name_plural = "screens", .translation_context = BLT_I18NCONTEXT_ID_SCREEN, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 732fabc6582..a8871777420 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -19,6 +19,8 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" #include "BKE_spline.hh" #include "FN_generic_virtual_array.hh" @@ -28,6 +30,8 @@ using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::attribute_math::convert_to_static_type; +using blender::bke::AttributeIDRef; using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray; @@ -110,6 +114,31 @@ void Spline::transform(const blender::float4x4 &matrix) this->mark_cache_invalid(); } +void Spline::reverse() +{ + this->positions().reverse(); + this->radii().reverse(); + this->tilts().reverse(); + + this->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + std::optional<blender::fn::GMutableSpan> attribute = this->attributes.get_for_write(id); + if (!attribute) { + BLI_assert_unreachable(); + return false; + } + convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + attribute->typed<T>().reverse(); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + this->reverse_impl(); + this->mark_cache_invalid(); +} + int Spline::evaluated_edges_size() const { const int eval_size = this->evaluated_points_size(); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index b6764f65631..79d2137ee84 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -166,6 +166,17 @@ MutableSpan<float3> BezierSpline::handle_positions_right() return handle_positions_right_; } +void BezierSpline::reverse_impl() +{ + this->handle_positions_left().reverse(); + this->handle_positions_right().reverse(); + std::swap(this->handle_positions_left_, this->handle_positions_right_); + + this->handle_types_left().reverse(); + this->handle_types_right().reverse(); + std::swap(this->handle_types_left_, this->handle_types_right_); +} + static float3 previous_position(Span<float3> positions, const bool cyclic, const int i) { if (i == 0) { diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index ac6f1bd082c..6d30d8ba916 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -142,6 +142,11 @@ Span<float> NURBSpline::weights() const return weights_; } +void NURBSpline::reverse_impl() +{ + this->weights().reverse(); +} + void NURBSpline::mark_cache_invalid() { basis_cache_dirty_ = true; diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index dfd24b2566e..338b5d0ac9e 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -91,6 +91,10 @@ Span<float> PolySpline::tilts() const return tilts_; } +void PolySpline::reverse_impl() +{ +} + void PolySpline::mark_cache_invalid() { tangent_cache_dirty_ = true; diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index 95436372a65..29f726ece71 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -439,17 +439,15 @@ static void studiolight_load_equirect_image(StudioLight *sl) if (ctx.diffuse_pass != NULL) { float *converted_pass = studiolight_multilayer_convert_pass( ibuf, ctx.diffuse_pass, ctx.num_diffuse_channels); - diffuse_ibuf = IMB_allocFromBuffer( + diffuse_ibuf = IMB_allocFromBufferOwn( NULL, converted_pass, ibuf->x, ibuf->y, ctx.num_diffuse_channels); - MEM_freeN(converted_pass); } if (ctx.specular_pass != NULL) { float *converted_pass = studiolight_multilayer_convert_pass( ibuf, ctx.specular_pass, ctx.num_specular_channels); - specular_ibuf = IMB_allocFromBuffer( + specular_ibuf = IMB_allocFromBufferOwn( NULL, converted_pass, ibuf->x, ibuf->y, ctx.num_specular_channels); - MEM_freeN(converted_pass); } IMB_exr_close(ibuf->userdata); @@ -1148,12 +1146,11 @@ static void studiolight_calculate_irradiance_equirect_image(StudioLight *sl) } ITER_PIXELS_END; - sl->equirect_irradiance_buffer = IMB_allocFromBuffer(NULL, - colbuf, - STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH, - STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT, - 4); - MEM_freeN(colbuf); + sl->equirect_irradiance_buffer = IMB_allocFromBufferOwn(NULL, + colbuf, + STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH, + STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT, + 4); } sl->flag |= STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED; } diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index da6ee8d8779..e9cd0b70019 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -1232,7 +1232,7 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, // BKE_mesh_validate(result, true, true); BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); if (!subdiv_context.can_evaluate_normals) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } /* Free used memory. */ subdiv_mesh_context_free(&subdiv_context); diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 6b7b3213a83..f67bf68010d 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -342,9 +342,9 @@ int txt_extended_ascii_as_utf8(char **str) return added; } -// this function removes any control characters from -// a textline and fixes invalid utf-8 sequences - +/** + * Removes any control characters from a text-line and fixes invalid UTF8 sequences. + */ static void cleanup_textline(TextLine *tl) { int i; diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index 0ca2b97b4ef..db5184edfd2 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -744,16 +744,15 @@ static UndoStep *undosys_step_iter_first(UndoStep *us_reference, const eUndoStep /** * Undo/Redo until the given `us_target` step becomes the active (currently loaded) one. * - * \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` will become the - * active step. + * \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` + * will become the active step. * - * \note In case `use_skip` is true, the final target will always be **beyond** the given one (if - * the given one has to be skipped). + * \note In case `use_skip` is true, the final target will always be **beyond** the given one + * (if the given one has to be skipped). * - * \param us_reference If NULL, will be set to current active step in the undo stack. Otherwise, it - * is assumed to match the current state, and will be used as basis for the - * undo/redo process (i.e. all steps in-between `us_reference` and `us_target` - * will be processed). + * \param us_reference: If NULL, will be set to current active step in the undo stack. Otherwise, + * it is assumed to match the current state, and will be used as basis for the undo/redo process + * (i.e. all steps in-between `us_reference` and `us_target` will be processed). */ bool BKE_undosys_step_load_data_ex(UndoStack *ustack, bContext *C, diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 329633c6759..3c168a6c7b2 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -186,7 +186,7 @@ IDTypeInfo IDType_ID_WS = { .name = "WorkSpace", .name_plural = "workspaces", .translation_context = BLT_I18NCONTEXT_ID_WORKSPACE, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = workspace_init_data, .copy_data = NULL, diff --git a/source/blender/blenlib/BLI_float4.hh b/source/blender/blenlib/BLI_float4.hh new file mode 100644 index 00000000000..b1feee3121b --- /dev/null +++ b/source/blender/blenlib/BLI_float4.hh @@ -0,0 +1,86 @@ +/* + * 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 + +namespace blender { + +struct float4 { + float x, y, z, w; + + float4() = default; + + float4(const float *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}, w{ptr[3]} + { + } + + explicit float4(float value) : x(value), y(value), z(value), w(value) + { + } + + explicit float4(int value) : x(value), y(value), z(value), w(value) + { + } + + float4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) + { + } + + operator float *() + { + return &x; + } + + operator const float *() const + { + return &x; + } + + float4 &operator+=(const float4 &other) + { + x += other.x; + y += other.y; + z += other.z; + w += other.w; + return *this; + } + + friend float4 operator+(const float4 &a, const float4 &b) + { + return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; + } + + float4 &operator*=(float factor) + { + x *= factor; + y *= factor; + z *= factor; + w *= factor; + return *this; + } + + friend float4 operator*(const float4 &a, float b) + { + return {a.x * b, a.y * b, a.z * b, a.w * b}; + } + + friend float4 operator*(float a, const float4 &b) + { + return b * a; + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh index fbed321534c..11ff7d040aa 100644 --- a/source/blender/blenlib/BLI_hash.hh +++ b/source/blender/blenlib/BLI_hash.hh @@ -250,6 +250,20 @@ template<typename T> struct DefaultHash<std::unique_ptr<T>> { } }; +template<typename T> struct DefaultHash<std::shared_ptr<T>> { + uint64_t operator()(const std::shared_ptr<T> &value) const + { + return get_default_hash(value.get()); + } +}; + +template<typename T> struct DefaultHash<std::reference_wrapper<T>> { + uint64_t operator()(const std::reference_wrapper<T> &value) const + { + return get_default_hash(value.get()); + } +}; + template<typename T1, typename T2> struct DefaultHash<std::pair<T1, T2>> { uint64_t operator()(const std::pair<T1, T2> &value) const { diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index 7a3169520ca..ad030e127fe 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -39,6 +39,7 @@ #include "BLI_index_range.hh" #include "BLI_span.hh" +#include "BLI_vector.hh" namespace blender { @@ -221,6 +222,8 @@ class IndexMask { { return indices_.is_empty(); } + + IndexMask slice_and_offset(IndexRange slice, Vector<int64_t> &r_new_indices) const; }; } // namespace blender diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh new file mode 100644 index 00000000000..760ff082d06 --- /dev/null +++ b/source/blender/blenlib/BLI_noise.hh @@ -0,0 +1,72 @@ +/* + * 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_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" + +namespace blender::noise { + +/* Perlin noise in the range [-1, 1]. */ + +float perlin_signed(float position); +float perlin_signed(float2 position); +float perlin_signed(float3 position); +float perlin_signed(float4 position); + +/* Perlin noise in the range [0, 1]. */ + +float perlin(float position); +float perlin(float2 position); +float perlin(float3 position); +float perlin(float4 position); + +/* Fractal perlin noise in the range [0, 1]. */ + +float perlin_fractal(float position, float octaves, float roughness); +float perlin_fractal(float2 position, float octaves, float roughness); +float perlin_fractal(float3 position, float octaves, float roughness); +float perlin_fractal(float4 position, float octaves, float roughness); + +/* Positive distorted fractal perlin noise. */ + +float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion); +float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion); +float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion); +float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion); + +/* Positive distorted fractal perlin noise that outputs a float3. */ + +float3 perlin_float3_fractal_distorted(float position, + float octaves, + float roughness, + float distortion); +float3 perlin_float3_fractal_distorted(float2 position, + float octaves, + float roughness, + float distortion); +float3 perlin_float3_fractal_distorted(float3 position, + float octaves, + float roughness, + float distortion); +float3 perlin_float3_fractal_distorted(float4 position, + float octaves, + float roughness, + float distortion); + +} // namespace blender::noise diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh index 6a98c2dcc1c..edffb148477 100644 --- a/source/blender/blenlib/BLI_resource_scope.hh +++ b/source/blender/blenlib/BLI_resource_scope.hh @@ -50,11 +50,10 @@ class ResourceScope : NonCopyable, NonMovable { struct ResourceData { void *data; void (*free)(void *data); - const char *debug_name; }; - LinearAllocator<> m_allocator; - Vector<ResourceData> m_resources; + LinearAllocator<> allocator_; + Vector<ResourceData> resources_; public: ResourceScope() = default; @@ -62,8 +61,8 @@ class ResourceScope : NonCopyable, NonMovable { ~ResourceScope() { /* Free in reversed order. */ - for (int64_t i = m_resources.size(); i--;) { - ResourceData &data = m_resources[i]; + for (int64_t i = resources_.size(); i--;) { + ResourceData &data = resources_[i]; data.free(data.data); } } @@ -72,20 +71,17 @@ class ResourceScope : NonCopyable, NonMovable { * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when * the collector is destructed. */ - template<typename T> T *add(std::unique_ptr<T> resource, const char *name) + template<typename T> T *add(std::unique_ptr<T> resource) { BLI_assert(resource.get() != nullptr); T *ptr = resource.release(); if (ptr == nullptr) { return nullptr; } - this->add( - ptr, - [](void *data) { - T *typed_data = reinterpret_cast<T *>(data); - delete typed_data; - }, - name); + this->add(ptr, [](void *data) { + T *typed_data = reinterpret_cast<T *>(data); + delete typed_data; + }); return ptr; } @@ -93,7 +89,7 @@ class ResourceScope : NonCopyable, NonMovable { * Pass ownership of the resource to the ResourceScope. It will be destructed when the * collector is destructed. */ - template<typename T> T *add(destruct_ptr<T> resource, const char *name) + template<typename T> T *add(destruct_ptr<T> resource) { T *ptr = resource.release(); if (ptr == nullptr) { @@ -104,13 +100,10 @@ class ResourceScope : NonCopyable, NonMovable { return ptr; } - this->add( - ptr, - [](void *data) { - T *typed_data = reinterpret_cast<T *>(data); - typed_data->~T(); - }, - name); + this->add(ptr, [](void *data) { + T *typed_data = reinterpret_cast<T *>(data); + typed_data->~T(); + }); return ptr; } @@ -118,22 +111,31 @@ class ResourceScope : NonCopyable, NonMovable { * Pass ownership of some resource to the ResourceScope. The given free function will be * called when the collector is destructed. */ - void add(void *userdata, void (*free)(void *), const char *name) + void add(void *userdata, void (*free)(void *)) { ResourceData data; - data.debug_name = name; data.data = userdata; data.free = free; - m_resources.append(data); + resources_.append(data); } /** * Construct an object with the same value in the ResourceScope and return a reference to the * new value. */ - template<typename T> T &add_value(T &&value, const char *name) + template<typename T> T &add_value(T &&value) { - return this->construct<T>(name, std::forward<T>(value)); + return this->construct<T>(std::forward<T>(value)); + } + + /** + * The passed in function will be called when the scope is destructed. + */ + template<typename Func> void add_destruct_call(Func func) + { + void *buffer = allocator_.allocate(sizeof(Func), alignof(Func)); + new (buffer) Func(std::move(func)); + this->add(buffer, [](void *data) { (*(Func *)data)(); }); } /** @@ -142,37 +144,19 @@ class ResourceScope : NonCopyable, NonMovable { */ LinearAllocator<> &linear_allocator() { - return m_allocator; + return allocator_; } /** * Utility method to construct an instance of type T that will be owned by the ResourceScope. */ - template<typename T, typename... Args> T &construct(const char *name, Args &&...args) + template<typename T, typename... Args> T &construct(Args &&...args) { - destruct_ptr<T> value_ptr = m_allocator.construct<T>(std::forward<Args>(args)...); + destruct_ptr<T> value_ptr = allocator_.construct<T>(std::forward<Args>(args)...); T &value_ref = *value_ptr; - this->add(std::move(value_ptr), name); + this->add(std::move(value_ptr)); return value_ref; } - - /** - * Print the names of all the resources that are owned by this ResourceScope. This can be - * useful for debugging. - */ - void print(StringRef name) const - { - if (m_resources.size() == 0) { - std::cout << "\"" << name << "\" has no resources.\n"; - return; - } - else { - std::cout << "Resources for \"" << name << "\":\n"; - for (const ResourceData &data : m_resources) { - std::cout << " " << data.data << ": " << data.debug_name << '\n'; - } - } - } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_session_uuid.h b/source/blender/blenlib/BLI_session_uuid.h index 05355a85b39..887044e9b54 100644 --- a/source/blender/blenlib/BLI_session_uuid.h +++ b/source/blender/blenlib/BLI_session_uuid.h @@ -18,6 +18,13 @@ /** \file * \ingroup bli + * + * Functions for generating and handling "Session UUIDs". + * + * Note that these are not true universally-unique identifiers, but only unique during the current + * Blender session. + * + * For true UUIDs, see `BLI_uuid.h`. */ #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index e04295b0e51..5adb47ba0b0 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -644,6 +644,16 @@ template<typename T> class MutableSpan { } /** + * Reverse the data in the MutableSpan. + */ + constexpr void reverse() + { + for (const int i : IndexRange(size_ / 2)) { + std::swap(data_[size_ - 1 - i], data_[i]); + } + } + + /** * Returns an (immutable) Span that references the same array. This is usually not needed, * due to implicit conversions. However, sometimes automatic type deduction needs some help. */ diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 0c51d38e813..d3dc05edd9e 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -57,10 +57,20 @@ size_t BLI_strncpy_rlen(char *__restrict dst, size_t BLI_strcpy_rlen(char *__restrict dst, const char *__restrict src) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BLI_str_quoted_substr_range(const char *__restrict str, + const char *__restrict prefix, + int *__restrict r_start, + int *__restrict r_end) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2, 3, 4); +#if 0 /* UNUSED */ char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC; - +#endif +bool BLI_str_quoted_substr(const char *__restrict str, + const char *__restrict prefix, + char *result, + size_t result_maxlen); char *BLI_str_replaceN(const char *__restrict str, const char *__restrict substr_old, const char *__restrict substr_new) ATTR_WARN_UNUSED_RESULT @@ -92,8 +102,15 @@ char *BLI_sprintfN(const char *__restrict format, ...) ATTR_WARN_UNUSED_RESULT size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const size_t dst_maxncpy) ATTR_NONNULL(); +size_t BLI_str_unescape_ex(char *__restrict dst, + const char *__restrict src, + const size_t src_maxncpy, + /* Additional arguments. */ + const size_t dst_maxncpy, + bool *r_is_complete) ATTR_NONNULL(); size_t BLI_str_unescape(char *__restrict dst, const char *__restrict src, const size_t src_maxncpy) ATTR_NONNULL(); + const char *BLI_str_escape_find_quote(const char *str) ATTR_NONNULL(); size_t BLI_str_format_int_grouped(char dst[16], int num) ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_string_utils.h b/source/blender/blenlib/BLI_string_utils.h index 1057e71a6b2..277bb6fac05 100644 --- a/source/blender/blenlib/BLI_string_utils.h +++ b/source/blender/blenlib/BLI_string_utils.h @@ -75,10 +75,10 @@ char *BLI_string_join_array_by_sep_char_with_tableN(char sep, BLI_string_join_array_by_sep_char_with_tableN( \ sep, table, ((const char *[]){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__)) -void BLI_string_flip_side_name(char *r_name, - const char *from_name, - const bool strip_number, - const size_t name_len); +size_t BLI_string_flip_side_name(char *r_name, + const char *from_name, + const bool strip_number, + const size_t name_len); bool BLI_uniquename_cb(UniquenameCheckCallback unique_check, void *arg, diff --git a/source/blender/blenlib/BLI_user_counter.hh b/source/blender/blenlib/BLI_user_counter.hh index 3e6d5af4c3f..8cebadeac4c 100644 --- a/source/blender/blenlib/BLI_user_counter.hh +++ b/source/blender/blenlib/BLI_user_counter.hh @@ -84,12 +84,24 @@ template<typename T> class UserCounter { return data_; } + const T *operator->() const + { + BLI_assert(data_ != nullptr); + return data_; + } + T &operator*() { BLI_assert(data_ != nullptr); return *data_; } + const T &operator*() const + { + BLI_assert(data_ != nullptr); + return *data_; + } + operator bool() const { return data_ != nullptr; diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index 83a2011545a..665226e4d1b 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -685,12 +685,22 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); # define UNUSED(x) UNUSED_##x #endif +/** + * WARNING: this doesn't warn when returning pointer types (because of the placement of `*`). + * Use #UNUSED_FUNCTION_WITH_RETURN_TYPE instead in this case. + */ #if defined(__GNUC__) || defined(__clang__) # define UNUSED_FUNCTION(x) __attribute__((__unused__)) UNUSED_##x #else # define UNUSED_FUNCTION(x) UNUSED_##x #endif +#if defined(__GNUC__) || defined(__clang__) +# define UNUSED_FUNCTION_WITH_RETURN_TYPE(rtype, x) __attribute__((__unused__)) rtype UNUSED_##x +#else +# define UNUSED_FUNCTION_WITH_RETURN_TYPE(rtype, x) rtype UNUSED_##x +#endif + /** * UNUSED_VARS#(a, ...): quiet unused warnings * diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h new file mode 100644 index 00000000000..15350849f67 --- /dev/null +++ b/source/blender/blenlib/BLI_uuid.h @@ -0,0 +1,67 @@ +/* + * 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 bli + * + * Functions for generating and handling UUID structs according to RFC4122. + * + * Note that these are true UUIDs, not to be confused with the "session uuid" defined in + * `BLI_session_uuid.h`. + */ +#include "DNA_uuid_types.h" + +#include "BLI_compiler_attrs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * UUID generator for random (version 4) UUIDs. See RFC4122 section 4.4. + * This function is not thread-safe. */ +UUID BLI_uuid_generate_random(void); + +/** Compare two UUIDs, return true iff they are equal. */ +bool BLI_uuid_equal(UUID uuid1, UUID uuid2); + +/** + * Format UUID as string. + * The buffer must be at least 37 bytes (36 bytes for the UUID + terminating 0). + */ +void BLI_uuid_format(char *buffer, UUID uuid) ATTR_NONNULL(); + +/** + * Parse a string as UUID. + * The string MUST be in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, + * as produced by #BLI_uuid_format(). + * + * Return true if the string could be parsed, and false otherwise. In the latter case, the UUID may + * have been partially updated. + */ +bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) ATTR_NONNULL(); + +#ifdef __cplusplus +} + +# include <ostream> + +/** Output the UUID as formatted ASCII string, see #BLI_uuid_format(). */ +std::ostream &operator<<(std::ostream &stream, UUID uuid); + +#endif diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 24178535068..1eaf007e01b 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -86,6 +86,7 @@ set(SRC intern/hash_md5.c intern/hash_mm2a.c intern/hash_mm3.c + intern/index_mask.cc intern/jitter_2d.c intern/kdtree_1d.c intern/kdtree_2d.c @@ -116,6 +117,7 @@ set(SRC intern/mesh_boolean.cc intern/mesh_intersect.cc intern/noise.c + intern/noise.cc intern/path_util.c intern/polyfill_2d.c intern/polyfill_2d_beautify.c @@ -145,6 +147,7 @@ set(SRC intern/time.c intern/timecode.c intern/timeit.cc + intern/uuid.cc intern/uvproject.c intern/voronoi_2d.c intern/voxel.c @@ -203,6 +206,7 @@ set(SRC BLI_filereader.h BLI_float2.hh BLI_float3.hh + BLI_float4.hh BLI_float4x4.hh BLI_fnmatch.h BLI_function_ref.hh @@ -264,6 +268,7 @@ set(SRC BLI_mpq3.hh BLI_multi_value_map.hh BLI_noise.h + BLI_noise.hh BLI_path_util.h BLI_polyfill_2d.h BLI_polyfill_2d_beautify.h @@ -306,6 +311,7 @@ set(SRC BLI_utildefines_stack.h BLI_utildefines_variadic.h BLI_utility_mixins.hh + BLI_uuid.h BLI_uvproject.h BLI_vector.hh BLI_vector_adaptor.hh @@ -451,6 +457,7 @@ if(WITH_GTESTS) tests/BLI_string_utf8_test.cc tests/BLI_task_graph_test.cc tests/BLI_task_test.cc + tests/BLI_uuid_test.cc tests/BLI_vector_set_test.cc tests/BLI_vector_test.cc tests/BLI_virtual_array_test.cc diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index e1e3aa273b5..34de8fe7f6d 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -369,36 +369,28 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) return vfd; } -static int check_freetypefont(PackedFile *pf) +static bool check_freetypefont(PackedFile *pf) { - FT_Face face; - FT_GlyphSlot glyph; - FT_UInt glyph_index; - int success = 0; + FT_Face face = NULL; + FT_UInt glyph_index = 0; + bool success = false; err = FT_New_Memory_Face(library, pf->data, pf->size, 0, &face); if (err) { - success = 0; + return false; // XXX error("This is not a valid font"); } - else { - glyph_index = FT_Get_Char_Index(face, 'A'); + + FT_Get_First_Char(face, &glyph_index); + if (glyph_index) { err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); - if (err) { - success = 0; - } - else { - glyph = face->glyph; - if (glyph->format == ft_glyph_format_outline) { - success = 1; - } - else { - // XXX error("Selected Font has no outline data"); - success = 0; - } + if (!err) { + success = (face->glyph->format == ft_glyph_format_outline); } } + FT_Done_Face(face); + return success; } @@ -413,7 +405,6 @@ static int check_freetypefont(PackedFile *pf) VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) { VFontData *vfd = NULL; - int success = 0; /* init Freetype */ err = FT_Init_FreeType(&library); @@ -422,9 +413,7 @@ VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) return NULL; } - success = check_freetypefont(pf); - - if (success) { + if (check_freetypefont(pf)) { vfd = objfnt_to_ftvfontdata(pf); } diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc new file mode 100644 index 00000000000..cba985b8a44 --- /dev/null +++ b/source/blender/blenlib/intern/index_mask.cc @@ -0,0 +1,57 @@ +/* + * 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_index_mask.hh" + +namespace blender { + +/** + * Create a sub-mask that is also shifted to the beginning. The shifting to the beginning allows + * code to work with smaller indices, which is more memory efficient. + * + * \return New index mask with the size of #slice. It is either empty or starts with 0. It might + * reference indices that have been appended to #r_new_indices. + * + * Example: + * this: [2, 3, 5, 7, 8, 9, 10] + * slice: ^--------^ + * output: [0, 2, 4, 5] + * + * All the indices in the sub-mask are shifted by 3 towards zero, so that the first index in the + * output is zero. + */ +IndexMask IndexMask::slice_and_offset(const IndexRange slice, Vector<int64_t> &r_new_indices) const +{ + const int slice_size = slice.size(); + if (slice_size == 0) { + return {}; + } + IndexMask sliced_mask{indices_.slice(slice)}; + if (sliced_mask.is_range()) { + return IndexMask(slice_size); + } + const int64_t offset = sliced_mask.indices().first(); + if (offset == 0) { + return sliced_mask; + } + r_new_indices.resize(slice_size); + for (const int i : IndexRange(slice_size)) { + r_new_indices[i] = sliced_mask[i] - offset; + } + return IndexMask(r_new_indices.as_span()); +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc new file mode 100644 index 00000000000..c057c12e543 --- /dev/null +++ b/source/blender/blenlib/intern/noise.cc @@ -0,0 +1,693 @@ +/* + * Adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * 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 <cmath> +#include <cstdint> + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" +#include "BLI_noise.hh" +#include "BLI_utildefines.h" + +namespace blender::noise { +/* ------------------------------ + * Jenkins Lookup3 Hash Functions + * ------------------------------ + * + * https://burtleburtle.net/bob/c/lookup3.c + * + */ + +BLI_INLINE uint32_t hash_bit_rotate(uint32_t x, uint32_t k) +{ + return (x << k) | (x >> (32 - k)); +} + +BLI_INLINE void hash_bit_mix(uint32_t &a, uint32_t &b, uint32_t &c) +{ + a -= c; + a ^= hash_bit_rotate(c, 4); + c += b; + b -= a; + b ^= hash_bit_rotate(a, 6); + a += c; + c -= b; + c ^= hash_bit_rotate(b, 8); + b += a; + a -= c; + a ^= hash_bit_rotate(c, 16); + c += b; + b -= a; + b ^= hash_bit_rotate(a, 19); + a += c; + c -= b; + c ^= hash_bit_rotate(b, 4); + b += a; +} + +BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c) +{ + c ^= b; + c -= hash_bit_rotate(b, 14); + a ^= c; + a -= hash_bit_rotate(c, 11); + b ^= a; + b -= hash_bit_rotate(a, 25); + c ^= b; + c -= hash_bit_rotate(b, 16); + a ^= c; + a -= hash_bit_rotate(c, 4); + b ^= a; + b -= hash_bit_rotate(a, 14); + c ^= b; + c -= hash_bit_rotate(b, 24); +} + +BLI_INLINE uint32_t hash(uint32_t kx) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (1 << 2) + 13; + + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (2 << 2) + 13; + + b += ky; + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (3 << 2) + 13; + + c += kz; + b += ky; + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (4 << 2) + 13; + + a += kx; + b += ky; + c += kz; + hash_bit_mix(a, b, c); + + a += kw; + hash_bit_final(a, b, c); + + return c; +} + +/* Hashing a number of uint32_t into a float in the range [0, 1]. */ + +BLI_INLINE float hash_to_float(uint32_t kx) +{ + return static_cast<float>(hash(kx)) / static_cast<float>(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky) +{ + return static_cast<float>(hash(kx, ky)) / static_cast<float>(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz) +{ + return static_cast<float>(hash(kx, ky, kz)) / static_cast<float>(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +{ + return static_cast<float>(hash(kx, ky, kz, kw)) / static_cast<float>(0xFFFFFFFFu); +} + +/* Hashing a number of floats into a float in the range [0, 1]. */ + +BLI_INLINE uint32_t float_as_uint(float f) +{ + union { + uint32_t i; + float f; + } u; + u.f = f; + return u.i; +} + +BLI_INLINE float hash_to_float(float k) +{ + return hash_to_float(float_as_uint(k)); +} + +BLI_INLINE float hash_to_float(float2 k) +{ + return hash_to_float(float_as_uint(k.x), float_as_uint(k.y)); +} + +BLI_INLINE float hash_to_float(float3 k) +{ + return hash_to_float(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z)); +} + +BLI_INLINE float hash_to_float(float4 k) +{ + return hash_to_float( + float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w)); +} + +/* ------------ + * Perlin Noise + * ------------ + * + * Perlin, Ken. "Improving noise." Proceedings of the 29th annual conference on Computer graphics + * and interactive techniques. 2002. + * + * This implementation is functionally identical to the implementations in EEVEE, OSL, and SVM. So + * any changes should be applied in all relevant implementations. + */ + +/* Linear Interpolation. */ +BLI_INLINE float mix(float v0, float v1, float x) +{ + return (1 - x) * v0 + x * v1; +} + +/* Bilinear Interpolation: + * + * v2 v3 + * @ + + + + @ y + * + + ^ + * + + | + * + + | + * @ + + + + @ @------> x + * v0 v1 + * + */ +BLI_INLINE float mix(float v0, float v1, float v2, float v3, float x, float y) +{ + float x1 = 1.0 - x; + return (1.0 - y) * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x); +} + +/* Trilinear Interpolation: + * + * v6 v7 + * @ + + + + + + @ + * +\ +\ + * + \ + \ + * + \ + \ + * + \ v4 + \ v5 + * + @ + + + +++ + @ z + * + + + + y ^ + * v2 @ + +++ + + + @ v3 + \ | + * \ + \ + \ | + * \ + \ + \| + * \ + \ + +---------> x + * \+ \+ + * @ + + + + + + @ + * v0 v1 + */ +BLI_INLINE float mix(float v0, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6, + float v7, + float x, + float y, + float z) +{ + float x1 = 1.0 - x; + float y1 = 1.0 - y; + float z1 = 1.0 - z; + return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) + + z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x)); +} + +/* Quadrilinear Interpolation. */ +BLI_INLINE float mix(float v0, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6, + float v7, + float v8, + float v9, + float v10, + float v11, + float v12, + float v13, + float v14, + float v15, + float x, + float y, + float z, + float w) +{ + return mix(mix(v0, v1, v2, v3, v4, v5, v6, v7, x, y, z), + mix(v8, v9, v10, v11, v12, v13, v14, v15, x, y, z), + w); +} + +BLI_INLINE float fade(float t) +{ + return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); +} + +BLI_INLINE float negate_if(float value, uint32_t condition) +{ + return (condition != 0u) ? -value : value; +} + +BLI_INLINE float noise_grad(uint32_t hash, float x) +{ + uint32_t h = hash & 15u; + float g = 1u + (h & 7u); + return negate_if(g, h & 8u) * x; +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y) +{ + uint32_t h = hash & 7u; + float u = h < 4u ? x : y; + float v = 2.0 * (h < 4u ? y : x); + return negate_if(u, h & 1u) + negate_if(v, h & 2u); +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z) +{ + uint32_t h = hash & 15u; + float u = h < 8u ? x : y; + float vt = ((h == 12u) || (h == 14u)) ? x : z; + float v = h < 4u ? y : vt; + return negate_if(u, h & 1u) + negate_if(v, h & 2u); +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z, float w) +{ + uint32_t h = hash & 31u; + float u = h < 24u ? x : y; + float v = h < 16u ? y : z; + float s = h < 8u ? z : w; + return negate_if(u, h & 1u) + negate_if(v, h & 2u) + negate_if(s, h & 4u); +} + +BLI_INLINE float floor_fraction(float x, int &i) +{ + i = (int)x - ((x < 0) ? 1 : 0); + return x - i; +} + +BLI_INLINE float perlin_noise(float position) +{ + int X; + + float fx = floor_fraction(position, X); + + float u = fade(fx); + + float r = mix(noise_grad(hash(X), fx), noise_grad(hash(X + 1), fx - 1.0), u); + + return r; +} + +BLI_INLINE float perlin_noise(float2 position) +{ + int X, Y; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + + float u = fade(fx); + float v = fade(fy); + + float r = mix(noise_grad(hash(X, Y), fx, fy), + noise_grad(hash(X + 1, Y), fx - 1.0, fy), + noise_grad(hash(X, Y + 1), fx, fy - 1.0), + noise_grad(hash(X + 1, Y + 1), fx - 1.0, fy - 1.0), + u, + v); + + return r; +} + +BLI_INLINE float perlin_noise(float3 position) +{ + int X, Y, Z; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + float fz = floor_fraction(position.z, Z); + + float u = fade(fx); + float v = fade(fy); + float w = fade(fz); + + float r = mix(noise_grad(hash(X, Y, Z), fx, fy, fz), + noise_grad(hash(X + 1, Y, Z), fx - 1, fy, fz), + noise_grad(hash(X, Y + 1, Z), fx, fy - 1, fz), + noise_grad(hash(X + 1, Y + 1, Z), fx - 1, fy - 1, fz), + noise_grad(hash(X, Y, Z + 1), fx, fy, fz - 1), + noise_grad(hash(X + 1, Y, Z + 1), fx - 1, fy, fz - 1), + noise_grad(hash(X, Y + 1, Z + 1), fx, fy - 1, fz - 1), + noise_grad(hash(X + 1, Y + 1, Z + 1), fx - 1, fy - 1, fz - 1), + u, + v, + w); + + return r; +} + +BLI_INLINE float perlin_noise(float4 position) +{ + int X, Y, Z, W; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + float fz = floor_fraction(position.z, Z); + float fw = floor_fraction(position.w, W); + + float u = fade(fx); + float v = fade(fy); + float t = fade(fz); + float s = fade(fw); + + float r = mix( + noise_grad(hash(X, Y, Z, W), fx, fy, fz, fw), + noise_grad(hash(X + 1, Y, Z, W), fx - 1.0, fy, fz, fw), + noise_grad(hash(X, Y + 1, Z, W), fx, fy - 1.0, fz, fw), + noise_grad(hash(X + 1, Y + 1, Z, W), fx - 1.0, fy - 1.0, fz, fw), + noise_grad(hash(X, Y, Z + 1, W), fx, fy, fz - 1.0, fw), + noise_grad(hash(X + 1, Y, Z + 1, W), fx - 1.0, fy, fz - 1.0, fw), + noise_grad(hash(X, Y + 1, Z + 1, W), fx, fy - 1.0, fz - 1.0, fw), + noise_grad(hash(X + 1, Y + 1, Z + 1, W), fx - 1.0, fy - 1.0, fz - 1.0, fw), + noise_grad(hash(X, Y, Z, W + 1), fx, fy, fz, fw - 1.0), + noise_grad(hash(X + 1, Y, Z, W + 1), fx - 1.0, fy, fz, fw - 1.0), + noise_grad(hash(X, Y + 1, Z, W + 1), fx, fy - 1.0, fz, fw - 1.0), + noise_grad(hash(X + 1, Y + 1, Z, W + 1), fx - 1.0, fy - 1.0, fz, fw - 1.0), + noise_grad(hash(X, Y, Z + 1, W + 1), fx, fy, fz - 1.0, fw - 1.0), + noise_grad(hash(X + 1, Y, Z + 1, W + 1), fx - 1.0, fy, fz - 1.0, fw - 1.0), + noise_grad(hash(X, Y + 1, Z + 1, W + 1), fx, fy - 1.0, fz - 1.0, fw - 1.0), + noise_grad(hash(X + 1, Y + 1, Z + 1, W + 1), fx - 1.0, fy - 1.0, fz - 1.0, fw - 1.0), + u, + v, + t, + s); + + return r; +} + +/* Signed versions of perlin noise in the range [-1, 1]. The scale values were computed + * experimentally by the OSL developers to remap the noise output to the correct range. */ + +float perlin_signed(float position) +{ + return perlin_noise(position) * 0.2500f; +} + +float perlin_signed(float2 position) +{ + return perlin_noise(position) * 0.6616f; +} + +float perlin_signed(float3 position) +{ + return perlin_noise(position) * 0.9820f; +} + +float perlin_signed(float4 position) +{ + return perlin_noise(position) * 0.8344f; +} + +/* Positive versions of perlin noise in the range [0, 1]. */ + +float perlin(float position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float2 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float3 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float4 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +/* Positive fractal perlin noise. */ + +template<typename T> float perlin_fractal_template(T position, float octaves, float roughness) +{ + float fscale = 1.0f; + float amp = 1.0f; + float maxamp = 0.0f; + float sum = 0.0f; + octaves = CLAMPIS(octaves, 0.0f, 16.0f); + int n = static_cast<int>(octaves); + for (int i = 0; i <= n; i++) { + float t = perlin(fscale * position); + sum += t * amp; + maxamp += amp; + amp *= CLAMPIS(roughness, 0.0f, 1.0f); + fscale *= 2.0f; + } + float rmd = octaves - std::floor(octaves); + if (rmd == 0.0f) { + return sum / maxamp; + } + + float t = perlin(fscale * position); + float sum2 = sum + t * amp; + sum /= maxamp; + sum2 /= maxamp + amp; + return (1.0f - rmd) * sum + rmd * sum2; +} + +float perlin_fractal(float position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float2 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float3 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float4 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +/* The following offset functions generate random offsets to be added to + * positions to act as a seed since the noise functions don't have seed values. + * The offset's components are in the range [100, 200], not too high to cause + * bad precision and not too small to be noticeable. We use float seed because + * OSL only support float hashes and we need to maintain compatibility with it. + */ + +BLI_INLINE float random_float_offset(float seed) +{ + return 100.0f + hash_to_float(seed) * 100.0f; +} + +BLI_INLINE float2 random_float2_offset(float seed) +{ + return float2(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f); +} + +BLI_INLINE float3 random_float3_offset(float seed) +{ + return float3(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f); +} + +BLI_INLINE float4 random_float4_offset(float seed) +{ + return float4(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 3.0f)) * 100.0f); +} + +/* Perlin noises to be added to the position to distort other noises. */ + +BLI_INLINE float perlin_distortion(float position, float strength) +{ + return perlin_signed(position + random_float_offset(0.0)) * strength; +} + +BLI_INLINE float2 perlin_distortion(float2 position, float strength) +{ + return float2(perlin_signed(position + random_float2_offset(0.0f)) * strength, + perlin_signed(position + random_float2_offset(1.0f)) * strength); +} + +BLI_INLINE float3 perlin_distortion(float3 position, float strength) +{ + return float3(perlin_signed(position + random_float3_offset(0.0f)) * strength, + perlin_signed(position + random_float3_offset(1.0f)) * strength, + perlin_signed(position + random_float3_offset(2.0f)) * strength); +} + +BLI_INLINE float4 perlin_distortion(float4 position, float strength) +{ + return float4(perlin_signed(position + random_float4_offset(0.0f)) * strength, + perlin_signed(position + random_float4_offset(1.0f)) * strength, + perlin_signed(position + random_float4_offset(2.0f)) * strength, + perlin_signed(position + random_float4_offset(3.0f)) * strength); +} + +/* Positive distorted fractal perlin noise. */ + +float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +/* Positive distorted fractal perlin noise that outputs a float3. The arbitrary seeds are for + * compatibility with shading functions. */ + +float3 perlin_float3_fractal_distorted(float position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float_offset(1.0f), octaves, roughness), + perlin_fractal(position + random_float_offset(2.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float2 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float2_offset(2.0f), octaves, roughness), + perlin_fractal(position + random_float2_offset(3.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float3 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float3_offset(3.0f), octaves, roughness), + perlin_fractal(position + random_float3_offset(4.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float4 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float4_offset(4.0f), octaves, roughness), + perlin_fractal(position + random_float4_offset(5.0f), octaves, roughness)); +} + +} // namespace blender::noise diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 5541d75bc73..0ea784c95b0 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -360,6 +360,27 @@ size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const si return len; } +BLI_INLINE bool str_unescape_pair(char c_next, char *r_out) +{ +#define CASE_PAIR(value_src, value_dst) \ + case value_src: { \ + *r_out = value_dst; \ + return true; \ + } + switch (c_next) { + CASE_PAIR('"', '"'); /* Quote. */ + CASE_PAIR('\\', '\\'); /* Backslash. */ + CASE_PAIR('t', '\t'); /* Tab. */ + CASE_PAIR('n', '\n'); /* Newline. */ + CASE_PAIR('r', '\r'); /* Carriage return. */ + CASE_PAIR('a', '\a'); /* Bell. */ + CASE_PAIR('b', '\b'); /* Backspace. */ + CASE_PAIR('f', '\f'); /* Form-feed. */ + } +#undef CASE_PAIR + return false; +} + /** * This roughly matches C and Python's string escaping with double quotes - `"`. * @@ -368,31 +389,53 @@ size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const si * * \param dst: The destination string, at least the size of `strlen(src) + 1`. * \param src: The escaped source string. - * \param dst_maxncpy: The maximum number of bytes allowable to copy. + * \param src_maxncpy: The maximum number of bytes allowable to copy from `src`. + * \param dst_maxncpy: The maximum number of bytes allowable to copy into `dst`. + * \param r_is_complete: Set to true when + */ +size_t BLI_str_unescape_ex(char *__restrict dst, + const char *__restrict src, + const size_t src_maxncpy, + /* Additional arguments to #BLI_str_unescape */ + const size_t dst_maxncpy, + bool *r_is_complete) +{ + size_t len = 0; + bool is_complete = true; + for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) { + if (UNLIKELY(len == dst_maxncpy)) { + is_complete = false; + break; + } + char c = *src; + if (UNLIKELY(c == '\\') && (str_unescape_pair(*(src + 1), &c))) { + src++; + } + dst[len++] = c; + } + dst[len] = 0; + *r_is_complete = is_complete; + return len; +} + +/** + * See #BLI_str_unescape_ex doc-string. + * + * This function makes the assumption that `dst` always has + * at least `src_maxncpy` bytes available. * - * \note This is used for parsing animation paths in blend files. + * Use #BLI_str_unescape_ex if `dst` has a smaller fixed size. + * + * \note This is used for parsing animation paths in blend files (runs often). */ size_t BLI_str_unescape(char *__restrict dst, const char *__restrict src, const size_t src_maxncpy) { size_t len = 0; - for (size_t i = 0; i < src_maxncpy && (*src != '\0'); i++, src++) { + for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) { char c = *src; - if (c == '\\') { - char c_next = *(src + 1); - if (((c_next == '"') && ((void)(c = '"'), true)) || /* Quote. */ - ((c_next == '\\') && ((void)(c = '\\'), true)) || /* Backslash. */ - ((c_next == 't') && ((void)(c = '\t'), true)) || /* Tab. */ - ((c_next == 'n') && ((void)(c = '\n'), true)) || /* Newline. */ - ((c_next == 'r') && ((void)(c = '\r'), true)) || /* Carriage return. */ - ((c_next == 'a') && ((void)(c = '\a'), true)) || /* Bell. */ - ((c_next == 'b') && ((void)(c = '\b'), true)) || /* Backspace. */ - ((c_next == 'f') && ((void)(c = '\f'), true))) /* Form-feed. */ - { - i++; - src++; - } + if (UNLIKELY(c == '\\') && (str_unescape_pair(*(src + 1), &c))) { + src++; } - dst[len++] = c; } dst[len] = 0; @@ -420,7 +463,58 @@ const char *BLI_str_escape_find_quote(const char *str) } /** - * Makes a copy of the text within the "" that appear after some text `blahblah`. + * Return the range of the quoted string (excluding quotes) `str` after `prefix`. + * + * A version of #BLI_str_quoted_substrN that calculates the range + * instead of un-escaping and allocating the result. + * + * \param str: String potentially including `prefix`. + * \param prefix: Quoted string prefix. + * \param r_start: The start of the quoted string (after the first quote). + * \param r_end: The end of the quoted string (before the last quote). + * \return True when a quoted string range could be found after `prefix`. + */ +bool BLI_str_quoted_substr_range(const char *__restrict str, + const char *__restrict prefix, + int *__restrict r_start, + int *__restrict r_end) +{ + const char *str_start = strstr(str, prefix); + if (str_start == NULL) { + return false; + } + const size_t prefix_len = strlen(prefix); + if (UNLIKELY(prefix_len == 0)) { + BLI_assert_msg(0, + "Zero length prefix passed in, " + "caller must prevent this from happening!"); + return false; + } + BLI_assert_msg(prefix[prefix_len - 1] != '"', + "Prefix includes trailing quote, " + "caller must prevent this from happening!"); + + str_start += prefix_len; + if (UNLIKELY(*str_start != '\"')) { + return false; + } + str_start += 1; + const char *str_end = BLI_str_escape_find_quote(str_start); + if (UNLIKELY(str_end == NULL)) { + return false; + } + + *r_start = (int)(str_start - str); + *r_end = (int)(str_end - str); + return true; +} + +/* NOTE(@campbellbarton): in principal it should be possible to access a quoted string + * with an arbitrary size, currently all callers for this functionality + * happened to use a fixed size buffer, so only #BLI_str_quoted_substr is needed. */ +#if 0 +/** + * Makes a copy of the text within the "" that appear after the contents of \a prefix. * i.e. for string `pose["apples"]` with prefix `pose[`, it will return `apples`. * * \param str: is the entire string to chop. @@ -431,27 +525,49 @@ const char *BLI_str_escape_find_quote(const char *str) */ char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix) { - const char *start_match, *end_match; - - /* get the starting point (i.e. where prefix starts, and add prefix_len+1 - * to it to get be after the first " */ - start_match = strstr(str, prefix); - if (start_match) { - const size_t prefix_len = strlen(prefix); - start_match += prefix_len + 1; - /* get the end point (i.e. where the next occurrence of " is after the starting point) */ - end_match = BLI_str_escape_find_quote(start_match); - if (end_match) { - const size_t escaped_len = (size_t)(end_match - start_match); - char *result = MEM_mallocN(sizeof(char) * (escaped_len + 1), __func__); - const size_t unescaped_len = BLI_str_unescape(result, start_match, escaped_len); - if (unescaped_len != escaped_len) { - result = MEM_reallocN(result, sizeof(char) * (unescaped_len + 1)); - } - return result; - } + int start_match_ofs, end_match_ofs; + if (!BLI_str_quoted_substr_range(str, prefix, &start_match_ofs, &end_match_ofs)) { + return NULL; + } + const size_t escaped_len = (size_t)(end_match_ofs - start_match_ofs); + char *result = MEM_mallocN(sizeof(char) * (escaped_len + 1), __func__); + const size_t unescaped_len = BLI_str_unescape(result, str + start_match_ofs, escaped_len); + if (unescaped_len != escaped_len) { + result = MEM_reallocN(result, sizeof(char) * (unescaped_len + 1)); + } + return result; +} +#endif + +/** + * Fills \a result with text within "" that appear after some the contents of \a prefix. + * i.e. for string `pose["apples"]` with prefix `pose[`, it will return `apples`. + * + * \param str: is the entire string to chop. + * \param prefix: is the part of the string to step over. + * \param result: The buffer to fill. + * \param result_maxlen: The maximum size of the buffer (including nil terminator). + * \return True if the prefix was found and the entire quoted string was copied into result. + * + * Assume that the strings returned must be freed afterwards, + * and that the inputs will contain data we want. + */ +bool BLI_str_quoted_substr(const char *__restrict str, + const char *__restrict prefix, + char *result, + size_t result_maxlen) +{ + int start_match_ofs, end_match_ofs; + if (!BLI_str_quoted_substr_range(str, prefix, &start_match_ofs, &end_match_ofs)) { + return false; + } + const size_t escaped_len = (size_t)(end_match_ofs - start_match_ofs); + bool is_complete; + BLI_str_unescape_ex(result, str + start_match_ofs, escaped_len, result_maxlen, &is_complete); + if (is_complete == false) { + *result = '\0'; } - return NULL; + return is_complete; } /** diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 3a5e2713b76..b9ea538ff24 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -378,7 +378,9 @@ size_t BLI_strncpy_wchar_from_utf8(wchar_t *__restrict dst_w, const size_t maxncpy) { #ifdef WIN32 - return conv_utf_8_to_16(src_c, dst_w, maxncpy); + conv_utf_8_to_16(src_c, dst_w, maxncpy); + /* NOTE: it would be more efficient to calculate the length as part of #conv_utf_8_to_16. */ + return wcslen(dst_w); #else return BLI_str_utf8_as_utf32((char32_t *)dst_w, src_c, maxncpy); #endif diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index d2666c6fe63..bd733aca4f1 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -155,11 +155,12 @@ void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, cons * \param from_name: original name, * assumed to be a pointer to a string of at least \a name_len size. * \param strip_number: If set, remove number extensions. + * \return The number of bytes written into \a r_name. */ -void BLI_string_flip_side_name(char *r_name, - const char *from_name, - const bool strip_number, - const size_t name_len) +size_t BLI_string_flip_side_name(char *r_name, + const char *from_name, + const bool strip_number, + const size_t name_len) { size_t len; char *prefix = alloca(name_len); /* The part before the facing */ @@ -172,12 +173,10 @@ void BLI_string_flip_side_name(char *r_name, *prefix = *suffix = *replace = *number = '\0'; /* always copy the name, since this can be called with an uninitialized string */ - BLI_strncpy(r_name, from_name, name_len); - - len = BLI_strnlen(from_name, name_len); + len = BLI_strncpy_rlen(r_name, from_name, name_len); if (len < 3) { /* we don't do names like .R or .L */ - return; + return len; } /* We first check the case with a .### extension, let's find the last period */ @@ -274,7 +273,7 @@ void BLI_string_flip_side_name(char *r_name, } } - BLI_snprintf(r_name, name_len, "%s%s%s%s", prefix, replace, suffix, number); + return BLI_snprintf_rlen(r_name, name_len, "%s%s%s%s", prefix, replace, suffix, number); } /* Unique name utils. */ diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc new file mode 100644 index 00000000000..7f1ec0e677b --- /dev/null +++ b/source/blender/blenlib/intern/uuid.cc @@ -0,0 +1,114 @@ +/* + * 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 bli + */ + +#include "BLI_uuid.h" + +#include <cstdio> +#include <cstring> +#include <random> + +/* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */ +static_assert(sizeof(UUID) == 16, "expect UUIDs to be 128 bit exactly"); + +UUID BLI_uuid_generate_random() +{ + static std::mt19937_64 rng = []() { + std::mt19937_64 rng; + + /* Ensure the RNG really can output 64-bit values. */ + static_assert(std::mt19937_64::min() == 0LL); + static_assert(std::mt19937_64::max() == 0xffffffffffffffffLL); + + struct timespec ts; + timespec_get(&ts, TIME_UTC); + rng.seed(ts.tv_nsec); + + return rng; + }(); + + UUID uuid; + + /* RFC4122 suggests setting certain bits to a fixed value, and then randomizing the remaining + * bits. The opposite is easier to implement, though, so that's what's done here. */ + + /* Read two 64-bit numbers to randomize all 128 bits of the UUID. */ + uint64_t *uuid_as_int64 = reinterpret_cast<uint64_t *>(&uuid); + uuid_as_int64[0] = rng(); + uuid_as_int64[1] = rng(); + + /* Set the most significant four bits to 0b0100 to indicate version 4 (random UUID). */ + uuid.time_hi_and_version &= ~0xF000; + uuid.time_hi_and_version |= 0x4000; + + /* Set the most significant two bits to 0b10 to indicate compatibility with RFC4122. */ + uuid.clock_seq_hi_and_reserved &= ~0x40; + uuid.clock_seq_hi_and_reserved |= 0x80; + + return uuid; +} + +bool BLI_uuid_equal(const UUID uuid1, const UUID uuid2) +{ + return std::memcmp(&uuid1, &uuid2, sizeof(uuid1)) == 0; +} + +void BLI_uuid_format(char *buffer, const UUID uuid) +{ + std::sprintf(buffer, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, + uuid.time_mid, + uuid.time_hi_and_version, + uuid.clock_seq_hi_and_reserved, + uuid.clock_seq_low, + uuid.node[0], + uuid.node[1], + uuid.node[2], + uuid.node[3], + uuid.node[4], + uuid.node[5]); +} + +bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) +{ + const int num_fields_parsed = std::sscanf( + buffer, + "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", + &uuid->time_low, + &uuid->time_mid, + &uuid->time_hi_and_version, + &uuid->clock_seq_hi_and_reserved, + &uuid->clock_seq_low, + &uuid->node[0], + &uuid->node[1], + &uuid->node[2], + &uuid->node[3], + &uuid->node[4], + &uuid->node[5]); + return num_fields_parsed == 11; +} + +std::ostream &operator<<(std::ostream &stream, UUID uuid) +{ + std::string buffer(36, '\0'); + BLI_uuid_format(buffer.data(), uuid); + stream << buffer; + return stream; +} diff --git a/source/blender/blenlib/intern/winstuff_dir.c b/source/blender/blenlib/intern/winstuff_dir.c index 2dc41bfc54c..6f99ea075bb 100644 --- a/source/blender/blenlib/intern/winstuff_dir.c +++ b/source/blender/blenlib/intern/winstuff_dir.c @@ -24,7 +24,7 @@ #ifdef WIN32 -/* standalone for inclusion in binaries other than blender */ +/* Standalone for inclusion in binaries other than Blender. */ # ifdef USE_STANDALONE # define MEM_mallocN(size, str) ((void)str, malloc(size)) # define MEM_callocN(size, str) ((void)str, calloc(size, 1)) @@ -33,7 +33,7 @@ # include "MEM_guardedalloc.h" # endif -# define WIN32_SKIP_HKEY_PROTECTION // need to use HKEY +# define WIN32_SKIP_HKEY_PROTECTION /* Need to use `HKEY`. */ # include "BLI_utildefines.h" # include "BLI_winstuff.h" # include "utfconv.h" diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc index 14796e6bf71..a91c743b133 100644 --- a/source/blender/blenlib/tests/BLI_color_test.cc +++ b/source/blender/blenlib/tests/BLI_color_test.cc @@ -128,6 +128,6 @@ TEST(color, SceneLinearByteDecoding) EXPECT_NEAR(0.5f, decoded.a, 0.01f); } -/* \} */ +/** \} */ } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc index f221036419e..70e3a99e57a 100644 --- a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc +++ b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc @@ -1842,7 +1842,7 @@ void text_test( std::copy(b_before_arcs_in.vert.begin(), b_before_arcs_in.vert.end(), b_vert.begin()); std::copy(b_before_arcs_in.face.begin(), b_before_arcs_in.face.end(), b_face.begin()); if (num_arc_points > 0) { - b_face[0].pop_last(); // We'll add center point back between arcs for outer face. + b_face[0].pop_last(); /* We'll add center point back between arcs for outer face. */ for (int arc = 0; arc < narcs; ++arc) { int arc_origin_vert; int arc_terminal_vert; diff --git a/source/blender/blenlib/tests/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc index 4d6060e51c9..0778d71df01 100644 --- a/source/blender/blenlib/tests/BLI_index_mask_test.cc +++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc @@ -40,4 +40,28 @@ TEST(index_mask, RangeConstructor) EXPECT_EQ(indices[2], 5); } +TEST(index_mask, SliceAndOffset) +{ + Vector<int64_t> indices; + { + IndexMask mask{IndexRange(10)}; + IndexMask new_mask = mask.slice_and_offset(IndexRange(3, 5), indices); + EXPECT_TRUE(new_mask.is_range()); + EXPECT_EQ(new_mask.size(), 5); + EXPECT_EQ(new_mask[0], 0); + EXPECT_EQ(new_mask[1], 1); + } + { + Vector<int64_t> original_indices = {2, 3, 5, 7, 8, 9, 10}; + IndexMask mask{original_indices.as_span()}; + IndexMask new_mask = mask.slice_and_offset(IndexRange(1, 4), indices); + EXPECT_FALSE(new_mask.is_range()); + EXPECT_EQ(new_mask.size(), 4); + EXPECT_EQ(new_mask[0], 0); + EXPECT_EQ(new_mask[1], 2); + EXPECT_EQ(new_mask[2], 4); + EXPECT_EQ(new_mask[3], 5); + } +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 4d23a53c08a..fb88fb63e53 100644 --- a/source/blender/blenlib/tests/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -362,6 +362,29 @@ TEST(span, ReverseIterator) EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4); } +TEST(span, ReverseMutableSpan) +{ + std::array<int, 0> src0 = {}; + MutableSpan<int> span0 = src0; + span0.reverse(); + EXPECT_EQ_ARRAY(span0.data(), Span<int>({}).data(), 0); + + std::array<int, 1> src1 = {4}; + MutableSpan<int> span1 = src1; + span1.reverse(); + EXPECT_EQ_ARRAY(span1.data(), Span<int>({4}).data(), 1); + + std::array<int, 2> src2 = {4, 5}; + MutableSpan<int> span2 = src2; + span2.reverse(); + EXPECT_EQ_ARRAY(span2.data(), Span<int>({5, 4}).data(), 2); + + std::array<int, 5> src5 = {4, 5, 6, 7, 8}; + MutableSpan<int> span5 = src5; + span5.reverse(); + EXPECT_EQ_ARRAY(span5.data(), Span<int>({8, 7, 6, 5, 4}).data(), 5); +} + TEST(span, MutableReverseIterator) { std::array<int, 4> src = {4, 5, 6, 7}; diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc new file mode 100644 index 00000000000..2c9da920897 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -0,0 +1,132 @@ +/* + * 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 "testing/testing.h" +#include <cstring> + +#include "BLI_uuid.h" + +TEST(BLI_uuid, generate_random) +{ + const UUID uuid = BLI_uuid_generate_random(); + + // The 4 MSbits represent the "version" of the UUID. + const uint16_t version = uuid.time_hi_and_version >> 12; + EXPECT_EQ(version, 4); + + // The 2 MSbits should be 0b10, indicating compliance with RFC4122. + const uint8_t reserved = uuid.clock_seq_hi_and_reserved >> 6; + EXPECT_EQ(reserved, 0b10); +} + +TEST(BLI_uuid, generate_many_random) +{ + const UUID first_uuid = BLI_uuid_generate_random(); + + /* Generate lots of UUIDs to get some indication that the randomness is okay. */ + for (int i = 0; i < 1000000; ++i) { + const UUID uuid = BLI_uuid_generate_random(); + EXPECT_FALSE(BLI_uuid_equal(first_uuid, uuid)); + + // Check that the non-random bits are set according to RFC4122. + const uint16_t version = uuid.time_hi_and_version >> 12; + EXPECT_EQ(version, 4); + const uint8_t reserved = uuid.clock_seq_hi_and_reserved >> 6; + EXPECT_EQ(reserved, 0b10); + } +} + +TEST(BLI_uuid, equality) +{ + const UUID uuid1 = BLI_uuid_generate_random(); + const UUID uuid2 = BLI_uuid_generate_random(); + + EXPECT_TRUE(BLI_uuid_equal(uuid1, uuid1)); + EXPECT_FALSE(BLI_uuid_equal(uuid1, uuid2)); +} + +TEST(BLI_uuid, string_formatting) +{ + UUID uuid; + std::string buffer(36, '\0'); + + memset(&uuid, 0, sizeof(uuid)); + BLI_uuid_format(buffer.data(), uuid); + EXPECT_EQ("00000000-0000-0000-0000-000000000000", buffer); + + /* Demo of where the bits end up in the formatted string. */ + uuid.time_low = 1; + uuid.time_mid = 2; + uuid.time_hi_and_version = 3; + uuid.clock_seq_hi_and_reserved = 4; + uuid.clock_seq_low = 5; + uuid.node[0] = 6; + uuid.node[5] = 7; + BLI_uuid_format(buffer.data(), uuid); + EXPECT_EQ("00000001-0002-0003-0405-060000000007", buffer); + + /* Somewhat more complex bit patterns. This is a version 1 UUID generated from Python. */ + const UUID uuid1 = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; + BLI_uuid_format(buffer.data(), uuid1); + EXPECT_EQ("d30a0e60-14a2-11ec-8b99-f7736944db8b", buffer); + + /* Namespace UUID, example listed in RFC4211. */ + const UUID namespace_dns = { + 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}; + BLI_uuid_format(buffer.data(), namespace_dns); + EXPECT_EQ("6ba7b810-9dad-11d1-80b4-00c04fd430c8", buffer); +} + +TEST(BLI_uuid, string_parsing_ok) +{ + UUID uuid; + std::string buffer(36, '\0'); + + const bool parsed_ok = BLI_uuid_parse_string(&uuid, "d30a0e60-14a2-11ec-8b99-f7736944db8b"); + EXPECT_TRUE(parsed_ok); + BLI_uuid_format(buffer.data(), uuid); + EXPECT_EQ("d30a0e60-14a2-11ec-8b99-f7736944db8b", buffer); +} + +TEST(BLI_uuid, string_parsing_capitalisation) +{ + UUID uuid; + std::string buffer(36, '\0'); + + /* RFC4122 demands acceptance of upper-case hex digits. */ + const bool parsed_ok = BLI_uuid_parse_string(&uuid, "D30A0E60-14A2-11EC-8B99-F7736944DB8B"); + EXPECT_TRUE(parsed_ok); + BLI_uuid_format(buffer.data(), uuid); + + /* Software should still output lower-case hex digits, though. */ + EXPECT_EQ("d30a0e60-14a2-11ec-8b99-f7736944db8b", buffer); +} + +TEST(BLI_uuid, string_parsing_fail) +{ + UUID uuid; + std::string buffer(36, '\0'); + + const bool parsed_ok = BLI_uuid_parse_string(&uuid, "d30a0e60!14a2-11ec-8b99-f7736944db8b"); + EXPECT_FALSE(parsed_ok); +} + +TEST(BLI_uuid, stream_operator) +{ + std::stringstream ss; + const UUID uuid = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; + ss << uuid; + EXPECT_EQ(ss.str(), "d30a0e60-14a2-11ec-8b99-f7736944db8b"); +} diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index f67ff0f7ac7..f88b470809c 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -50,7 +50,7 @@ #include "readfile.h" -#include "BLI_sys_types.h" // needed for intptr_t +#include "BLI_sys_types.h" /* Needed for `intptr_t`. */ #ifdef WIN32 # include "BLI_winstuff.h" diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3e9ea8db758..15653264211 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4500,7 +4500,8 @@ static void add_loose_objects_to_scene(Main *mainvar, * or for a collection when *lib has been set. */ LISTBASE_FOREACH (Object *, ob, &mainvar->objects) { bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0; - if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { + if (do_it || + ((ob->id.tag & LIB_TAG_INDIRECT) != 0 && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { if (do_append) { if (ob->id.us == 0) { do_it = true; @@ -4560,6 +4561,17 @@ static void add_loose_object_data_to_scene(Main *mainvar, active_collection = lc->collection; } + /* Do not re-instantiate obdata IDs that are already instantiated by an object. */ + LISTBASE_FOREACH (Object *, ob, &mainvar->objects) { + if ((ob->id.tag & LIB_TAG_PRE_EXISTING) == 0 && ob->data != NULL) { + ID *obdata = ob->data; + BLI_assert(ID_REAL_USERS(obdata) > 0); + if ((obdata->tag & LIB_TAG_PRE_EXISTING) == 0) { + obdata->tag &= ~LIB_TAG_DOIT; + } + } + } + /* Loop over all ID types, instancing object-data for ID types that have support for it. */ ListBase *lbarray[INDEX_ID_MAX]; int i = set_listbasepointers(mainvar, lbarray); @@ -4648,7 +4660,7 @@ static void add_collections_to_scene(Main *mainvar, LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; if ((ob->id.tag & (LIB_TAG_PRE_EXISTING | LIB_TAG_DOIT | LIB_TAG_INDIRECT)) == 0 && - (ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) { + (ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == false)) { do_add_collection = true; break; } diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 6492f0d1f69..fa15e541e43 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -291,7 +291,7 @@ static void do_version_hue_sat_node(bNodeTree *ntree, bNode *node) } /* Make sure new sockets are properly created. */ - node_verify_socket_templates(ntree, node); + node_verify_sockets(ntree, node, false); /* Convert value from old storage to new sockets. */ NodeHueSat *nhs = node->storage; bNodeSocket *hue = nodeFindSocket(node, SOCK_IN, "Hue"), @@ -357,7 +357,7 @@ static void do_versions_compositor_render_passes(bNodeTree *ntree) */ do_versions_compositor_render_passes_storage(node); /* Make sure new sockets are properly created. */ - node_verify_socket_templates(ntree, node); + node_verify_sockets(ntree, node, false); /* Make sure all possibly created sockets have proper storage. */ do_versions_compositor_render_passes_storage(node); } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index f2a44f500cd..c0cc0819100 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3416,7 +3416,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) case SPACE_FILE: { SpaceFile *sfile = (SpaceFile *)sl; if (sfile->params) { - sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_6 | + sfile->params->flag &= ~(FILE_APPEND_SET_FAKEUSER | FILE_APPEND_RECURSIVE | FILE_OBDATA_INSTANCE); } break; diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 34ca3a400c5..046f3753812 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -688,7 +688,7 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) if (!MAIN_VERSION_ATLEAST(bmain, 293, 16)) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { - seq_update_meta_disp_range(SEQ_editing_get(scene, false)); + seq_update_meta_disp_range(SEQ_editing_get(scene)); } /* Add a separate socket for Grid node X and Y size. */ @@ -830,33 +830,6 @@ static void do_versions_strip_cache_settings_recursive(const ListBase *seqbase) } } -static void version_node_socket_name(bNodeTree *ntree, - const int node_type, - const char *old_name, - const char *new_name) -{ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == node_type) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (STREQ(socket->name, old_name)) { - strcpy(socket->name, new_name); - } - if (STREQ(socket->identifier, old_name)) { - strcpy(socket->identifier, new_name); - } - } - LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - if (STREQ(socket->name, old_name)) { - strcpy(socket->name, new_name); - } - if (STREQ(socket->identifier, old_name)) { - strcpy(socket->identifier, new_name); - } - } - } - } -} - static void version_node_join_geometry_for_multi_input_socket(bNodeTree *ntree) { LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { @@ -1120,8 +1093,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (md->type == eModifierType_MeshSequenceCache) { MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; mcmd->velocity_scale = 1.0f; - mcmd->vertex_velocities = NULL; - mcmd->num_vertices = 0; } } } @@ -1587,7 +1558,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_ATTRIBUTE_MATH && node->storage == NULL) { + if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_MATH && node->storage == NULL) { const int old_use_attibute_a = (1 << 0); const int old_use_attibute_b = (1 << 1); NodeAttributeMath *data = MEM_callocN(sizeof(NodeAttributeMath), "NodeAttributeMath"); @@ -1651,7 +1622,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { continue; } @@ -1748,7 +1719,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) continue; } LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_POINT_INSTANCE && node->storage == NULL) { + if (node->type == GEO_NODE_LEGACY_POINT_INSTANCE && node->storage == NULL) { NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( sizeof(NodeGeometryPointInstance), __func__); data->instance_type = node->custom1; @@ -1765,7 +1736,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_ATTRIBUTE_MATH) { + if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_MATH) { NodeAttributeMath *data = (NodeAttributeMath *)node->storage; data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; } @@ -1824,7 +1795,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) continue; } LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_ATTRIBUTE_RANDOMIZE && node->storage == NULL) { + if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE && node->storage == NULL) { NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN( sizeof(NodeAttributeRandomize), __func__); data->data_type = node->custom1; @@ -1860,7 +1831,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_ATTRIBUTE_PROXIMITY, "Result", "Distance"); + version_node_socket_name(ntree, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Result", "Distance"); } } FOREACH_NODETREE_END; @@ -1869,7 +1840,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 293, 10)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_ATTRIBUTE_PROXIMITY, "Location", "Position"); + version_node_socket_name( + ntree, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Location", "Position"); } } FOREACH_NODETREE_END; @@ -1963,7 +1935,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_ATTRIBUTE_FILL) { + if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_FILL) { node->custom2 = ATTR_DOMAIN_AUTO; } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 2456b22f046..538634f4c9e 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -37,6 +37,8 @@ #include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_genfile.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_lineart_types.h" #include "DNA_listBase.h" #include "DNA_material_types.h" #include "DNA_modifier_types.h" @@ -126,7 +128,7 @@ static void version_idproperty_move_data_float(IDPropertyUIDataFloat *ui_data, } IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max"); if (max != NULL) { - ui_data->max = ui_data->soft_max = IDP_coerce_to_double_or_zero(min); + ui_data->max = ui_data->soft_max = IDP_coerce_to_double_or_zero(max); } IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min"); if (soft_min != NULL) { @@ -237,6 +239,16 @@ static void do_versions_idproperty_bones_recursive(Bone *bone) } } +static void do_versions_idproperty_seq_recursive(ListBase *seqbase) +{ + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + version_idproperty_ui_data(seq->prop); + if (seq->type == SEQ_TYPE_META) { + do_versions_idproperty_seq_recursive(&seq->seqbase); + } + } +} + /** * For every data block that supports them, initialize the new IDProperty UI data struct based on * the old more complicated storage. Assumes only the top level of IDProperties below the parent @@ -276,23 +288,28 @@ static void do_versions_idproperty_ui_data(Main *bmain) } } - /* The UI data from exposed node modifier properties is just copied from the corresponding node - * group, but the copying only runs when necessary, so we still need to version UI data here. */ LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + /* The UI data from exposed node modifier properties is just copied from the corresponding node + * group, but the copying only runs when necessary, so we still need to version data here. */ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type == eModifierType_Nodes) { NodesModifierData *nmd = (NodesModifierData *)md; version_idproperty_ui_data(nmd->settings.properties); } } + + /* Object post bones. */ + if (ob->type == OB_ARMATURE && ob->pose != NULL) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + version_idproperty_ui_data(pchan->prop); + } + } } /* Sequences. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (scene->ed != NULL) { - LISTBASE_FOREACH (Sequence *, seq, &scene->ed->seqbase) { - version_idproperty_ui_data(seq->prop); - } + do_versions_idproperty_seq_recursive(&scene->ed->seqbase); } } } @@ -439,7 +456,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) continue; } LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type != GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) { + if (node->type != GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE) { continue; } if (node->id == NULL) { @@ -519,33 +536,6 @@ static void version_switch_node_input_prefix(Main *bmain) FOREACH_NODETREE_END; } -static void version_node_socket_name(bNodeTree *ntree, - const int node_type, - const char *old_name, - const char *new_name) -{ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == node_type) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (STREQ(socket->name, old_name)) { - strcpy(socket->name, new_name); - } - if (STREQ(socket->identifier, old_name)) { - strcpy(socket->identifier, new_name); - } - } - LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - if (STREQ(socket->name, old_name)) { - strcpy(socket->name, new_name); - } - if (STREQ(socket->identifier, old_name)) { - strcpy(socket->identifier, new_name); - } - } - } - } -} - static bool replace_bbone_len_scale_rnapath(char **p_old_path, int *p_index) { char *old_path = *p_old_path; @@ -636,6 +626,155 @@ static void do_version_constraints_spline_ik_joint_bindings(ListBase *lb) } } +static bNodeSocket *do_version_replace_float_size_with_vector(bNodeTree *ntree, + bNode *node, + bNodeSocket *socket) +{ + const bNodeSocketValueFloat *socket_value = (const bNodeSocketValueFloat *)socket->default_value; + const float old_value = socket_value->value; + nodeRemoveSocket(ntree, node, socket); + bNodeSocket *new_socket = nodeAddSocket( + ntree, node, SOCK_IN, nodeStaticSocketType(SOCK_VECTOR, PROP_TRANSLATION), "Size", "Size"); + bNodeSocketValueVector *value_vector = (bNodeSocketValueVector *)new_socket->default_value; + copy_v3_fl(value_vector->value, old_value); + return new_socket; +} + +static bool geometry_node_is_293_legacy(const short node_type) +{ + switch (node_type) { + /* Not legacy: No attribute inputs or outputs. */ + case GEO_NODE_TRIANGULATE: + case GEO_NODE_EDGE_SPLIT: + case GEO_NODE_TRANSFORM: + case GEO_NODE_BOOLEAN: + case GEO_NODE_SUBDIVISION_SURFACE: + case GEO_NODE_IS_VIEWPORT: + case GEO_NODE_MESH_SUBDIVIDE: + case GEO_NODE_MESH_PRIMITIVE_CUBE: + case GEO_NODE_MESH_PRIMITIVE_CIRCLE: + case GEO_NODE_MESH_PRIMITIVE_UV_SPHERE: + case GEO_NODE_MESH_PRIMITIVE_CYLINDER: + case GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE: + case GEO_NODE_MESH_PRIMITIVE_CONE: + case GEO_NODE_MESH_PRIMITIVE_LINE: + case GEO_NODE_MESH_PRIMITIVE_GRID: + case GEO_NODE_BOUNDING_BOX: + case GEO_NODE_CURVE_RESAMPLE: + case GEO_NODE_INPUT_MATERIAL: + case GEO_NODE_MATERIAL_REPLACE: + case GEO_NODE_CURVE_LENGTH: + case GEO_NODE_CONVEX_HULL: + case GEO_NODE_SEPARATE_COMPONENTS: + case GEO_NODE_CURVE_PRIMITIVE_STAR: + case GEO_NODE_CURVE_PRIMITIVE_SPIRAL: + case GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER: + case GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT: + case GEO_NODE_CURVE_PRIMITIVE_CIRCLE: + case GEO_NODE_VIEWER: + case GEO_NODE_CURVE_PRIMITIVE_LINE: + case GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL: + case GEO_NODE_CURVE_FILL: + case GEO_NODE_CURVE_TRIM: + case GEO_NODE_CURVE_TO_MESH: + return false; + + /* Not legacy: Newly added with fields patch. */ + case GEO_NODE_INPUT_POSITION: + case GEO_NODE_SET_POSITION: + case GEO_NODE_INPUT_INDEX: + case GEO_NODE_INPUT_NORMAL: + case GEO_NODE_ATTRIBUTE_CAPTURE: + return false; + + /* Maybe legacy: Might need special attribute handling, depending on design. */ + case GEO_NODE_SWITCH: + case GEO_NODE_JOIN_GEOMETRY: + case GEO_NODE_ATTRIBUTE_REMOVE: + case GEO_NODE_OBJECT_INFO: + case GEO_NODE_COLLECTION_INFO: + return false; + + /* Maybe legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ + case GEO_NODE_CURVE_ENDPOINTS: + case GEO_NODE_CURVE_TO_POINTS: + return false; + + /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to generate + * a mesh for all grids in the volume. */ + case GEO_NODE_VOLUME_TO_MESH: + return false; + + /* Legacy: Attribute operation completely replaced by field nodes. */ + case GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE: + case GEO_NODE_LEGACY_ATTRIBUTE_MATH: + case GEO_NODE_LEGACY_ATTRIBUTE_FILL: + case GEO_NODE_LEGACY_ATTRIBUTE_MIX: + case GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP: + case GEO_NODE_LEGACY_ATTRIBUTE_COMPARE: + case GEO_NODE_LEGACY_POINT_ROTATE: + case GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR: + case GEO_NODE_LEGACY_POINT_SCALE: + case GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE: + case GEO_NODE_ATTRIBUTE_VECTOR_ROTATE: + case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP: + case GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE: + case GEO_NODE_LECAGY_ATTRIBUTE_CLAMP: + case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH: + case GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ: + case GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ: + return true; + + /* Legacy: Replaced by field node depending on another geometry. */ + case GEO_NODE_LEGACY_RAYCAST: + case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER: + case GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY: + return true; + + /* Legacy: Simple selection attribute input. */ + case GEO_NODE_LEGACY_MESH_TO_CURVE: + case GEO_NODE_LEGACY_POINT_SEPARATE: + case GEO_NODE_LEGACY_CURVE_SELECT_HANDLES: + case GEO_NODE_LEGACY_CURVE_SPLINE_TYPE: + case GEO_NODE_LEGACY_CURVE_REVERSE: + case GEO_NODE_LEGACY_MATERIAL_ASSIGN: + case GEO_NODE_LEGACY_CURVE_SET_HANDLES: + return true; + + /* Legacy: More complex attribute inputs or outputs. */ + case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */ + case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */ + case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */ + case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */ + case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */ + case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */ + case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */ + case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */ + return true; + } + return false; +} + +static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (geometry_node_is_293_legacy(node->type)) { + if (strstr(node->idname, "Legacy")) { + /* Make sure we haven't changed this idname already, better safe than sorry. */ + continue; + } + + char temp_idname[sizeof(node->idname)]; + BLI_strncpy(temp_idname, node->idname, sizeof(node->idname)); + + BLI_snprintf(node->idname, + sizeof(node->idname), + "GeometryNodeLegacy%s", + temp_idname + strlen("GeometryNode")); + } + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -965,7 +1104,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Fix SplineIK constraint's inconsistency between binding points array and its stored size. */ + /* Fix SplineIK constraint's inconsistency between binding points array and its stored size. + */ LISTBASE_FOREACH (Object *, ob, &bmain->objects) { /* NOTE: Objects should never have SplineIK constraint, so no need to apply this fix on * their constraints. */ @@ -1033,8 +1173,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } FOREACH_NODETREE_END; - /* Disable Fade Inactive Overlay by default as it is redundant after introducing flash on mode - * transfer. */ + /* Disable Fade Inactive Overlay by default as it is redundant after introducing flash on + * mode transfer. */ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { @@ -1052,6 +1192,64 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 20)) { + /* Use new vector Size socket in Cube Mesh Primitive node. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { + if (link->tonode->type == GEO_NODE_MESH_PRIMITIVE_CUBE) { + bNode *node = link->tonode; + if (STREQ(link->tosock->identifier, "Size") && link->tosock->type == SOCK_FLOAT) { + bNode *link_fromnode = link->fromnode; + bNodeSocket *link_fromsock = link->fromsock; + bNodeSocket *socket = link->tosock; + BLI_assert(socket); + + bNodeSocket *new_socket = do_version_replace_float_size_with_vector( + ntree, node, socket); + nodeAddLink(ntree, link_fromnode, link_fromsock, node, new_socket); + } + } + } + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_MESH_PRIMITIVE_CUBE) { + continue; + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (STREQ(socket->identifier, "Size") && (socket->type == SOCK_FLOAT)) { + do_version_replace_float_size_with_vector(ntree, node, socket); + break; + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) { + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_geometry_nodes_change_legacy_names(ntree); + } + } + if (!DNA_struct_elem_find( + fd->filesdna, "LineartGpencilModifierData", "bool", "use_crease_on_smooth")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->type == OB_GPENCIL) { + LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { + if (md->type == eGpencilModifierType_Lineart) { + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; + lmd->calculation_flags |= LRT_USE_CREASE_ON_SMOOTH_SURFACES; + } + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -1063,5 +1261,18 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_FILE) { + SpaceFile *sfile = (SpaceFile *)sl; + if (sfile->asset_params) { + sfile->asset_params->base_params.recursion_level = FILE_SELECT_MAX_RECURSIONS; + } + } + } + } + } } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 208c02b60d1..3f13d1ec12e 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -22,6 +22,7 @@ #include <cstring> +#include "DNA_node_types.h" #include "DNA_screen_types.h" #include "BLI_listbase.h" @@ -85,3 +86,30 @@ ID *do_versions_rename_id(Main *bmain, } return id; } + +void version_node_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (STREQ(socket->name, old_name)) { + BLI_strncpy(socket->name, new_name, sizeof(socket->name)); + } + if (STREQ(socket->identifier, old_name)) { + BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); + } + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + if (STREQ(socket->name, old_name)) { + BLI_strncpy(socket->name, new_name, sizeof(socket->name)); + } + if (STREQ(socket->identifier, old_name)) { + BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); + } + } + } + } +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index 47e0b74a3e4..c1fe2b591cd 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -23,6 +23,7 @@ struct ARegion; struct ListBase; struct Main; +struct bNodeTree; #ifdef __cplusplus extern "C" { @@ -38,6 +39,11 @@ ID *do_versions_rename_id(Main *bmain, const char *name_src, const char *name_dst); +void version_node_socket_name(struct bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index ea2673df78a..ccccc6ffc1d 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -52,6 +52,7 @@ #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_curveprofile.h" +#include "BKE_customdata.h" #include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -552,6 +553,11 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) mesh->flag |= ME_REMESH_FIX_POLES | ME_REMESH_REPROJECT_VOLUME; BKE_mesh_smooth_flag_set(mesh, false); } + else { + /* Remove sculpt-mask data in default mesh objects for all non-sculpt templates. */ + CustomData_free_layers(&mesh->vdata, CD_PAINT_MASK, mesh->totvert); + CustomData_free_layers(&mesh->ldata, CD_GRID_PAINT_MASK, mesh->totloop); + } } for (Camera *camera = bmain->cameras.first; camera; camera = camera->id.next) { diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 0042ff29dc2..f4853ff803f 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -885,6 +885,14 @@ void blo_do_versions_userdef(UserDef *userdef) BKE_addon_ensure(&userdef->addons, "pose_library"); } + if (!USER_VERSION_ATLEAST(300, 21)) { + /* Deprecated userdef->flag USER_SAVE_PREVIEWS */ + userdef->file_preview_type = (userdef->flag & USER_FLAG_UNUSED_5) ? USER_FILE_PREVIEW_AUTO : + USER_FILE_PREVIEW_NONE; + /* Clear for reuse. */ + userdef->flag &= ~USER_FLAG_UNUSED_5; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index b63a09a97a6..7865c79323d 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1587,7 +1587,7 @@ static BMOpDefine bmo_create_uvsphere_def = { /* slots_in */ {{"u_segments", BMO_OP_SLOT_INT}, /* number of u segments */ {"v_segments", BMO_OP_SLOT_INT}, /* number of v segment */ - {"diameter", BMO_OP_SLOT_FLT}, /* diameter */ + {"radius", BMO_OP_SLOT_FLT}, /* radius */ {"matrix", BMO_OP_SLOT_MAT}, /* matrix to multiply the new geometry with */ {"calc_uvs", BMO_OP_SLOT_BOOL}, /* calculate default UVs */ {{'\0'}}, @@ -1610,7 +1610,7 @@ static BMOpDefine bmo_create_icosphere_def = { "create_icosphere", /* slots_in */ {{"subdivisions", BMO_OP_SLOT_INT}, /* how many times to recursively subdivide the sphere */ - {"diameter", BMO_OP_SLOT_FLT}, /* diameter */ + {"radius", BMO_OP_SLOT_FLT}, /* radius */ {"matrix", BMO_OP_SLOT_MAT}, /* matrix to multiply the new geometry with */ {"calc_uvs", BMO_OP_SLOT_BOOL}, /* calculate default UVs */ {{'\0'}}, @@ -1656,8 +1656,8 @@ static BMOpDefine bmo_create_cone_def = { {{"cap_ends", BMO_OP_SLOT_BOOL}, /* whether or not to fill in the ends with faces */ {"cap_tris", BMO_OP_SLOT_BOOL}, /* fill ends with triangles instead of ngons */ {"segments", BMO_OP_SLOT_INT}, /* number of vertices in the base circle */ - {"diameter1", BMO_OP_SLOT_FLT}, /* diameter of one end */ - {"diameter2", BMO_OP_SLOT_FLT}, /* diameter of the opposite */ + {"radius1", BMO_OP_SLOT_FLT}, /* radius of one end */ + {"radius2", BMO_OP_SLOT_FLT}, /* radius of the opposite */ {"depth", BMO_OP_SLOT_FLT}, /* distance between ends */ {"matrix", BMO_OP_SLOT_MAT}, /* matrix to multiply the new geometry with */ {"calc_uvs", BMO_OP_SLOT_BOOL}, /* calculate default UVs */ diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index cb5764b1c91..795d8829ee7 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -2637,7 +2637,7 @@ int BM_mesh_calc_face_groups(BMesh *bm, STACK_DECLARE(stack); BMIter iter; - BMFace *f; + BMFace *f, *f_next; int i; STACK_INIT(group_array, bm->totface); @@ -2662,6 +2662,8 @@ int BM_mesh_calc_face_groups(BMesh *bm, /* detect groups */ stack = MEM_mallocN(sizeof(*stack) * tot_faces, __func__); + f_next = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); + while (tot_touch != tot_faces) { int *group_item; bool ok = false; @@ -2670,10 +2672,10 @@ int BM_mesh_calc_face_groups(BMesh *bm, STACK_INIT(stack, tot_faces); - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_TAG) == false) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - STACK_PUSH(stack, f); + for (; f_next; f_next = BM_iter_step(&iter)) { + if (BM_elem_flag_test(f_next, BM_ELEM_TAG) == false) { + BM_elem_flag_enable(f_next, BM_ELEM_TAG); + STACK_PUSH(stack, f_next); ok = true; break; } @@ -2799,9 +2801,8 @@ int BM_mesh_calc_edge_groups(BMesh *bm, STACK_DECLARE(stack); BMIter iter; - BMEdge *e; + BMEdge *e, *e_next; int i; - STACK_INIT(group_array, bm->totedge); /* init the array */ @@ -2822,6 +2823,8 @@ int BM_mesh_calc_edge_groups(BMesh *bm, /* detect groups */ stack = MEM_mallocN(sizeof(*stack) * tot_edges, __func__); + e_next = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); + while (tot_touch != tot_edges) { int *group_item; bool ok = false; @@ -2830,10 +2833,10 @@ int BM_mesh_calc_edge_groups(BMesh *bm, STACK_INIT(stack, tot_edges); - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { - BM_elem_flag_enable(e, BM_ELEM_TAG); - STACK_PUSH(stack, e); + for (; e_next; e_next = BM_iter_step(&iter)) { + if (BM_elem_flag_test(e_next, BM_ELEM_TAG) == false) { + BM_elem_flag_enable(e_next, BM_ELEM_TAG); + STACK_PUSH(stack, e_next); ok = true; break; } diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index 8d5963cfb1c..d8047499780 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -854,7 +854,7 @@ void BM_mesh_calc_uvs_grid(BMesh *bm, void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) { - const float dia = BMO_slot_float_get(op->slots_in, "diameter"); + const float rad = BMO_slot_float_get(op->slots_in, "radius"); const int seg = BMO_slot_int_get(op->slots_in, "u_segments"); const int tot = BMO_slot_int_get(op->slots_in, "v_segments"); @@ -881,8 +881,8 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) const float phi = M_PI * ((double)a / (double)tot); vec[0] = 0.0; - vec[1] = dia * sinf(phi); - vec[2] = dia * cosf(phi); + vec[1] = rad * sinf(phi); + vec[2] = rad * cosf(phi); eve = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); BMO_vert_flag_enable(bm, eve, VERT_MARK); @@ -921,12 +921,12 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) { float len, len2, vec2[3]; - len = 2 * dia * sinf(phid / 2.0f); + len = 2 * rad * sinf(phid / 2.0f); /* Length of one segment in shortest parallel. */ - vec[0] = dia * sinf(phid); + vec[0] = rad * sinf(phid); vec[1] = 0.0f; - vec[2] = dia * cosf(phid); + vec[2] = rad * cosf(phid); mul_v3_m3v3(vec2, cmat, vec); len2 = len_v3v3(vec, vec2); @@ -973,8 +973,8 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op) { - const float dia = BMO_slot_float_get(op->slots_in, "diameter"); - const float dia_div = dia / 200.0f; + const float rad = BMO_slot_float_get(op->slots_in, "radius"); + const float rad_div = rad / 200.0f; const int subdiv = BMO_slot_int_get(op->slots_in, "subdivisions"); const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); @@ -994,9 +994,9 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op) /* phi = 0.25f * (float)M_PI; */ /* UNUSED */ for (a = 0; a < 12; a++) { - vec[0] = dia_div * icovert[a][0]; - vec[1] = dia_div * icovert[a][1]; - vec[2] = dia_div * icovert[a][2]; + vec[0] = rad_div * icovert[a][0]; + vec[1] = rad_div * icovert[a][1]; + vec[2] = rad_div * icovert[a][2]; eva[a] = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); BMO_vert_flag_enable(bm, eva[a], VERT_MARK); @@ -1041,7 +1041,7 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op) "cuts=%i " "use_grid_fill=%b use_sphere=%b", EDGE_MARK, - dia, + rad, (1 << (subdiv - 1)) - 1, true, true); @@ -1392,8 +1392,8 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2; BMFace *f; float vec[3], mat[4][4]; - const float dia1 = BMO_slot_float_get(op->slots_in, "diameter1"); - const float dia2 = BMO_slot_float_get(op->slots_in, "diameter2"); + const float rad1 = BMO_slot_float_get(op->slots_in, "radius1"); + const float rad2 = BMO_slot_float_get(op->slots_in, "radius2"); const float depth_half = 0.5f * BMO_slot_float_get(op->slots_in, "depth"); int segs = BMO_slot_int_get(op->slots_in, "segments"); const bool cap_ends = BMO_slot_bool_get(op->slots_in, "cap_ends"); @@ -1431,15 +1431,14 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) for (int i = 0; i < segs; i++) { /* Calculate with doubles for higher precision, see: T87779. */ const float phi = (2.0 * M_PI) * ((double)i / (double)segs); - - vec[0] = dia1 * sinf(phi); - vec[1] = dia1 * cosf(phi); + vec[0] = rad1 * sinf(phi); + vec[1] = rad1 * cosf(phi); vec[2] = -depth_half; mul_m4_v3(mat, vec); v1 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - vec[0] = dia2 * sinf(phi); - vec[1] = dia2 * cosf(phi); + vec[0] = rad2 * sinf(phi); + vec[1] = rad2 * cosf(phi); vec[2] = depth_half; mul_m4_v3(mat, vec); v2 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); @@ -1497,11 +1496,11 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) } if (calc_uvs) { - BM_mesh_calc_uvs_cone(bm, mat, dia2, dia1, segs, cap_ends, FACE_MARK, cd_loop_uv_offset); + BM_mesh_calc_uvs_cone(bm, mat, rad2, rad1, segs, cap_ends, FACE_MARK, cd_loop_uv_offset); } /* Collapse vertices at the first end. */ - if (dia1 == 0.0f) { + if (rad1 == 0.0f) { if (cap_ends) { BM_vert_kill(bm, cent1); } @@ -1513,7 +1512,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) } /* Collapse vertices at the second end. */ - if (dia2 == 0.0f) { + if (rad2 == 0.0f) { if (cap_ends) { BM_vert_kill(bm, cent2); } diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 8ddcf11602a..10e385e0187 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -317,6 +317,8 @@ set(SRC nodes/COM_FilterNode.h nodes/COM_InpaintNode.cc nodes/COM_InpaintNode.h + nodes/COM_PosterizeNode.cc + nodes/COM_PosterizeNode.h operations/COM_BlurBaseOperation.cc operations/COM_BlurBaseOperation.h @@ -346,6 +348,8 @@ set(SRC operations/COM_MovieClipAttributeOperation.h operations/COM_MovieDistortionOperation.cc operations/COM_MovieDistortionOperation.h + operations/COM_PosterizeOperation.cc + operations/COM_PosterizeOperation.h operations/COM_SMAAOperation.cc operations/COM_SMAAOperation.h operations/COM_VariableSizeBokehBlurOperation.cc @@ -647,6 +651,7 @@ if(WITH_GTESTS) tests/COM_BufferArea_test.cc tests/COM_BufferRange_test.cc tests/COM_BuffersIterator_test.cc + tests/COM_NodeOperation_test.cc ) set(TEST_INC ) diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index e270eeb3386..73c4343a230 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -95,11 +95,11 @@ constexpr DataType COM_num_channels_data_type(const int num_channels) } } -// configurable items - -// chunk size determination - -// chunk order +/* Configurable items. + * + * Chunk size determination. + * + * Chunk order. */ /** * \brief The order of chunks to be scheduled * \ingroup Execution diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 1983eb190e2..4b103c21c75 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -90,6 +90,7 @@ #include "COM_OutputFileNode.h" #include "COM_PixelateNode.h" #include "COM_PlaneTrackDeformNode.h" +#include "COM_PosterizeNode.h" #include "COM_RenderLayersNode.h" #include "COM_RotateNode.h" #include "COM_ScaleNode.h" @@ -424,6 +425,9 @@ Node *COM_convert_bnode(bNode *b_node) case CMP_NODE_ANTIALIASING: node = new AntiAliasingNode(b_node); break; + case CMP_NODE_POSTERIZE: + node = new PosterizeNode(b_node); + break; } return node; } diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index a0333cf96cf..007085ee528 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -425,7 +425,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma } const bool has_execution_groups = system->getContext().get_execution_model() == - eExecutionModel::Tiled; + eExecutionModel::Tiled && + system->m_groups.size() > 0; len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n"); diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc index a45c453d7ed..505a4066a25 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.cc +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc @@ -446,7 +446,7 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo MEM_freeN(memoryBuffers); } if (this->m_bTree) { - // status report is only performed for top level Execution Groups. + /* Status report is only performed for top level Execution Groups. */ float progress = this->m_chunks_finished; progress /= this->m_chunks_len; this->m_bTree->progress(this->m_bTree->prh, progress); @@ -494,7 +494,7 @@ void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int chunkNu MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect) { - // we assume that this method is only called from complex execution groups. + /* We assume that this method is only called from complex execution groups. */ NodeOperation *operation = this->getOutputOperation(); if (operation->get_flags().is_write_buffer_operation) { WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation; @@ -510,8 +510,9 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area if (this->m_flags.single_threaded) { return scheduleChunkWhenPossible(graph, 0, 0); } - // find all chunks inside the rect - // determine minxchunk, minychunk, maxxchunk, maxychunk where x and y are chunknumbers + /* Find all chunks inside the rect + * determine `minxchunk`, `minychunk`, `maxxchunk`, `maxychunk` + * where x and y are chunk-numbers. */ int indexx, indexy; int minx = max_ii(area->xmin - m_viewerBorder.xmin, 0); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index f3e15c2a495..f730d53acec 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -373,6 +373,12 @@ class MemoryBuffer { return this->m_buffer; } + float *release_ownership_buffer() + { + owns_data_ = false; + return this->m_buffer; + } + MemoryBuffer *inflate() const; inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 1b87cdf72fb..3bbd1b22d60 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -41,6 +41,53 @@ NodeOperation::NodeOperation() this->m_btree = nullptr; } +/** + * Generate a hash that identifies the operation result in the current execution. + * Requires `hash_output_params` to be implemented, otherwise `std::nullopt` is returned. + * If the operation parameters or its linked inputs change, the hash must be re-generated. + */ +std::optional<NodeOperationHash> NodeOperation::generate_hash() +{ + params_hash_ = get_default_hash_2(m_width, m_height); + + /* Hash subclasses params. */ + is_hash_output_params_implemented_ = true; + hash_output_params(); + if (!is_hash_output_params_implemented_) { + return std::nullopt; + } + + hash_param(getOutputSocket()->getDataType()); + NodeOperationHash hash; + hash.params_hash_ = params_hash_; + + hash.parents_hash_ = 0; + for (NodeOperationInput &socket : m_inputs) { + if (!socket.isConnected()) { + continue; + } + + NodeOperation &input = socket.getLink()->getOperation(); + const bool is_constant = input.get_flags().is_constant_operation; + combine_hashes(hash.parents_hash_, get_default_hash(is_constant)); + if (is_constant) { + const float *elem = ((ConstantOperation *)&input)->get_constant_elem(); + const int num_channels = COM_data_type_num_channels(socket.getDataType()); + for (const int i : IndexRange(num_channels)) { + combine_hashes(hash.parents_hash_, get_default_hash(elem[i])); + } + } + else { + combine_hashes(hash.parents_hash_, get_default_hash(input.get_id())); + } + } + + hash.type_hash_ = typeid(*this).hash_code(); + hash.operation_ = this; + + return hash; +} + NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index) { return &m_outputs[index]; diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index b402dc7f174..ef7cf319222 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -22,6 +22,8 @@ #include <sstream> #include <string> +#include "BLI_ghash.h" +#include "BLI_hash.hh" #include "BLI_math_color.h" #include "BLI_math_vector.h" #include "BLI_threads.h" @@ -269,6 +271,42 @@ struct NodeOperationFlags { } }; +/** Hash that identifies an operation output result in the current execution. */ +struct NodeOperationHash { + private: + NodeOperation *operation_; + size_t type_hash_; + size_t parents_hash_; + size_t params_hash_; + + friend class NodeOperation; + + public: + NodeOperation *get_operation() const + { + return operation_; + } + + bool operator==(const NodeOperationHash &other) const + { + return type_hash_ == other.type_hash_ && parents_hash_ == other.parents_hash_ && + params_hash_ == other.params_hash_; + } + + bool operator!=(const NodeOperationHash &other) const + { + return !(*this == other); + } + + bool operator<(const NodeOperationHash &other) const + { + return type_hash_ < other.type_hash_ || + (type_hash_ == other.type_hash_ && parents_hash_ < other.parents_hash_) || + (type_hash_ == other.type_hash_ && parents_hash_ == other.parents_hash_ && + params_hash_ < other.params_hash_); + } +}; + /** * \brief NodeOperation contains calculation logic * @@ -282,6 +320,9 @@ class NodeOperation { Vector<NodeOperationInput> m_inputs; Vector<NodeOperationOutput> m_outputs; + size_t params_hash_; + bool is_hash_output_params_implemented_; + /** * \brief the index of the input socket that will be used to determine the resolution */ @@ -363,6 +404,8 @@ class NodeOperation { return flags; } + std::optional<NodeOperationHash> generate_hash(); + unsigned int getNumberOfInputSockets() const { return m_inputs.size(); @@ -624,6 +667,33 @@ class NodeOperation { protected: NodeOperation(); + /* Overridden by subclasses to allow merging equal operations on compiling. Implementations must + * hash any subclass parameter that affects the output result using `hash_params` methods. */ + virtual void hash_output_params() + { + is_hash_output_params_implemented_ = false; + } + + static void combine_hashes(size_t &combined, size_t other) + { + combined = BLI_ghashutil_combine_hash(combined, other); + } + + template<typename T> void hash_param(T param) + { + combine_hashes(params_hash_, get_default_hash(param)); + } + + template<typename T1, typename T2> void hash_params(T1 param1, T2 param2) + { + combine_hashes(params_hash_, get_default_hash_2(param1, param2)); + } + + template<typename T1, typename T2, typename T3> void hash_params(T1 param1, T2 param2, T3 param3) + { + combine_hashes(params_hash_, get_default_hash_3(param1, param2, param3)); + } + void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center); void addOutputSocket(DataType datatype); diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 10a91bbcd3e..b2cd76be2c3 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -101,16 +101,16 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) add_datatype_conversions(); if (m_context->get_execution_model() == eExecutionModel::FullFrame) { - /* Copy operations to system. Needed for graphviz. */ - system->set_operations(m_operations, {}); - - DebugInfo::graphviz(system, "compositor_prior_folding"); + save_graphviz("compositor_prior_folding"); ConstantFolder folder(*this); folder.fold_operations(); } determineResolutions(); + save_graphviz("compositor_prior_merging"); + merge_equal_operations(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { /* surround complex ops with read/write buffer */ add_complex_operation_buffers(); @@ -149,22 +149,28 @@ void NodeOperationBuilder::replace_operation_with_constant(NodeOperation *operat ConstantOperation *constant_operation) { BLI_assert(constant_operation->getNumberOfInputSockets() == 0); + unlink_inputs_and_relink_outputs(operation, constant_operation); + addOperation(constant_operation); +} + +void NodeOperationBuilder::unlink_inputs_and_relink_outputs(NodeOperation *unlinked_op, + NodeOperation *linked_op) +{ int i = 0; while (i < m_links.size()) { Link &link = m_links[i]; - if (&link.to()->getOperation() == operation) { + if (&link.to()->getOperation() == unlinked_op) { link.to()->setLink(nullptr); m_links.remove(i); continue; } - if (&link.from()->getOperation() == operation) { - link.to()->setLink(constant_operation->getOutputSocket()); - m_links[i] = Link(constant_operation->getOutputSocket(), link.to()); + if (&link.from()->getOperation() == unlinked_op) { + link.to()->setLink(linked_op->getOutputSocket()); + m_links[i] = Link(linked_op->getOutputSocket(), link.to()); } i++; } - addOperation(constant_operation); } void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket, @@ -456,6 +462,50 @@ void NodeOperationBuilder::determineResolutions() } } +static Vector<NodeOperationHash> generate_hashes(Span<NodeOperation *> operations) +{ + Vector<NodeOperationHash> hashes; + for (NodeOperation *op : operations) { + std::optional<NodeOperationHash> hash = op->generate_hash(); + if (hash) { + hashes.append(std::move(*hash)); + } + } + return hashes; +} + +/** Merge operations with same type, inputs and parameters that produce the same result. */ +void NodeOperationBuilder::merge_equal_operations() +{ + bool check_for_next_merge = true; + while (check_for_next_merge) { + /* Re-generate hashes with any change. */ + Vector<NodeOperationHash> hashes = generate_hashes(m_operations); + + /* Make hashes be consecutive when they are equal. */ + std::sort(hashes.begin(), hashes.end()); + + bool any_merged = false; + const NodeOperationHash *prev_hash = nullptr; + for (const NodeOperationHash &hash : hashes) { + if (prev_hash && *prev_hash == hash) { + merge_equal_operations(prev_hash->get_operation(), hash.get_operation()); + any_merged = true; + } + prev_hash = &hash; + } + + check_for_next_merge = any_merged; + } +} + +void NodeOperationBuilder::merge_equal_operations(NodeOperation *from, NodeOperation *into) +{ + unlink_inputs_and_relink_outputs(from, into); + m_operations.remove_first_occurrence_and_reorder(from); + delete from; +} + Vector<NodeOperationInput *> NodeOperationBuilder::cache_output_links( NodeOperationOutput *output) const { @@ -728,6 +778,14 @@ void NodeOperationBuilder::group_operations() } } +void NodeOperationBuilder::save_graphviz(StringRefNull name) +{ + if (COM_EXPORT_GRAPHVIZ) { + exec_system_->set_operations(m_operations, m_groups); + DebugInfo::graphviz(exec_system_, name); + } +} + /** Create a graphviz representation of the NodeOperationBuilder. */ std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder) { diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h index 1f76765c846..aca4d043d41 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h @@ -169,7 +169,10 @@ class NodeOperationBuilder { private: PreviewOperation *make_preview_operation() const; - + void unlink_inputs_and_relink_outputs(NodeOperation *unlinked_op, NodeOperation *linked_op); + void merge_equal_operations(); + void merge_equal_operations(NodeOperation *from, NodeOperation *into); + void save_graphviz(StringRefNull name = ""); #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompilerImpl") #endif diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index 8e49bf34b51..a08f9dd284c 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -298,7 +298,7 @@ static void opencl_deinitialize() g_work_scheduler.opencl.initialized = false; } -/* \} */ +/** \} */ /* -------------------------------------------------------------------- */ /** \name Single threaded Scheduling @@ -310,7 +310,7 @@ static void threading_model_single_thread_execute(WorkPackage *package) device.execute(package); } -/* \} */ +/** \} */ /* -------------------------------------------------------------------- */ /** \name Queue Scheduling @@ -388,7 +388,7 @@ static void threading_model_queue_deinitialize() } } -/* \} */ +/** \} */ /* -------------------------------------------------------------------- */ /** \name Task Scheduling @@ -426,7 +426,7 @@ static void threading_model_task_stop() BLI_thread_local_delete(g_thread_device); } -/* \} */ +/** \} */ /* -------------------------------------------------------------------- */ /** \name Public API @@ -587,6 +587,6 @@ int WorkScheduler::current_thread_id() return device->thread_id(); } -/* \} */ +/** \} */ } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index 5835f051ce3..c04d98d6a2b 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -33,7 +33,8 @@ namespace blender::compositor { -/** \name Cryptomatte base +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Base * \{ */ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, @@ -73,10 +74,12 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, converter.mapOutputSocket(output_pick_socket, extract_pick_operation->getOutputSocket(0)); } -/* \} */ +/** \} */ +/* -------------------------------------------------------------------- */ /** \name Cryptomatte V2 * \{ */ + static std::string prefix_from_node(const CompositorContext &context, const bNode &node) { char prefix[MAX_NAME]; @@ -247,9 +250,10 @@ CryptomatteOperation *CryptomatteNode::create_cryptomatte_operation( return operation; } -/* \} */ +/** \} */ -/** \name Cryptomatte legacy +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Legacy * \{ */ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation( @@ -273,6 +277,6 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation( return operation; } -/* \} */ +/** \} */ } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc index 0537074552a..cd0b5306be1 100644 --- a/source/blender/compositor/nodes/COM_GlareNode.cc +++ b/source/blender/compositor/nodes/COM_GlareNode.cc @@ -46,13 +46,13 @@ void GlareNode::convertToOperations(NodeConverter &converter, case 3: glareoperation = new GlareGhostOperation(); break; - case 2: // streaks + case 2: /* Streaks. */ glareoperation = new GlareStreaksOperation(); break; - case 1: // fog glow + case 1: /* Fog glow. */ glareoperation = new GlareFogGlowOperation(); break; - case 0: // simple star + case 0: /* Simple star. */ glareoperation = new GlareSimpleStarOperation(); break; } diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc index cbe4f165a45..43574d02d80 100644 --- a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc @@ -38,7 +38,7 @@ void KeyingScreenNode::convertToOperations(NodeConverter &converter, NodeOutput *outputScreen = this->getOutputSocket(0); - // always connect the output image + /* Always connect the output image. */ KeyingScreenOperation *operation = new KeyingScreenOperation(); operation->setMovieClip(clip); operation->setTrackingObject(keyingscreen_data->tracking_object); diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.cc b/source/blender/compositor/nodes/COM_PosterizeNode.cc new file mode 100644 index 00000000000..9f5a69961a4 --- /dev/null +++ b/source/blender/compositor/nodes/COM_PosterizeNode.cc @@ -0,0 +1,41 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +#include "COM_PosterizeNode.h" +#include "COM_ExecutionSystem.h" +#include "COM_PosterizeOperation.h" + +namespace blender::compositor { + +PosterizeNode::PosterizeNode(bNode *editorNode) : Node(editorNode) +{ + /* pass */ +} + +void PosterizeNode::convertToOperations(NodeConverter &converter, + const CompositorContext & /*context*/) const +{ + PosterizeOperation *operation = new PosterizeOperation(); + converter.addOperation(operation); + + converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); + converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.h b/source/blender/compositor/nodes/COM_PosterizeNode.h new file mode 100644 index 00000000000..bb9bef2bdd0 --- /dev/null +++ b/source/blender/compositor/nodes/COM_PosterizeNode.h @@ -0,0 +1,36 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +/** + * \brief PosterizeNode + * \ingroup Node + */ +class PosterizeNode : public Node { + public: + PosterizeNode(bNode *editorNode); + void convertToOperations(NodeConverter &converter, + const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc index dc454b95080..5a03972c89d 100644 --- a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc +++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc @@ -34,7 +34,7 @@ void ViewLevelsNode::convertToOperations(NodeConverter &converter, { NodeInput *input = this->getInputSocket(0); if (input->isLinked()) { - // add preview to inputSocket; + /* Add preview to input-socket. */ /* calculate mean operation */ { diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc index a76049ff249..ddf66740578 100644 --- a/source/blender/compositor/nodes/COM_ZCombineNode.cc +++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc @@ -58,8 +58,9 @@ void ZCombineNode::convertToOperations(NodeConverter &converter, } else { /* XXX custom1 is "use_alpha", what on earth is this supposed to do here?!? */ - // not full anti alias, use masking for Z combine. be aware it uses anti aliasing. - // step 1 create mask + /* not full anti alias, use masking for Z combine. be aware it uses anti aliasing. */ + + /* Step 1 create mask. */ NodeOperation *maskoperation; if (this->getbNode()->custom1) { maskoperation = new MathGreaterThanOperation(); @@ -76,13 +77,13 @@ void ZCombineNode::convertToOperations(NodeConverter &converter, converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1)); } - // step 2 anti alias mask bit of an expensive operation, but does the trick + /* Step 2 anti alias mask bit of an expensive operation, but does the trick. */ AntiAliasOperation *antialiasoperation = new AntiAliasOperation(); converter.addOperation(antialiasoperation); converter.addLink(maskoperation->getOutputSocket(), antialiasoperation->getInputSocket(0)); - // use mask to blend between the input colors. + /* use mask to blend between the input colors. */ ZCombineMaskOperation *zcombineoperation = this->getbNode()->custom1 ? new ZCombineMaskAlphaOperation() : new ZCombineMaskOperation(); diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 0c1bb688d4e..44680c3acd1 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -43,8 +43,8 @@ void BilateralBlurOperation::initExecution() void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *data) { - // read the determinator color at x, y, this will be used as the reference color for the - // determinator + /* Read the determinator color at x, y, + * this will be used as the reference color for the determinator. */ float determinatorReferenceColor[4]; float determinator[4]; float tempColor[4]; @@ -67,14 +67,14 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d */ for (int yi = miny; yi < maxy; yi += QualityStepHelper::getStep()) { for (int xi = minx; xi < maxx; xi += QualityStepHelper::getStep()) { - // read determinator + /* Read determinator. */ this->m_inputDeterminatorProgram->read(determinator, xi, yi, data); deltaColor = (fabsf(determinatorReferenceColor[0] - determinator[0]) + fabsf(determinatorReferenceColor[1] - determinator[1]) + - fabsf(determinatorReferenceColor[2] - - determinator[2])); // do not take the alpha channel into account + /* Do not take the alpha channel into account. */ + fabsf(determinatorReferenceColor[2] - determinator[2])); if (deltaColor < sigmacolor) { - // add this to the blur + /* Add this to the blur. */ this->m_inputColorProgram->read(tempColor, xi, yi, data); add_v4_v4(blurColor, tempColor); blurDivider += 1.0f; diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc index b50145b106d..d3557e541c0 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc @@ -132,7 +132,7 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], g = color_correct_powf_safe(g * gain + lift, invgamma, g); b = color_correct_powf_safe(b * gain + lift, invgamma, b); - // mix with mask + /* Mix with mask. */ r = mvalue * inputImageColor[0] + value * r; g = mvalue * inputImageColor[1] + value * g; b = mvalue * inputImageColor[2] + value * b; diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc index 57027c11949..405ba03abf3 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc @@ -58,7 +58,7 @@ void ConvertDepthToRadiusOperation::initExecution() this->m_inputOperation = this->getInputSocketReader(0); float focalDistance = determineFocalDistance(); if (focalDistance == 0.0f) { - focalDistance = 1e10f; /* if the dof is 0.0 then set it to be far away */ + focalDistance = 1e10f; /* If the DOF is 0.0 then set it to be far away. */ } this->m_inverseFocalDistance = 1.0f / focalDistance; this->m_aspect = (this->getWidth() > this->getHeight()) ? @@ -66,9 +66,9 @@ void ConvertDepthToRadiusOperation::initExecution() (this->getWidth() / (float)this->getHeight()); this->m_aperture = 0.5f * (this->m_cam_lens / (this->m_aspect * cam_sensor)) / this->m_fStop; const float minsz = MIN2(getWidth(), getHeight()); - this->m_dof_sp = minsz / - ((cam_sensor / 2.0f) / - this->m_cam_lens); // <- == aspect * MIN2(img->x, img->y) / tan(0.5f * fov); + this->m_dof_sp = + minsz / ((cam_sensor / 2.0f) / + this->m_cam_lens); /* <- == `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)` */ if (this->m_blurPostOperation) { m_blurPostOperation->setSigma(MIN2(m_aperture * 128.0f, this->m_maxRadius)); @@ -91,7 +91,7 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], /* bug T6656 part 2b, do not re-scale. */ #if 0 bcrad = 0.5f * fabs(aperture * (dof_sp * (cam_invfdist - iZ) - 1.0f)); - // scale crad back to original maximum and blend + /* Scale crad back to original maximum and blend. */ crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad); #endif radius = 0.5f * fabsf(this->m_aperture * @@ -116,4 +116,31 @@ void ConvertDepthToRadiusOperation::deinitExecution() this->m_inputOperation = nullptr; } +void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float z = *it.in(0); + if (z == 0.0f) { + *it.out = 0.0f; + continue; + } + + const float inv_z = (1.0f / z); + + /* Bug T6656 part 2b, do not re-scale. */ +#if 0 + bcrad = 0.5f * fabs(aperture * (dof_sp * (cam_invfdist - iZ) - 1.0f)); + /* Scale crad back to original maximum and blend: + * `crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad);` */ +#endif + const float radius = 0.5f * + fabsf(m_aperture * (m_dof_sp * (m_inverseFocalDistance - inv_z) - 1.0f)); + /* Bug T6615, limit minimum radius to 1 pixel, + * not really a solution, but somewhat mitigates the problem. */ + *it.out = CLAMPIS(radius, 0.0f, m_maxRadius); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h index 1f4e856b128..3d163843d06 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h @@ -19,7 +19,7 @@ #pragma once #include "COM_FastGaussianBlurOperation.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_object_types.h" namespace blender::compositor { @@ -28,7 +28,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ConvertDepthToRadiusOperation : public NodeOperation { +class ConvertDepthToRadiusOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -83,6 +83,10 @@ class ConvertDepthToRadiusOperation : public NodeOperation { { this->m_blurPostOperation = operation; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index d377903efea..9a3733dda5b 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -40,6 +40,10 @@ void ConvertBaseOperation::deinitExecution() this->m_inputOperation = nullptr; } +void ConvertBaseOperation::hash_output_params() +{ +} + void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span<MemoryBuffer *> inputs) @@ -269,6 +273,12 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertRGBToYCCOperation::hash_output_params() +{ + ConvertBaseOperation::hash_output_params(); + hash_param(m_mode); +} + void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator<float> &it) { for (; !it.is_end(); ++it) { @@ -327,6 +337,12 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertYCCToRGBOperation::hash_output_params() +{ + ConvertBaseOperation::hash_output_params(); + hash_param(m_mode); +} + void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it) { for (; !it.is_end(); ++it) { diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index 0334959ae7e..72864b3c5e2 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -37,6 +37,7 @@ class ConvertBaseOperation : public MultiThreadedOperation { Span<MemoryBuffer *> inputs) final; protected: + virtual void hash_output_params() override; virtual void update_memory_buffer_partial(BuffersIterator<float> &it) = 0; }; @@ -124,6 +125,7 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation { void setMode(int mode); protected: + void hash_output_params() override; void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; @@ -141,6 +143,7 @@ class ConvertYCCToRGBOperation : public ConvertBaseOperation { void setMode(int mode); protected: + void hash_output_params() override; void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc index 5ead300a368..9127a871b04 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc @@ -95,4 +95,81 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, output[3] = MAX2(output[3], 0.0f); } +void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; + const int last_x = getWidth() - 1; + const int last_y = getHeight() - 1; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const int left_offset = (it.x == 0) ? 0 : -image->elem_stride; + const int right_offset = (it.x == last_x) ? 0 : image->elem_stride; + const int down_offset = (it.y == 0) ? 0 : -image->row_stride; + const int up_offset = (it.y == last_y) ? 0 : image->row_stride; + + const float *center_color = it.in(IMAGE_INPUT_INDEX); + float res1[4] = {0}; + float res2[4] = {0}; + + const float *color = center_color + down_offset + left_offset; + madd_v3_v3fl(res1, color, m_filter[0]); + copy_v3_v3(res2, res1); + + color = center_color + down_offset; + madd_v3_v3fl(res1, color, m_filter[1]); + madd_v3_v3fl(res2, color, m_filter[3]); + + color = center_color + down_offset + right_offset; + madd_v3_v3fl(res1, color, m_filter[2]); + madd_v3_v3fl(res2, color, m_filter[6]); + + color = center_color + left_offset; + madd_v3_v3fl(res1, color, m_filter[3]); + madd_v3_v3fl(res2, color, m_filter[1]); + + { + float rgb_filtered[3]; + mul_v3_v3fl(rgb_filtered, center_color, m_filter[4]); + add_v3_v3(res1, rgb_filtered); + add_v3_v3(res2, rgb_filtered); + } + + color = center_color + right_offset; + madd_v3_v3fl(res1, color, m_filter[5]); + madd_v3_v3fl(res2, color, m_filter[7]); + + color = center_color + up_offset + left_offset; + madd_v3_v3fl(res1, color, m_filter[6]); + madd_v3_v3fl(res2, color, m_filter[2]); + + color = center_color + up_offset; + madd_v3_v3fl(res1, color, m_filter[7]); + madd_v3_v3fl(res2, color, m_filter[5]); + + { + color = center_color + up_offset + right_offset; + float rgb_filtered[3]; + mul_v3_v3fl(rgb_filtered, color, m_filter[8]); + add_v3_v3(res1, rgb_filtered); + add_v3_v3(res2, rgb_filtered); + } + + it.out[0] = sqrt(res1[0] * res1[0] + res2[0] * res2[0]); + it.out[1] = sqrt(res1[1] * res1[1] + res2[1] * res2[1]); + it.out[2] = sqrt(res1[2] * res1[2] + res2[2] * res2[2]); + + const float factor = *it.in(FACTOR_INPUT_INDEX); + const float m_factor = 1.0f - factor; + it.out[0] = it.out[0] * factor + center_color[0] * m_factor; + it.out[1] = it.out[1] * factor + center_color[1] * m_factor; + it.out[2] = it.out[2] * factor + center_color[2] * m_factor; + + it.out[3] = center_color[3]; + + /* Make sure we don't return negative color. */ + CLAMP4_MIN(it.out, 0.0f); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h index 319b424bd4a..bd38e27165a 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h @@ -25,6 +25,10 @@ namespace blender::compositor { class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation { public: void executePixel(float output[4], int x, int y, void *data) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index 72cbbf4283a..11a077229fd 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -127,4 +127,62 @@ bool ConvolutionFilterOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void ConvolutionFilterOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case IMAGE_INPUT_INDEX: { + const int add_x = (m_filterWidth - 1) / 2 + 1; + const int add_y = (m_filterHeight - 1) / 2 + 1; + r_input_area.xmin = output_area.xmin - add_x; + r_input_area.xmax = output_area.xmax + add_x; + r_input_area.ymin = output_area.ymin - add_y; + r_input_area.ymax = output_area.ymax + add_y; + break; + } + case FACTOR_INPUT_INDEX: { + r_input_area = output_area; + break; + } + } +} + +void ConvolutionFilterOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; + const int last_x = getWidth() - 1; + const int last_y = getHeight() - 1; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const int left_offset = (it.x == 0) ? 0 : -image->elem_stride; + const int right_offset = (it.x == last_x) ? 0 : image->elem_stride; + const int down_offset = (it.y == 0) ? 0 : -image->row_stride; + const int up_offset = (it.y == last_y) ? 0 : image->row_stride; + + const float *center_color = it.in(IMAGE_INPUT_INDEX); + zero_v4(it.out); + madd_v4_v4fl(it.out, center_color + down_offset + left_offset, m_filter[0]); + madd_v4_v4fl(it.out, center_color + down_offset, m_filter[1]); + madd_v4_v4fl(it.out, center_color + down_offset + right_offset, m_filter[2]); + madd_v4_v4fl(it.out, center_color + left_offset, m_filter[3]); + madd_v4_v4fl(it.out, center_color, m_filter[4]); + madd_v4_v4fl(it.out, center_color + right_offset, m_filter[5]); + madd_v4_v4fl(it.out, center_color + up_offset + left_offset, m_filter[6]); + madd_v4_v4fl(it.out, center_color + up_offset, m_filter[7]); + madd_v4_v4fl(it.out, center_color + up_offset + right_offset, m_filter[8]); + + const float factor = *it.in(FACTOR_INPUT_INDEX); + const float m_factor = 1.0f - factor; + it.out[0] = it.out[0] * factor + center_color[0] * m_factor; + it.out[1] = it.out[1] * factor + center_color[1] * m_factor; + it.out[2] = it.out[2] * factor + center_color[2] * m_factor; + it.out[3] = it.out[3] * factor + center_color[3] * m_factor; + + /* Make sure we don't return negative color. */ + CLAMP4_MIN(it.out, 0.0f); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h index 16dee502929..7e12c7faa5c 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h @@ -18,11 +18,15 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class ConvolutionFilterOperation : public NodeOperation { +class ConvolutionFilterOperation : public MultiThreadedOperation { + protected: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int FACTOR_INPUT_INDEX = 1; + private: int m_filterWidth; int m_filterHeight; @@ -43,6 +47,11 @@ class ConvolutionFilterOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index ec11ad4d69a..e7f2d5a740a 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -35,6 +35,8 @@ DenoiseOperation::DenoiseOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->m_settings = nullptr; + flags.is_fullframe_operation = true; + output_rendered_ = false; } void DenoiseOperation::initExecution() { @@ -63,8 +65,7 @@ MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) rect.xmax = getWidth(); rect.ymax = getHeight(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); - float *data = result->getBuffer(); - this->generateDenoise(data, tileColor, tileNormal, tileAlbedo, this->m_settings); + this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, this->m_settings); return result; } @@ -84,23 +85,33 @@ bool DenoiseOperation::determineDependingAreaOfInterest(rcti * /*input*/, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } -void DenoiseOperation::generateDenoise(float *data, - MemoryBuffer *inputTileColor, - MemoryBuffer *inputTileNormal, - MemoryBuffer *inputTileAlbedo, +void DenoiseOperation::generateDenoise(MemoryBuffer *output, + MemoryBuffer *input_color, + MemoryBuffer *input_normal, + MemoryBuffer *input_albedo, NodeDenoise *settings) { - float *inputBufferColor = inputTileColor->getBuffer(); - BLI_assert(inputBufferColor); - if (!inputBufferColor) { + BLI_assert(input_color->getBuffer()); + if (!input_color->getBuffer()) { return; } + #ifdef WITH_OPENIMAGEDENOISE /* Always supported through Accelerate framework BNNS on macOS. */ # ifndef __APPLE__ if (BLI_cpu_support_sse41()) # endif { + /* OpenImageDenoise needs full buffers. */ + MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() : + input_color; + MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ? + input_normal->inflate() : + input_normal; + MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ? + input_albedo->inflate() : + input_albedo; + /* Since it's memory intensive, it's better to run only one instance of OIDN at a time. * OpenImageDenoise is multithreaded internally and should use all available cores nonetheless. */ @@ -111,35 +122,35 @@ void DenoiseOperation::generateDenoise(float *data, oidn::FilterRef filter = device.newFilter("RT"); filter.setImage("color", - inputBufferColor, + buf_color->getBuffer(), oidn::Format::Float3, - inputTileColor->getWidth(), - inputTileColor->getHeight(), + buf_color->getWidth(), + buf_color->getHeight(), 0, sizeof(float[4])); - if (inputTileNormal && inputTileNormal->getBuffer()) { + if (buf_normal && buf_normal->getBuffer()) { filter.setImage("normal", - inputTileNormal->getBuffer(), + buf_normal->getBuffer(), oidn::Format::Float3, - inputTileNormal->getWidth(), - inputTileNormal->getHeight(), + buf_normal->getWidth(), + buf_normal->getHeight(), 0, sizeof(float[3])); } - if (inputTileAlbedo && inputTileAlbedo->getBuffer()) { + if (buf_albedo && buf_albedo->getBuffer()) { filter.setImage("albedo", - inputTileAlbedo->getBuffer(), + buf_albedo->getBuffer(), oidn::Format::Float3, - inputTileAlbedo->getWidth(), - inputTileAlbedo->getHeight(), + buf_albedo->getWidth(), + buf_albedo->getHeight(), 0, sizeof(float[4])); } filter.setImage("output", - data, + output->getBuffer(), oidn::Format::Float3, - inputTileColor->getWidth(), - inputTileColor->getHeight(), + buf_color->getWidth(), + buf_color->getHeight(), 0, sizeof(float[4])); @@ -153,19 +164,46 @@ void DenoiseOperation::generateDenoise(float *data, filter.execute(); BLI_mutex_unlock(&oidn_lock); - /* copy the alpha channel, OpenImageDenoise currently only supports RGB */ - size_t numPixels = inputTileColor->getWidth() * inputTileColor->getHeight(); - for (size_t i = 0; i < numPixels; i++) { - data[i * 4 + 3] = inputBufferColor[i * 4 + 3]; + /* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */ + output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3); + + /* Delete inflated buffers. */ + if (input_color->is_a_single_elem()) { + delete buf_color; + } + if (input_normal && input_normal->is_a_single_elem()) { + delete buf_normal; } + if (input_albedo && input_albedo->is_a_single_elem()) { + delete buf_albedo; + } + return; } #endif /* If built without OIDN or running on an unsupported CPU, just pass through. */ - UNUSED_VARS(inputTileAlbedo, inputTileNormal, settings); - ::memcpy(data, - inputBufferColor, - sizeof(float[4]) * inputTileColor->getWidth() * inputTileColor->getHeight()); + UNUSED_VARS(input_albedo, input_normal, settings); + output->copy_from(input_color, input_color->get_rect()); +} + +void DenoiseOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void DenoiseOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + if (!output_rendered_) { + this->generateDenoise(output, inputs[0], inputs[1], inputs[2], m_settings); + output_rendered_ = true; + } } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h index a9298c17e92..48209c3eacf 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.h +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -37,6 +37,8 @@ class DenoiseOperation : public SingleThreadedOperation { */ NodeDenoise *m_settings; + bool output_rendered_; + public: DenoiseOperation(); /** @@ -57,11 +59,16 @@ class DenoiseOperation : public SingleThreadedOperation { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + protected: - void generateDenoise(float *data, - MemoryBuffer *inputTileColor, - MemoryBuffer *inputTileNormal, - MemoryBuffer *inputTileAlbedo, + void generateDenoise(MemoryBuffer *output, + MemoryBuffer *input_color, + MemoryBuffer *input_normal, + MemoryBuffer *input_albedo, NodeDenoise *settings); MemoryBuffer *createMemoryBuffer(rcti *rect) override; diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index fc8778c7d2e..19bd7b2af6f 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -127,6 +127,11 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da else { copy_v4_v4(output, color_org); } + +#undef TOT_DIV_ONE +#undef TOT_DIV_CNR +#undef WTOT +#undef COLOR_ADD } bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input, @@ -144,4 +149,106 @@ bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void DespeckleOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case IMAGE_INPUT_INDEX: { + const int add_x = 2; //(this->m_filterWidth - 1) / 2 + 1; + const int add_y = 2; //(this->m_filterHeight - 1) / 2 + 1; + r_input_area.xmin = output_area.xmin - add_x; + r_input_area.xmax = output_area.xmax + add_x; + r_input_area.ymin = output_area.ymin - add_y; + r_input_area.ymax = output_area.ymax + add_y; + break; + } + case FACTOR_INPUT_INDEX: { + r_input_area = output_area; + break; + } + } +} + +void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; + const int last_x = getWidth() - 1; + const int last_y = getHeight() - 1; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const int x1 = MAX2(it.x - 1, 0); + const int x2 = it.x; + const int x3 = MIN2(it.x + 1, last_x); + const int y1 = MAX2(it.y - 1, 0); + const int y2 = it.y; + const int y3 = MIN2(it.y + 1, last_y); + + float w = 0.0f; + const float *color_org = it.in(IMAGE_INPUT_INDEX); + float color_mid[4]; + float color_mid_ok[4]; + const float *in1 = nullptr; + +#define TOT_DIV_ONE 1.0f +#define TOT_DIV_CNR (float)M_SQRT1_2 + +#define WTOT (TOT_DIV_ONE * 4 + TOT_DIV_CNR * 4) + +#define COLOR_ADD(fac) \ + { \ + madd_v4_v4fl(color_mid, in1, fac); \ + if (color_diff(in1, color_org, m_threshold)) { \ + w += fac; \ + madd_v4_v4fl(color_mid_ok, in1, fac); \ + } \ + } + + zero_v4(color_mid); + zero_v4(color_mid_ok); + + in1 = image->get_elem(x1, y1); + COLOR_ADD(TOT_DIV_CNR) + in1 = image->get_elem(x2, y1); + COLOR_ADD(TOT_DIV_ONE) + in1 = image->get_elem(x3, y1); + COLOR_ADD(TOT_DIV_CNR) + in1 = image->get_elem(x1, y2); + COLOR_ADD(TOT_DIV_ONE) + +#if 0 + const float* in2 = image->get_elem(x2, y2); + madd_v4_v4fl(color_mid, in2, this->m_filter[4]); +#endif + + in1 = image->get_elem(x3, y2); + COLOR_ADD(TOT_DIV_ONE) + in1 = image->get_elem(x1, y3); + COLOR_ADD(TOT_DIV_CNR) + in1 = image->get_elem(x2, y3); + COLOR_ADD(TOT_DIV_ONE) + in1 = image->get_elem(x3, y3); + COLOR_ADD(TOT_DIV_CNR) + + mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); + // mul_v4_fl(color_mid, 1.0f / w); + + if ((w != 0.0f) && ((w / WTOT) > (m_threshold_neighbor)) && + color_diff(color_mid, color_org, m_threshold)) { + const float factor = *it.in(FACTOR_INPUT_INDEX); + mul_v4_fl(color_mid_ok, 1.0f / w); + interp_v4_v4v4(it.out, color_org, color_mid_ok, factor); + } + else { + copy_v4_v4(it.out, color_org); + } + +#undef TOT_DIV_ONE +#undef TOT_DIV_CNR +#undef WTOT +#undef COLOR_ADD + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.h b/source/blender/compositor/operations/COM_DespeckleOperation.h index e8d3461d2ec..70d6c2227f4 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.h +++ b/source/blender/compositor/operations/COM_DespeckleOperation.h @@ -18,12 +18,15 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DespeckleOperation : public NodeOperation { +class DespeckleOperation : public MultiThreadedOperation { private: + constexpr static int IMAGE_INPUT_INDEX = 0; + constexpr static int FACTOR_INPUT_INDEX = 1; + float m_threshold; float m_threshold_neighbor; @@ -52,6 +55,11 @@ class DespeckleOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index c459d09f02c..28b40021cd9 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -35,9 +35,9 @@ DilateErodeThresholdOperation::DilateErodeThresholdOperation() this->m__switch = 0.5f; this->m_distance = 0.0f; } -void DilateErodeThresholdOperation::initExecution() + +void DilateErodeThresholdOperation::init_data() { - this->m_inputProgram = this->getInputSocketReader(0); if (this->m_distance < 0.0f) { this->m_scope = -this->m_distance + this->m_inset; } @@ -54,6 +54,11 @@ void DilateErodeThresholdOperation::initExecution() } } +void DilateErodeThresholdOperation::initExecution() +{ + this->m_inputProgram = this->getInputSocketReader(0); +} + void *DilateErodeThresholdOperation::initializeTileData(rcti * /*rect*/) { void *buffer = this->m_inputProgram->initializeTileData(nullptr); @@ -160,6 +165,112 @@ bool DilateErodeThresholdOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void DilateErodeThresholdOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_scope; + r_input_area.xmax = output_area.xmax + m_scope; + r_input_area.ymin = output_area.ymin - m_scope; + r_input_area.ymax = output_area.ymax + m_scope; +} + +struct DilateErodeThresholdOperation::PixelData { + int x; + int y; + int xmin; + int xmax; + int ymin; + int ymax; + const float *elem; + float distance; + int elem_stride; + int row_stride; + /** Switch. */ + float sw; +}; + +template<template<typename> typename TCompare> +static float get_min_distance(DilateErodeThresholdOperation::PixelData &p) +{ + /* TODO(manzanilla): bad performance, generate a table with relative offsets on operation + * initialization to loop from less to greater distance and break as soon as #compare is + * true. */ + const TCompare compare; + float min_dist = p.distance; + const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride + + ((intptr_t)p.xmin - p.x) * p.elem_stride; + for (int yi = p.ymin; yi < p.ymax; yi++) { + const float dy = yi - p.y; + const float dist_y = dy * dy; + const float *elem = row; + for (int xi = p.xmin; xi < p.xmax; xi++) { + if (compare(*elem, p.sw)) { + const float dx = xi - p.x; + const float dist = dx * dx + dist_y; + min_dist = MIN2(min_dist, dist); + } + elem += p.elem_stride; + } + row += p.row_stride; + } + return min_dist; +} + +void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[0]; + const rcti &input_rect = input->get_rect(); + const float rd = m_scope * m_scope; + const float inset = m_inset; + + PixelData p; + p.sw = m__switch; + p.distance = rd * 2; + p.elem_stride = input->elem_stride; + p.row_stride = input->row_stride; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + p.x = it.x; + p.y = it.y; + p.xmin = MAX2(p.x - m_scope, input_rect.xmin); + p.ymin = MAX2(p.y - m_scope, input_rect.ymin); + p.xmax = MIN2(p.x + m_scope, input_rect.xmax); + p.ymax = MIN2(p.y + m_scope, input_rect.ymax); + p.elem = it.in(0); + + float pixel_value; + if (*p.elem > p.sw) { + pixel_value = -sqrtf(get_min_distance<std::less>(p)); + } + else { + pixel_value = sqrtf(get_min_distance<std::greater>(p)); + } + + if (m_distance > 0.0f) { + const float delta = m_distance - pixel_value; + if (delta >= 0.0f) { + *it.out = delta >= inset ? 1.0f : delta / inset; + } + else { + *it.out = 0.0f; + } + } + else { + const float delta = -m_distance + pixel_value; + if (delta < 0.0f) { + *it.out = delta < -inset ? 1.0f : (-delta) / inset; + } + else { + *it.out = 0.0f; + } + } + } +} + /* Dilate Distance. */ DilateDistanceOperation::DilateDistanceOperation() { @@ -170,15 +281,20 @@ DilateDistanceOperation::DilateDistanceOperation() flags.complex = true; flags.open_cl = true; } -void DilateDistanceOperation::initExecution() + +void DilateDistanceOperation::init_data() { - this->m_inputProgram = this->getInputSocketReader(0); this->m_scope = this->m_distance; if (this->m_scope < 3) { this->m_scope = 3; } } +void DilateDistanceOperation::initExecution() +{ + this->m_inputProgram = this->getInputSocketReader(0); +} + void *DilateDistanceOperation::initializeTileData(rcti * /*rect*/) { void *buffer = this->m_inputProgram->initializeTileData(nullptr); @@ -258,6 +374,92 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this); } +void DilateDistanceOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_scope; + r_input_area.xmax = output_area.xmax + m_scope; + r_input_area.ymin = output_area.ymin - m_scope; + r_input_area.ymax = output_area.ymax + m_scope; +} + +struct DilateDistanceOperation::PixelData { + int x; + int y; + int xmin; + int xmax; + int ymin; + int ymax; + const float *elem; + float min_distance; + int scope; + int elem_stride; + int row_stride; + const rcti &input_rect; + + PixelData(MemoryBuffer *input, const int distance, const int scope) + : min_distance(distance * distance), + scope(scope), + elem_stride(input->elem_stride), + row_stride(input->row_stride), + input_rect(input->get_rect()) + { + } + + void update(BuffersIterator<float> &it) + { + x = it.x; + y = it.y; + xmin = MAX2(x - scope, input_rect.xmin); + ymin = MAX2(y - scope, input_rect.ymin); + xmax = MIN2(x + scope, input_rect.xmax); + ymax = MIN2(y + scope, input_rect.ymax); + elem = it.in(0); + } +}; + +template<template<typename> typename TCompare> +static float get_distance_value(DilateDistanceOperation::PixelData &p, const float start_value) +{ + /* TODO(manzanilla): bad performance, only loop elements within minimum distance removing + * coordinates and conditional if `dist <= min_dist`. May need to generate a table of offsets. */ + const TCompare compare; + const float min_dist = p.min_distance; + float value = start_value; + const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride + + ((intptr_t)p.xmin - p.x) * p.elem_stride; + for (int yi = p.ymin; yi < p.ymax; yi++) { + const float dy = yi - p.y; + const float dist_y = dy * dy; + const float *elem = row; + for (int xi = p.xmin; xi < p.xmax; xi++) { + const float dx = xi - p.x; + const float dist = dx * dx + dist_y; + if (dist <= min_dist) { + value = compare(*elem, value) ? *elem : value; + } + elem += p.elem_stride; + } + row += p.row_stride; + } + + return value; +} + +void DilateDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + PixelData p(inputs[0], m_distance, m_scope); + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + p.update(it); + *it.out = get_distance_value<std::greater>(p, 0.0f); + } +} + /* Erode Distance */ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() { @@ -318,6 +520,17 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this); } +void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + PixelData p(inputs[0], m_distance, m_scope); + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + p.update(it); + *it.out = get_distance_value<std::less>(p, 1.0f); + } +} + /* Dilate step */ DilateStepOperation::DilateStepOperation() { @@ -475,6 +688,126 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void DilateStepOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_iterations; + r_input_area.xmax = output_area.xmax + m_iterations; + r_input_area.ymin = output_area.ymin - m_iterations; + r_input_area.ymax = output_area.ymax + m_iterations; +} + +template<typename TCompareSelector> +static void step_update_memory_buffer(MemoryBuffer *output, + const MemoryBuffer *input, + const rcti &area, + const int num_iterations, + const float compare_min_value) +{ + TCompareSelector selector; + + const int width = output->getWidth(); + const int height = output->getHeight(); + + const int half_window = num_iterations; + const int window = half_window * 2 + 1; + + const int xmin = MAX2(0, area.xmin - half_window); + const int ymin = MAX2(0, area.ymin - half_window); + const int xmax = MIN2(width, area.xmax + half_window); + const int ymax = MIN2(height, area.ymax + half_window); + + const int bwidth = area.xmax - area.xmin; + const int bheight = area.ymax - area.ymin; + + /* NOTE: #result has area width, but new height. + * We have to calculate the additional rows in the first pass, + * to have valid data available for the second pass. */ + rcti result_area; + BLI_rcti_init(&result_area, area.xmin, area.xmax, ymin, ymax); + MemoryBuffer result(DataType::Value, result_area); + + /* #temp holds maxima for every step in the algorithm, #buf holds a + * single row or column of input values, padded with #limit values to + * simplify the logic. */ + float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp"); + float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window), + "dilate erode buf"); + + /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. */ + /* First pass, horizontal dilate/erode. */ + for (int y = ymin; y < ymax; y++) { + for (int x = 0; x < bwidth + 5 * half_window; x++) { + buf[x] = compare_min_value; + } + for (int x = xmin; x < xmax; x++) { + buf[x - area.xmin + window - 1] = input->get_value(x, y, 0); + } + + for (int i = 0; i < (bwidth + 3 * half_window) / window; i++) { + int start = (i + 1) * window - 1; + + temp[window - 1] = buf[start]; + for (int x = 1; x < window; x++) { + temp[window - 1 - x] = selector(temp[window - x], buf[start - x]); + temp[window - 1 + x] = selector(temp[window + x - 2], buf[start + x]); + } + + start = half_window + (i - 1) * window + 1; + for (int x = -MIN2(0, start); x < window - MAX2(0, start + window - bwidth); x++) { + result.get_value(start + x + area.xmin, y, 0) = selector(temp[x], temp[x + window - 1]); + } + } + } + + /* Second pass, vertical dilate/erode. */ + for (int x = 0; x < bwidth; x++) { + for (int y = 0; y < bheight + 5 * half_window; y++) { + buf[y] = compare_min_value; + } + for (int y = ymin; y < ymax; y++) { + buf[y - area.ymin + window - 1] = result.get_value(x + area.xmin, y, 0); + } + + for (int i = 0; i < (bheight + 3 * half_window) / window; i++) { + int start = (i + 1) * window - 1; + + temp[window - 1] = buf[start]; + for (int y = 1; y < window; y++) { + temp[window - 1 - y] = selector(temp[window - y], buf[start - y]); + temp[window - 1 + y] = selector(temp[window + y - 2], buf[start + y]); + } + + start = half_window + (i - 1) * window + 1; + for (int y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) { + result.get_value(x, y + start + area.ymin, 0) = selector(temp[y], temp[y + window - 1]); + } + } + } + + MEM_freeN(temp); + MEM_freeN(buf); + + output->copy_from(&result, area); +} + +struct Max2Selector { + float operator()(float f1, float f2) const + { + return MAX2(f1, f2); + } +}; + +void DilateStepOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + step_update_memory_buffer<Max2Selector>(output, inputs[0], area, m_iterations, -FLT_MAX); +} + /* Erode step */ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() { @@ -571,4 +904,18 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) return result; } +struct Min2Selector { + float operator()(float f1, float f2) const + { + return MIN2(f1, f2); + } +}; + +void ErodeStepOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + step_update_memory_buffer<Min2Selector>(output, inputs[0], area, m_iterations, FLT_MAX); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.h b/source/blender/compositor/operations/COM_DilateErodeOperation.h index a489e293e8e..9c32a5ac1fd 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.h +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.h @@ -18,11 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DilateErodeThresholdOperation : public NodeOperation { +class DilateErodeThresholdOperation : public MultiThreadedOperation { + public: + struct PixelData; + private: /** * Cached reference to the inputProgram @@ -47,6 +50,7 @@ class DilateErodeThresholdOperation : public NodeOperation { */ void executePixel(float output[4], int x, int y, void *data) override; + void init_data() override; /** * Initialize the execution */ @@ -74,10 +78,17 @@ class DilateErodeThresholdOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; -class DilateDistanceOperation : public NodeOperation { - private: +class DilateDistanceOperation : public MultiThreadedOperation { + public: + struct PixelData; + protected: /** * Cached reference to the inputProgram @@ -94,6 +105,7 @@ class DilateDistanceOperation : public NodeOperation { */ void executePixel(float output[4], int x, int y, void *data) override; + void init_data() override; /** * Initialize the execution */ @@ -119,7 +131,13 @@ class DilateDistanceOperation : public NodeOperation { MemoryBuffer **inputMemoryBuffers, std::list<cl_mem> *clMemToCleanUp, std::list<cl_kernel> *clKernelsToCleanUp) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; + class ErodeDistanceOperation : public DilateDistanceOperation { public: ErodeDistanceOperation(); @@ -135,9 +153,13 @@ class ErodeDistanceOperation : public DilateDistanceOperation { MemoryBuffer **inputMemoryBuffers, std::list<cl_mem> *clMemToCleanUp, std::list<cl_kernel> *clKernelsToCleanUp) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; -class DilateStepOperation : public NodeOperation { +class DilateStepOperation : public MultiThreadedOperation { protected: /** * Cached reference to the inputProgram @@ -174,6 +196,11 @@ class DilateStepOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class ErodeStepOperation : public DilateStepOperation { @@ -181,6 +208,9 @@ class ErodeStepOperation : public DilateStepOperation { ErodeStepOperation(); void *initializeTileData(rcti *rect) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index 97bdc25af3b..102025ed915 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -146,4 +146,58 @@ bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/ return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void DirectionalBlurOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[0]; + const int iterations = pow(2.0f, this->m_data->iter); + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + const int x = it.x; + const int y = it.y; + float color_accum[4]; + input->read_elem_bilinear(x, y, color_accum); + + /* Blur pixel. */ + /* TODO(manzanilla): Many values used on iterations can be calculated beforehand. Create a + * table on operation initialization. */ + float ltx = this->m_tx; + float lty = this->m_ty; + float lsc = this->m_sc; + float lrot = this->m_rot; + for (int i = 0; i < iterations; i++) { + const float cs = cosf(lrot), ss = sinf(lrot); + const float isc = 1.0f / (1.0f + lsc); + + const float v = isc * (y - this->m_center_y_pix) + lty; + const float u = isc * (x - this->m_center_x_pix) + ltx; + + float color[4]; + input->read_elem_bilinear( + cs * u + ss * v + this->m_center_x_pix, cs * v - ss * u + this->m_center_y_pix, color); + add_v4_v4(color_accum, color); + + /* Double transformations. */ + ltx += this->m_tx; + lty += this->m_ty; + lrot += this->m_rot; + lsc += this->m_sc; + } + + mul_v4_v4fl(it.out, color_accum, 1.0f / (iterations + 1)); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h index 5555520462b..9a982bf6481 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "COM_QualityStepHelper.h" namespace blender::compositor { -class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper { +class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: SocketReader *m_inputProgram; NodeDBlurData *m_data; @@ -65,6 +65,11 @@ class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper MemoryBuffer **inputMemoryBuffers, std::list<cl_mem> *clMemToCleanUp, std::list<cl_kernel> *clKernelsToCleanUp) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc index 07075ae1d9d..875b161e208 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cc +++ b/source/blender/compositor/operations/COM_DotproductOperation.cc @@ -28,6 +28,7 @@ DotproductOperation::DotproductOperation() this->setResolutionInputSocketIndex(0); this->m_input1Operation = nullptr; this->m_input2Operation = nullptr; + flags.can_be_constant = true; } void DotproductOperation::initExecution() { @@ -55,4 +56,15 @@ void DotproductOperation::executePixelSampled(float output[4], output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]); } +void DotproductOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *input1 = it.in(0); + const float *input2 = it.in(1); + *it.out = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DotproductOperation.h b/source/blender/compositor/operations/COM_DotproductOperation.h index 728033bcf32..c3f39d43fff 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.h +++ b/source/blender/compositor/operations/COM_DotproductOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DotproductOperation : public NodeOperation { +class DotproductOperation : public MultiThreadedOperation { private: SocketReader *m_input1Operation; SocketReader *m_input2Operation; @@ -33,6 +33,10 @@ class DotproductOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc index 0af4eb43624..5ca64b02586 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc @@ -47,15 +47,15 @@ void GlareStreaksOperation::generateGlare(float *data, const float p4 = pow(4.0, (double)n); const float vxp = vx * p4, vyp = vy * p4; const float wt = pow((double)settings->fade, (double)p4); - const float cmo = 1.0f - - (float)pow((double)settings->colmod, - (double)n + - 1); // colormodulation amount relative to current pass + + /* Color-modulation amount relative to current pass. */ + const float cmo = 1.0f - (float)pow((double)settings->colmod, (double)n + 1); + float *tdstcol = tdst.getBuffer(); for (y = 0; y < tsrc.getHeight() && (!breaked); y++) { for (x = 0; x < tsrc.getWidth(); x++, tdstcol += 4) { - // first pass no offset, always same for every pass, exact copy, - // otherwise results in uneven brightness, only need once + /* First pass no offset, always same for every pass, exact copy, + * otherwise results in uneven brightness, only need once. */ if (n == 0) { tsrc.read(c1, x, y); } @@ -65,7 +65,7 @@ void GlareStreaksOperation::generateGlare(float *data, tsrc.readBilinear(c2, x + vxp, y + vyp); tsrc.readBilinear(c3, x + vxp * 2.0f, y + vyp * 2.0f); tsrc.readBilinear(c4, x + vxp * 3.0f, y + vyp * 3.0f); - // modulate color to look vaguely similar to a color spectrum + /* Modulate color to look vaguely similar to a color spectrum. */ c2[1] *= cmo; c2[2] *= cmo; diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index bfcd504177f..5e76c41752c 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -39,6 +39,7 @@ InpaintSimpleOperation::InpaintSimpleOperation() this->m_manhattan_distance = nullptr; this->m_cached_buffer = nullptr; this->m_cached_buffer_ready = false; + flags.is_fullframe_operation = true; } void InpaintSimpleOperation::initExecution() { @@ -286,4 +287,47 @@ bool InpaintSimpleOperation::determineDependingAreaOfInterest(rcti * /*input*/, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void InpaintSimpleOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ + MemoryBuffer *input = inputs[0]; + if (!m_cached_buffer_ready) { + if (input->is_a_single_elem()) { + MemoryBuffer *tmp = input->inflate(); + m_cached_buffer = tmp->release_ownership_buffer(); + delete tmp; + } + else { + m_cached_buffer = (float *)MEM_dupallocN(input->getBuffer()); + } + + this->calc_manhattan_distance(); + + int curr = 0; + int x, y; + while (this->next_pixel(x, y, curr, this->m_iterations)) { + this->pix_step(x, y); + } + m_cached_buffer_ready = true; + } + + const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); + MemoryBuffer buf(m_cached_buffer, num_channels, input->getWidth(), input->getHeight()); + output->copy_from(&buf, area); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index e3d27bf7704..e11610bd263 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -66,6 +66,13 @@ class InpaintSimpleOperation : public NodeOperation { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + private: void calc_manhattan_distance(); void clamp_xy(int &x, int &y); diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc index ada3cd6f159..82fb033bf24 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.cc +++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc @@ -30,6 +30,7 @@ MapRangeOperation::MapRangeOperation() this->addOutputSocket(DataType::Value); this->m_inputOperation = nullptr; this->m_useClamp = false; + flags.can_be_constant = true; } void MapRangeOperation::initExecution() @@ -104,4 +105,43 @@ void MapRangeOperation::deinitExecution() this->m_destMaxOperation = nullptr; } +void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float source_min = *it.in(1); + const float source_max = *it.in(2); + if (fabsf(source_max - source_min) < 1e-6f) { + it.out[0] = 0.0f; + continue; + } + + float value = *it.in(0); + const float dest_min = *it.in(3); + const float dest_max = *it.in(4); + if (value >= -BLENDER_ZMAX && value <= BLENDER_ZMAX) { + value = (value - source_min) / (source_max - source_min); + value = dest_min + value * (dest_max - dest_min); + } + else if (value > BLENDER_ZMAX) { + value = dest_max; + } + else { + value = dest_min; + } + + if (m_useClamp) { + if (dest_max > dest_min) { + CLAMP(value, dest_min, dest_max); + } + else { + CLAMP(value, dest_max, dest_min); + } + } + + it.out[0] = value; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.h b/source/blender/compositor/operations/COM_MapRangeOperation.h index a544c59887e..a01be14d528 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.h +++ b/source/blender/compositor/operations/COM_MapRangeOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_texture_types.h" namespace blender::compositor { @@ -27,7 +27,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class MapRangeOperation : public NodeOperation { +class MapRangeOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -68,6 +68,10 @@ class MapRangeOperation : public NodeOperation { { this->m_useClamp = value; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc index 03fa80d220d..94fecc3f49e 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.cc +++ b/source/blender/compositor/operations/COM_MapValueOperation.cc @@ -25,6 +25,7 @@ MapValueOperation::MapValueOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); this->m_inputOperation = nullptr; + flags.can_be_constant = true; } void MapValueOperation::initExecution() @@ -60,4 +61,27 @@ void MapValueOperation::deinitExecution() this->m_inputOperation = nullptr; } +void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float input = *it.in(0); + TexMapping *texmap = this->m_settings; + float value = (input + texmap->loc[0]) * texmap->size[0]; + if (texmap->flag & TEXMAP_CLIP_MIN) { + if (value < texmap->min[0]) { + value = texmap->min[0]; + } + } + if (texmap->flag & TEXMAP_CLIP_MAX) { + if (value > texmap->max[0]) { + value = texmap->max[0]; + } + } + + it.out[0] = value; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h index eb7714714e9..a595eac3155 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.h +++ b/source/blender/compositor/operations/COM_MapValueOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_texture_types.h" namespace blender::compositor { @@ -27,7 +27,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class MapValueOperation : public NodeOperation { +class MapValueOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -63,6 +63,10 @@ class MapValueOperation : public NodeOperation { { this->m_settings = settings; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index f93afcaab95..c3e72d2575f 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -27,6 +27,7 @@ NormalizeOperation::NormalizeOperation() this->m_imageReader = nullptr; this->m_cachedInstance = nullptr; this->flags.complex = true; + flags.can_be_constant = true; } void NormalizeOperation::initExecution() { @@ -56,6 +57,7 @@ void NormalizeOperation::deinitExecution() { this->m_imageReader = nullptr; delete this->m_cachedInstance; + m_cachedInstance = nullptr; NodeOperation::deinitMutex(); } @@ -127,4 +129,60 @@ void NormalizeOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) /* pass */ } +void NormalizeOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + NodeOperation *input = get_input_operation(0); + r_input_area.xmin = 0; + r_input_area.xmax = input->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = input->getHeight(); +} + +void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + if (m_cachedInstance == nullptr) { + MemoryBuffer *input = inputs[0]; + + /* Using generic two floats struct to store `x: min`, `y: multiply`. */ + NodeTwoFloats *minmult = new NodeTwoFloats(); + + float minv = 1.0f + BLENDER_ZMAX; + float maxv = -1.0f - BLENDER_ZMAX; + for (const float *elem : input->as_range()) { + const float value = *elem; + if ((value > maxv) && (value <= BLENDER_ZMAX)) { + maxv = value; + } + if ((value < minv) && (value >= -BLENDER_ZMAX)) { + minv = value; + } + } + + minmult->x = minv; + /* The case of a flat buffer would cause a divide by 0. */ + minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); + + m_cachedInstance = minmult; + } +} + +void NormalizeOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + NodeTwoFloats *minmult = m_cachedInstance; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float input_value = *it.in(0); + + *it.out = (input_value - minmult->x) * minmult->y; + + /* Clamp infinities. */ + CLAMP(*it.out, 0.0f, 1.0f); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.h b/source/blender/compositor/operations/COM_NormalizeOperation.h index c89ba372189..7af2aad8a88 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.h +++ b/source/blender/compositor/operations/COM_NormalizeOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" namespace blender::compositor { @@ -27,7 +27,7 @@ namespace blender::compositor { * \brief base class of normalize, implementing the simple normalize * \ingroup operation */ -class NormalizeOperation : public NodeOperation { +class NormalizeOperation : public MultiThreadedOperation { protected: /** * \brief Cached reference to the reader @@ -64,6 +64,14 @@ class NormalizeOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.cc b/source/blender/compositor/operations/COM_PosterizeOperation.cc new file mode 100644 index 00000000000..db5860f48f8 --- /dev/null +++ b/source/blender/compositor/operations/COM_PosterizeOperation.cc @@ -0,0 +1,82 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_PosterizeOperation.h" + +namespace blender::compositor { + +PosterizeOperation::PosterizeOperation() +{ + this->addInputSocket(DataType::Color); + this->addInputSocket(DataType::Value); + this->addOutputSocket(DataType::Color); + this->m_inputProgram = nullptr; + this->m_inputStepsProgram = nullptr; + flags.can_be_constant = true; +} + +void PosterizeOperation::initExecution() +{ + this->m_inputProgram = this->getInputSocketReader(0); + this->m_inputStepsProgram = this->getInputSocketReader(1); +} + +void PosterizeOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue[4]; + float inputSteps[4]; + + this->m_inputProgram->readSampled(inputValue, x, y, sampler); + this->m_inputStepsProgram->readSampled(inputSteps, x, y, sampler); + CLAMP(inputSteps[0], 2.0f, 1024.0f); + const float steps_inv = 1.0f / inputSteps[0]; + + output[0] = floor(inputValue[0] / steps_inv) * steps_inv; + output[1] = floor(inputValue[1] / steps_inv) * steps_inv; + output[2] = floor(inputValue[2] / steps_inv) * steps_inv; + output[3] = inputValue[3]; +} + +void PosterizeOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *in_value = it.in(0); + const float *in_steps = it.in(1); + float steps = in_steps[0]; + CLAMP(steps, 2.0f, 1024.0f); + const float steps_inv = 1.0f / steps; + + it.out[0] = floor(in_value[0] / steps_inv) * steps_inv; + it.out[1] = floor(in_value[1] / steps_inv) * steps_inv; + it.out[2] = floor(in_value[2] / steps_inv) * steps_inv; + it.out[3] = in_value[3]; + } +} + +void PosterizeOperation::deinitExecution() +{ + this->m_inputProgram = nullptr; + this->m_inputStepsProgram = nullptr; +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.h b/source/blender/compositor/operations/COM_PosterizeOperation.h new file mode 100644 index 00000000000..c625cbb83c6 --- /dev/null +++ b/source/blender/compositor/operations/COM_PosterizeOperation.h @@ -0,0 +1,56 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_MultiThreadedOperation.h" + +namespace blender::compositor { + +class PosterizeOperation : public MultiThreadedOperation { + private: + /** + * Cached reference to the inputProgram + */ + SocketReader *m_inputProgram; + SocketReader *m_inputStepsProgram; + + public: + PosterizeOperation(); + + /** + * The inner loop of this operation. + */ + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + /** + * Initialize the execution + */ + void initExecution() override; + + /** + * Deinitialize the execution + */ + void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index e7c11613aa3..fa8b5ffcabf 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -171,4 +171,43 @@ eCompositorPriority PreviewOperation::getRenderPriority() const return eCompositorPriority::Low; } +void PreviewOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + + r_input_area.xmin = output_area.xmin / m_divider; + r_input_area.xmax = output_area.xmax / m_divider; + r_input_area.ymin = output_area.ymin / m_divider; + r_input_area.ymax = output_area.ymax / m_divider; +} + +void PreviewOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *input = inputs[0]; + struct ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new( + m_viewSettings, m_displaySettings); + + rcti buffer_area; + BLI_rcti_init(&buffer_area, 0, this->getWidth(), 0, this->getHeight()); + BuffersIteratorBuilder<uchar> it_builder( + m_outputBuffer, buffer_area, area, COM_data_type_num_channels(DataType::Color)); + + for (BuffersIterator<uchar> it = it_builder.build(); !it.is_end(); ++it) { + const float rx = it.x / m_divider; + const float ry = it.y / m_divider; + + float color[4]; + input->read_elem_checked(rx, ry, color); + IMB_colormanagement_processor_apply_v4(cm_processor, color); + rgba_float_to_uchar(it.out, color); + } + + IMB_colormanagement_processor_free(cm_processor); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h index 0f43f01c5d6..05dae9c4dd8 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.h +++ b/source/blender/compositor/operations/COM_PreviewOperation.h @@ -20,13 +20,13 @@ #include "BKE_global.h" #include "BLI_rect.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_color_types.h" #include "DNA_image_types.h" namespace blender::compositor { -class PreviewOperation : public NodeOperation { +class PreviewOperation : public MultiThreadedOperation { protected: unsigned char *m_outputBuffer; @@ -63,6 +63,11 @@ class PreviewOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index b078d85372d..4153b9c8523 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -61,6 +61,8 @@ namespace blender::compositor { /*-----------------------------------------------------------------------------*/ /* Internal Functions to Sample Pixel Color from Image */ +/* TODO(manzanilla): to be removed with tiled implementation. Replace it with + * #buffer->read_elem_checked. */ static inline void sample(SocketReader *reader, int x, int y, float color[4]) { if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) { @@ -71,8 +73,13 @@ static inline void sample(SocketReader *reader, int x, int y, float color[4]) reader->read(color, x, y, nullptr); } -static void sample_bilinear_vertical( - SocketReader *reader, int x, int y, float yoffset, float color[4]) +static inline void sample(MemoryBuffer *reader, int x, int y, float color[4]) +{ + reader->read_elem_checked(x, y, color); +} + +template<typename T> +static void sample_bilinear_vertical(T *reader, int x, int y, float yoffset, float color[4]) { float iy = floorf(yoffset); float fy = yoffset - iy; @@ -89,8 +96,8 @@ static void sample_bilinear_vertical( color[3] = interpf(color01[3], color00[3], fy); } -static void sample_bilinear_horizontal( - SocketReader *reader, int x, int y, float xoffset, float color[4]) +template<typename T> +static void sample_bilinear_horizontal(T *reader, int x, int y, float xoffset, float color[4]) { float ix = floorf(xoffset); float fx = xoffset - ix; @@ -162,7 +169,7 @@ static void area_diag(int d1, int d2, int e1, int e2, float weights[2]) SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() { this->addInputSocket(DataType::Color); /* image */ - this->addInputSocket(DataType::Value); /* depth, material ID, etc. */ + this->addInputSocket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ this->addOutputSocket(DataType::Color); this->flags.complex = true; this->m_imageReader = nullptr; @@ -207,6 +214,16 @@ bool SMAAEdgeDetectionOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void SMAAEdgeDetectionOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + r_input_area.xmax = output_area.xmax + 1; + r_input_area.xmin = output_area.xmin - 2; + r_input_area.ymax = output_area.ymax + 1; + r_input_area.ymin = output_area.ymin - 2; +} + void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/) { float color[4]; @@ -288,6 +305,94 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi } } +void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *image = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + float color[4]; + const int x = it.x; + const int y = it.y; + + /* Calculate luma deltas: */ + image->read_elem_checked(x, y, color); + const float L = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x - 1, y, color); + const float Lleft = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x, y - 1, color); + const float Ltop = IMB_colormanagement_get_luminance(color); + const float Dleft = fabsf(L - Lleft); + const float Dtop = fabsf(L - Ltop); + + /* We do the usual threshold: */ + it.out[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f; + it.out[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f; + it.out[2] = 0.0f; + it.out[3] = 1.0f; + + /* Then discard if there is no edge: */ + if (is_zero_v2(it.out)) { + continue; + } + + /* Calculate right and bottom deltas: */ + image->read_elem_checked(x + 1, y, color); + const float Lright = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x, y + 1, color); + const float Lbottom = IMB_colormanagement_get_luminance(color); + const float Dright = fabsf(L - Lright); + const float Dbottom = fabsf(L - Lbottom); + + /* Calculate the maximum delta in the direct neighborhood: */ + float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); + + /* Calculate luma used for both left and top edges: */ + image->read_elem_checked(x - 1, y - 1, color); + const float Llefttop = IMB_colormanagement_get_luminance(color); + + /* Left edge */ + if (it.out[0] != 0.0f) { + /* Calculate deltas around the left pixel: */ + image->read_elem_checked(x - 2, y, color); + const float Lleftleft = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x - 1, y + 1, color); + const float Lleftbottom = IMB_colormanagement_get_luminance(color); + const float Dleftleft = fabsf(Lleft - Lleftleft); + const float Dlefttop = fabsf(Lleft - Llefttop); + const float Dleftbottom = fabsf(Lleft - Lleftbottom); + + /* Calculate the final maximum delta: */ + maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); + + /* Local contrast adaptation: */ + if (maxDelta > m_contrast_limit * Dleft) { + it.out[0] = 0.0f; + } + } + + /* Top edge */ + if (it.out[1] != 0.0f) { + /* Calculate top-top delta: */ + image->read_elem_checked(x, y - 2, color); + const float Ltoptop = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x + 1, y - 1, color); + const float Ltopright = IMB_colormanagement_get_luminance(color); + const float Dtoptop = fabsf(Ltop - Ltoptop); + const float Dtopleft = fabsf(Ltop - Llefttop); + const float Dtopright = fabsf(Ltop - Ltopright); + + /* Calculate the final maximum delta: */ + maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); + + /* Local contrast adaptation: */ + if (maxDelta > m_contrast_limit * Dtop) { + it.out[1] = 0.0f; + } + } + } +} + /*-----------------------------------------------------------------------------*/ /* Blending Weight Calculation (Second Pass) */ /*-----------------------------------------------------------------------------*/ @@ -309,6 +414,9 @@ void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) void SMAABlendingWeightCalculationOperation::initExecution() { this->m_imageReader = this->getInputSocketReader(0); + if (execution_model_ == eExecutionModel::Tiled) { + sample_image_fn_ = [=](int x, int y, float *out) { sample(m_imageReader, x, y, out); }; + } } void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding) @@ -414,6 +522,113 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], } } +void SMAABlendingWeightCalculationOperation::update_memory_buffer_started( + MemoryBuffer *UNUSED(output), const rcti &UNUSED(out_area), Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *image = inputs[0]; + sample_image_fn_ = [=](int x, int y, float *out) { image->read_elem_checked(x, y, out); }; +} + +void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( + MemoryBuffer *output, const rcti &out_area, Span<MemoryBuffer *> UNUSED(inputs)) +{ + for (BuffersIterator<float> it = output->iterate_with({}, out_area); !it.is_end(); ++it) { + const int x = it.x; + const int y = it.y; + zero_v4(it.out); + + float edges[4]; + sample_image_fn_(x, y, edges); + + /* Edge at north */ + float c[4]; + if (edges[1] > 0.0f) { + /* Diagonals have both north and west edges, so calculating weights for them */ + /* in one of the boundaries is enough. */ + calculateDiagWeights(x, y, edges, it.out); + + /* We give priority to diagonals, so if we find a diagonal we skip. */ + /* horizontal/vertical processing. */ + if (!is_zero_v2(it.out)) { + continue; + } + + /* Find the distance to the left and the right: */ + int left = searchXLeft(x, y); + int right = searchXRight(x, y); + int d1 = x - left, d2 = right - x; + + /* Fetch the left and right crossing edges: */ + int e1 = 0, e2 = 0; + sample_image_fn_(left, y - 1, c); + if (c[0] > 0.0) { + e1 += 1; + } + sample_image_fn_(left, y, c); + if (c[0] > 0.0) { + e1 += 2; + } + sample_image_fn_(right + 1, y - 1, c); + if (c[0] > 0.0) { + e2 += 1; + } + sample_image_fn_(right + 1, y, c); + if (c[0] > 0.0) { + e2 += 2; + } + + /* Ok, we know how this pattern looks like, now it is time for getting */ + /* the actual area: */ + area(d1, d2, e1, e2, it.out); /* R, G */ + + /* Fix corners: */ + if (m_corner_rounding) { + detectHorizontalCornerPattern(it.out, left, right, y, d1, d2); + } + } + + /* Edge at west */ + if (edges[0] > 0.0f) { + /* Did we already do diagonal search for this west edge from the left neighboring pixel? */ + if (isVerticalSearchUnneeded(x, y)) { + continue; + } + + /* Find the distance to the top and the bottom: */ + int top = searchYUp(x, y); + int bottom = searchYDown(x, y); + int d1 = y - top, d2 = bottom - y; + + /* Fetch the top and bottom crossing edges: */ + int e1 = 0, e2 = 0; + sample_image_fn_(x - 1, top, c); + if (c[1] > 0.0) { + e1 += 1; + } + sample_image_fn_(x, top, c); + if (c[1] > 0.0) { + e1 += 2; + } + sample_image_fn_(x - 1, bottom + 1, c); + if (c[1] > 0.0) { + e2 += 1; + } + sample_image_fn_(x, bottom + 1, c); + if (c[1] > 0.0) { + e2 += 2; + } + + /* Get the area for this direction: */ + area(d1, d2, e1, e2, it.out + 2); /* B, A */ + + /* Fix corners: */ + if (m_corner_rounding) { + detectVerticalCornerPattern(it.out + 2, x, top, bottom, d1, d2); + } + } + } +} + void SMAABlendingWeightCalculationOperation::deinitExecution() { this->m_imageReader = nullptr; @@ -434,6 +649,19 @@ bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void SMAABlendingWeightCalculationOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + r_input_area.xmax = output_area.xmax + + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); + r_input_area.xmin = output_area.xmin - + fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); + r_input_area.ymax = output_area.ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); + r_input_area.ymin = output_area.ymin - + fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); +} + /*-----------------------------------------------------------------------------*/ /* Diagonal Search Functions */ @@ -449,7 +677,7 @@ int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, b while (x != end) { x += dir; y -= dir; - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[1] == 0.0f) { *found = true; break; @@ -472,12 +700,12 @@ int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, b while (x != end) { x += dir; y += dir; - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[1] == 0.0f) { *found = true; break; } - sample(m_imageReader, x + 1, y, e); + sample_image_fn_(x + 1, y, e); if (e[0] == 0.0f) { *found = true; return (dir > 0) ? x : x - dir; @@ -522,11 +750,11 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Fetch the crossing edges: */ int left = x - d1, bottom = y + d1; - sample(m_imageReader, left - 1, bottom, c); + sample_image_fn_(left - 1, bottom, c); if (c[1] > 0.0) { e1 += 2; } - sample(m_imageReader, left, bottom, c); + sample_image_fn_(left, bottom, c); if (c[0] > 0.0) { e1 += 1; } @@ -536,11 +764,11 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Fetch the crossing edges: */ int right = x + d2, top = y - d2; - sample(m_imageReader, right + 1, top, c); + sample_image_fn_(right + 1, top, c); if (c[1] > 0.0) { e2 += 2; } - sample(m_imageReader, right + 1, top - 1, c); + sample_image_fn_(right + 1, top - 1, c); if (c[0] > 0.0) { e2 += 1; } @@ -552,7 +780,7 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Search for the line ends: */ d1 = x - searchDiag2(x, y, -1, &d1_found); - sample(m_imageReader, x + 1, y, e); + sample_image_fn_(x + 1, y, e); if (e[0] > 0.0f) { d2 = searchDiag2(x, y, 1, &d2_found) - x; } @@ -568,11 +796,11 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Fetch the crossing edges: */ int left = x - d1, top = y - d1; - sample(m_imageReader, left - 1, top, c); + sample_image_fn_(left - 1, top, c); if (c[1] > 0.0) { e1 += 2; } - sample(m_imageReader, left, top - 1, c); + sample_image_fn_(left, top - 1, c); if (c[0] > 0.0) { e1 += 1; } @@ -582,7 +810,7 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Fetch the crossing edges: */ int right = x + d2, bottom = y + d2; - sample(m_imageReader, right + 1, bottom, c); + sample_image_fn_(right + 1, bottom, c); if (c[1] > 0.0) { e2 += 2; } @@ -610,7 +838,7 @@ bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int } /* Search for the line ends: */ - sample(m_imageReader, x - 1, y, e); + sample_image_fn_(x - 1, y, e); if (e[1] > 0.0f) { d1 = x - searchDiag2(x - 1, y, -1, &found); } @@ -631,14 +859,14 @@ int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y) float e[4]; while (x > end) { - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[1] == 0.0f) { /* Is the edge not activated? */ break; } if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ return x; } - sample(m_imageReader, x, y - 1, e); + sample_image_fn_(x, y - 1, e); if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ return x; } @@ -655,12 +883,12 @@ int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y) while (x < end) { x++; - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[1] == 0.0f || /* Is the edge not activated? */ e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ break; } - sample(m_imageReader, x, y - 1, e); + sample_image_fn_(x, y - 1, e); if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ break; } @@ -675,14 +903,14 @@ int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y) float e[4]; while (y > end) { - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[0] == 0.0f) { /* Is the edge not activated? */ break; } if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ return y; } - sample(m_imageReader, x - 1, y, e); + sample_image_fn_(x - 1, y, e); if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ return y; } @@ -699,12 +927,12 @@ int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y) while (y < end) { y++; - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[0] == 0.0f || /* Is the edge not activated? */ e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ break; } - sample(m_imageReader, x - 1, y, e); + sample_image_fn_(x - 1, y, e); if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ break; } @@ -728,16 +956,16 @@ void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( /* Near the left corner */ if (d1 <= d2) { - sample(m_imageReader, left, y + 1, e); + sample_image_fn_(left, y + 1, e); factor[0] -= rounding * e[0]; - sample(m_imageReader, left, y - 2, e); + sample_image_fn_(left, y - 2, e); factor[1] -= rounding * e[0]; } /* Near the right corner */ if (d1 >= d2) { - sample(m_imageReader, right + 1, y + 1, e); + sample_image_fn_(right + 1, y + 1, e); factor[0] -= rounding * e[0]; - sample(m_imageReader, right + 1, y - 2, e); + sample_image_fn_(right + 1, y - 2, e); factor[1] -= rounding * e[0]; } @@ -757,16 +985,16 @@ void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( /* Near the top corner */ if (d1 <= d2) { - sample(m_imageReader, x + 1, top, e); + sample_image_fn_(x + 1, top, e); factor[0] -= rounding * e[1]; - sample(m_imageReader, x - 2, top, e); + sample_image_fn_(x - 2, top, e); factor[1] -= rounding * e[1]; } /* Near the bottom corner */ if (d1 >= d2) { - sample(m_imageReader, x + 1, bottom + 1, e); + sample_image_fn_(x + 1, bottom + 1, e); factor[0] -= rounding * e[1]; - sample(m_imageReader, x - 2, bottom + 1, e); + sample_image_fn_(x - 2, bottom + 1, e); factor[1] -= rounding * e[1]; } @@ -847,6 +1075,59 @@ void SMAANeighborhoodBlendingOperation::executePixel(float output[4], madd_v4_v4fl(output, color2, weight2); } +void SMAANeighborhoodBlendingOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &out_area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *image1 = inputs[0]; + MemoryBuffer *image2 = inputs[1]; + for (BuffersIterator<float> it = output->iterate_with({}, out_area); !it.is_end(); ++it) { + const float x = it.x; + const float y = it.y; + float w[4]; + + /* Fetch the blending weights for current pixel: */ + image2->read_elem_checked(x, y, w); + const float left = w[2], top = w[0]; + image2->read_elem_checked(x + 1, y, w); + const float right = w[3]; + image2->read_elem_checked(x, y + 1, w); + const float bottom = w[1]; + + /* Is there any blending weight with a value greater than 0.0? */ + if (right + bottom + left + top < 1e-5f) { + image1->read_elem_checked(x, y, it.out); + continue; + } + + /* Calculate the blending offsets: */ + void (*sample_fn)(MemoryBuffer * reader, int x, int y, float xoffset, float color[4]); + float offset1, offset2, weight1, weight2, color1[4], color2[4]; + + if (fmaxf(right, left) > fmaxf(bottom, top)) { /* `max(horizontal) > max(vertical)` */ + sample_fn = sample_bilinear_horizontal; + offset1 = right; + offset2 = -left; + weight1 = right / (right + left); + weight2 = left / (right + left); + } + else { + sample_fn = sample_bilinear_vertical; + offset1 = bottom; + offset2 = -top; + weight1 = bottom / (bottom + top); + weight2 = top / (bottom + top); + } + + /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */ + sample_fn(image1, x, y, offset1, color1); + sample_fn(image1, x, y, offset2, color2); + + mul_v4_v4fl(it.out, color1, weight1); + madd_v4_v4fl(it.out, color2, weight2); + } +} + void SMAANeighborhoodBlendingOperation::deinitExecution() { this->m_image1Reader = nullptr; @@ -866,4 +1147,12 @@ bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void SMAANeighborhoodBlendingOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + r_input_area = output_area; + expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SMAAOperation.h b/source/blender/compositor/operations/COM_SMAAOperation.h index 781762202b4..91b9299ee43 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.h +++ b/source/blender/compositor/operations/COM_SMAAOperation.h @@ -20,14 +20,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { /*-----------------------------------------------------------------------------*/ /* Edge Detection (First Pass) */ -class SMAAEdgeDetectionOperation : public NodeOperation { +class SMAAEdgeDetectionOperation : public MultiThreadedOperation { protected: SocketReader *m_imageReader; SocketReader *m_valueReader; @@ -60,15 +60,20 @@ class SMAAEdgeDetectionOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; /*-----------------------------------------------------------------------------*/ /* Blending Weight Calculation (Second Pass) */ -class SMAABlendingWeightCalculationOperation : public NodeOperation { +class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { private: SocketReader *m_imageReader; - + std::function<void(int x, int y, float *out)> sample_image_fn_; int m_corner_rounding; public: @@ -96,6 +101,14 @@ class SMAABlendingWeightCalculationOperation : public NodeOperation { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + private: /* Diagonal Search Functions */ int searchDiag1(int x, int y, int dir, bool *found); @@ -117,7 +130,7 @@ class SMAABlendingWeightCalculationOperation : public NodeOperation { /*-----------------------------------------------------------------------------*/ /* Neighborhood Blending (Third Pass) */ -class SMAANeighborhoodBlendingOperation : public NodeOperation { +class SMAANeighborhoodBlendingOperation : public MultiThreadedOperation { private: SocketReader *m_image1Reader; SocketReader *m_image2Reader; @@ -144,6 +157,11 @@ class SMAANeighborhoodBlendingOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index bbb6de2f39e..0161b837915 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -213,12 +213,12 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4], this->m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); this->m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); - const float scx = scaleX[0]; // target absolute scale - const float scy = scaleY[0]; // target absolute scale + const float scx = scaleX[0]; /* Target absolute scale. */ + const float scy = scaleY[0]; /* Target absolute scale. */ const float width = this->getWidth(); const float height = this->getHeight(); - // div + /* Divide. */ float relativeXScale = scx / width; float relativeYScale = scy / height; @@ -244,7 +244,7 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, const float scy = scaleY[0]; const float width = this->getWidth(); const float height = this->getHeight(); - // div + /* Divide. */ float relateveXScale = scx / width; float relateveYScale = scy / height; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 1f503051349..628da686a42 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -369,7 +369,7 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( void ScreenLensDistortionOperation::updateVariables(float distortion, float dispersion) { m_k[1] = max_ff(min_ff(distortion, 1.0f), -0.999f); - // smaller dispersion range for somewhat more control + /* Smaller dispersion range for somewhat more control. */ float d = 0.25f * max_ff(min_ff(dispersion, 1.0f), 0.0f); m_k[0] = max_ff(min_ff((m_k[1] + d), 1.0f), -0.999f); m_k[2] = max_ff(min_ff((m_k[1] - d), 1.0f), -0.999f); diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index 6af6f5a6244..b8274576cb5 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -28,11 +28,11 @@ namespace blender::compositor { VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation() { this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color, ResizeMode::None); // do not resize the bokeh image. - this->addInputSocket(DataType::Value); // radius + this->addInputSocket(DataType::Color, ResizeMode::None); /* Do not resize the bokeh image. */ + this->addInputSocket(DataType::Value); /* Radius. */ #ifdef COM_DEFOCUS_SEARCH - this->addInputSocket(DataType::Color, - ResizeMode::None); // inverse search radius optimization structure. + /* Inverse search radius optimization structure. */ + this->addInputSocket(DataType::Color, ResizeMode::None); #endif this->addOutputSocket(DataType::Color); flags.complex = true; @@ -438,10 +438,10 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer * } #ifdef COM_DEFOCUS_SEARCH -// InverseSearchRadiusOperation +/* #InverseSearchRadiusOperation. */ InverseSearchRadiusOperation::InverseSearchRadiusOperation() { - this->addInputSocket(DataType::Value, ResizeMode::None); // radius + this->addInputSocket(DataType::Value, ResizeMode::None); /* Radius. */ this->addOutputSocket(DataType::Color); this->flags.complex = true; this->m_inputRadius = nullptr; @@ -472,37 +472,39 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) offset += 4; } } - // for (x = rect->xmin; x < rect->xmax ; x++) { - // for (y = rect->ymin; y < rect->ymax ; y++) { - // int rx = x * DIVIDER; - // int ry = y * DIVIDER; - // float radius = 0.0f; - // float maxx = x; - // float maxy = y; - - // for (int x2 = 0 ; x2 < DIVIDER ; x2 ++) { - // for (int y2 = 0 ; y2 < DIVIDER ; y2 ++) { - // this->m_inputRadius->read(temp, rx+x2, ry+y2, PixelSampler::Nearest); - // if (radius < temp[0]) { - // radius = temp[0]; - // maxx = x2; - // maxy = y2; - // } - // } - // } - // int impactRadius = ceil(radius / DIVIDER); - // for (int x2 = x - impactRadius ; x2 < x + impactRadius ; x2 ++) { - // for (int y2 = y - impactRadius ; y2 < y + impactRadius ; y2 ++) { - // data->read(temp, x2, y2); - // temp[0] = MIN2(temp[0], maxx); - // temp[1] = MIN2(temp[1], maxy); - // temp[2] = MAX2(temp[2], maxx); - // temp[3] = MAX2(temp[3], maxy); - // data->writePixel(x2, y2, temp); - // } - // } - // } - // } +# if 0 + for (x = rect->xmin; x < rect->xmax; x++) { + for (y = rect->ymin; y < rect->ymax; y++) { + int rx = x * DIVIDER; + int ry = y * DIVIDER; + float radius = 0.0f; + float maxx = x; + float maxy = y; + + for (int x2 = 0; x2 < DIVIDER; x2++) { + for (int y2 = 0; y2 < DIVIDER; y2++) { + this->m_inputRadius->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); + if (radius < temp[0]) { + radius = temp[0]; + maxx = x2; + maxy = y2; + } + } + } + int impactRadius = ceil(radius / DIVIDER); + for (int x2 = x - impactRadius; x2 < x + impactRadius; x2++) { + for (int y2 = y - impactRadius; y2 < y + impactRadius; y2++) { + data->read(temp, x2, y2); + temp[0] = MIN2(temp[0], maxx); + temp[1] = MIN2(temp[1], maxy); + temp[2] = MAX2(temp[2], maxx); + temp[3] = MAX2(temp[3], maxy); + data->writePixel(x2, y2, temp); + } + } + } + } +# endif return data; } diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index df65044afc1..5405e6d424a 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -57,6 +57,7 @@ VectorBlurOperation::VectorBlurOperation() this->m_inputSpeedProgram = nullptr; this->m_inputZProgram = nullptr; flags.complex = true; + flags.is_fullframe_operation = true; } void VectorBlurOperation::initExecution() { @@ -121,6 +122,51 @@ bool VectorBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, return false; } +void VectorBlurOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ + if (!m_cachedInstance) { + MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; + const bool is_image_inflated = image->is_a_single_elem(); + image = is_image_inflated ? image->inflate() : image; + + /* Must be a copy because it's modified in #generateVectorBlur. */ + MemoryBuffer *speed = inputs[SPEED_INPUT_INDEX]; + speed = speed->is_a_single_elem() ? speed->inflate() : new MemoryBuffer(*speed); + + MemoryBuffer *z = inputs[Z_INPUT_INDEX]; + const bool is_z_inflated = z->is_a_single_elem(); + z = is_z_inflated ? z->inflate() : z; + + m_cachedInstance = (float *)MEM_dupallocN(image->getBuffer()); + this->generateVectorBlur(m_cachedInstance, image, speed, z); + + if (is_image_inflated) { + delete image; + } + delete speed; + if (is_z_inflated) { + delete z; + } + } + + const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); + MemoryBuffer buf(m_cachedInstance, num_channels, this->getWidth(), this->getHeight()); + output->copy_from(&buf, area); +} + void VectorBlurOperation::generateVectorBlur(float *data, MemoryBuffer *inputImage, MemoryBuffer *inputSpeed, diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h index dfcf1fb16f7..c30c150db3c 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.h +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h @@ -26,6 +26,10 @@ namespace blender::compositor { class VectorBlurOperation : public NodeOperation, public QualityStepHelper { private: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int Z_INPUT_INDEX = 1; + static constexpr int SPEED_INPUT_INDEX = 2; + /** * \brief Cached reference to the inputProgram */ @@ -68,6 +72,13 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + protected: void generateVectorBlur(float *data, MemoryBuffer *inputImage, diff --git a/source/blender/compositor/tests/COM_NodeOperation_test.cc b/source/blender/compositor/tests/COM_NodeOperation_test.cc new file mode 100644 index 00000000000..94e9fdeedb1 --- /dev/null +++ b/source/blender/compositor/tests/COM_NodeOperation_test.cc @@ -0,0 +1,169 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#include "testing/testing.h" + +#include "COM_ConstantOperation.h" + +namespace blender::compositor::tests { + +class NonHashedOperation : public NodeOperation { + public: + NonHashedOperation(int id) + { + set_id(id); + addOutputSocket(DataType::Value); + setWidth(2); + setHeight(3); + } +}; + +class NonHashedConstantOperation : public ConstantOperation { + float constant_; + + public: + NonHashedConstantOperation(int id) + { + set_id(id); + addOutputSocket(DataType::Value); + setWidth(2); + setHeight(3); + constant_ = 1.0f; + } + + const float *get_constant_elem() override + { + return &constant_; + } + + void set_constant(float value) + { + constant_ = value; + } +}; + +class HashedOperation : public NodeOperation { + private: + int param1; + float param2; + + public: + HashedOperation(NodeOperation &input, int width, int height) + { + addInputSocket(DataType::Value); + addOutputSocket(DataType::Color); + setWidth(width); + setHeight(height); + param1 = 2; + param2 = 7.0f; + + getInputSocket(0)->setLink(input.getOutputSocket()); + } + + void set_param1(int value) + { + param1 = value; + } + + void hash_output_params() override + { + hash_params(param1, param2); + } +}; + +static void test_non_equal_hashes_compare(NodeOperationHash &h1, + NodeOperationHash &h2, + NodeOperationHash &h3) +{ + if (h1 < h2) { + if (h3 < h1) { + EXPECT_TRUE(h3 < h2); + } + else if (h3 < h2) { + EXPECT_TRUE(h1 < h3); + } + else { + EXPECT_TRUE(h1 < h3); + EXPECT_TRUE(h2 < h3); + } + } + else { + EXPECT_TRUE(h2 < h1); + } +} + +TEST(NodeOperation, generate_hash) +{ + /* Constant input. */ + { + NonHashedConstantOperation input_op1(1); + input_op1.set_constant(1.0f); + EXPECT_EQ(input_op1.generate_hash(), std::nullopt); + + HashedOperation op1(input_op1, 6, 4); + std::optional<NodeOperationHash> hash1_opt = op1.generate_hash(); + EXPECT_NE(hash1_opt, std::nullopt); + NodeOperationHash hash1 = *hash1_opt; + + NonHashedConstantOperation input_op2(1); + input_op2.set_constant(1.0f); + HashedOperation op2(input_op2, 6, 4); + NodeOperationHash hash2 = *op2.generate_hash(); + EXPECT_EQ(hash1, hash2); + + input_op2.set_constant(3.0f); + hash2 = *op2.generate_hash(); + EXPECT_NE(hash1, hash2); + } + + /* Non constant input. */ + { + NonHashedOperation input_op(1); + EXPECT_EQ(input_op.generate_hash(), std::nullopt); + + HashedOperation op1(input_op, 6, 4); + HashedOperation op2(input_op, 6, 4); + NodeOperationHash hash1 = *op1.generate_hash(); + NodeOperationHash hash2 = *op2.generate_hash(); + EXPECT_EQ(hash1, hash2); + op1.set_param1(-1); + hash1 = *op1.generate_hash(); + EXPECT_NE(hash1, hash2); + + HashedOperation op3(input_op, 11, 14); + NodeOperationHash hash3 = *op3.generate_hash(); + EXPECT_NE(hash2, hash3); + EXPECT_NE(hash1, hash3); + + test_non_equal_hashes_compare(hash1, hash2, hash3); + test_non_equal_hashes_compare(hash3, hash2, hash1); + test_non_equal_hashes_compare(hash2, hash3, hash1); + test_non_equal_hashes_compare(hash3, hash1, hash2); + + NonHashedOperation input_op2(2); + HashedOperation op4(input_op2, 11, 14); + NodeOperationHash hash4 = *op4.generate_hash(); + EXPECT_NE(hash3, hash4); + + input_op2.set_id(1); + hash4 = *op4.generate_hash(); + EXPECT_EQ(hash3, hash4); + } +} + +} // namespace blender::compositor::tests diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 3ad26c6f4db..41253117096 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -161,6 +161,13 @@ set(LIB bf_blenkernel ) +if(WITH_PYTHON) + add_definitions(-DWITH_PYTHON) + list(APPEND INC + ../python + ) +endif() + blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index 17f5ca0db79..e9195a1eb26 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -145,15 +145,7 @@ typedef struct DEGObjectIterData { eEvaluationMode eval_mode; - /* **** Iteration over geometry components **** */ - - /* The object whose components we currently iterate over. - * This might point to #temp_dupli_object. */ - struct Object *geometry_component_owner; - /* Some identifier that is used to determine which geometry component should be returned next. */ - int geometry_component_id; - /* Temporary storage for an object that is created from a component. */ - struct Object temp_geometry_component_object; + struct Object *next_object; /* **** Iteration over dupli-list. *** */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index a739a0fe337..36c6b56caae 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -571,9 +571,10 @@ void DepsgraphNodeBuilder::build_id(ID *id) build_movieclip((MovieClip *)id); break; case ID_ME: - case ID_CU: case ID_MB: + case ID_CU: case ID_LT: + case ID_GD: case ID_HA: case ID_PT: case ID_VO: @@ -604,9 +605,6 @@ void DepsgraphNodeBuilder::build_id(ID *id) case ID_PA: build_particle_settings((ParticleSettings *)id); break; - case ID_GD: - build_gpencil((bGPdata *)id); - break; case ID_LI: case ID_IP: @@ -1852,22 +1850,6 @@ void DepsgraphNodeBuilder::build_image(Image *image) &image->id, NodeType::GENERIC_DATABLOCK, OperationCode::GENERIC_DATABLOCK_UPDATE); } -void DepsgraphNodeBuilder::build_gpencil(bGPdata *gpd) -{ - if (built_map_.checkIsBuiltAndTag(gpd)) { - return; - } - ID *gpd_id = &gpd->id; - - /* TODO(sergey): what about multiple users of same datablock? This should - * only get added once. */ - - /* The main reason Grease Pencil is included here is because the animation - * (and drivers) need to be hosted somewhere. */ - build_animdata(gpd_id); - build_parameters(gpd_id); -} - void DepsgraphNodeBuilder::build_cachefile(CacheFile *cache_file) { if (built_map_.checkIsBuiltAndTag(cache_file)) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 3b2dd44af0d..2378f3fc100 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -225,7 +225,6 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_texture(Tex *tex); virtual void build_image(Image *image); virtual void build_world(World *world); - virtual void build_gpencil(bGPdata *gpd); virtual void build_cachefile(CacheFile *cache_file); virtual void build_mask(Mask *mask); virtual void build_movieclip(MovieClip *clip); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index ab3081cb1ae..28cfc5a9e1a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -544,12 +544,13 @@ void DepsgraphRelationBuilder::build_id(ID *id) build_movieclip((MovieClip *)id); break; case ID_ME: - case ID_CU: case ID_MB: + case ID_CU: case ID_LT: case ID_HA: case ID_PT: case ID_VO: + case ID_GD: build_object_data_geometry_datablock(id); break; case ID_SPK: @@ -573,9 +574,6 @@ void DepsgraphRelationBuilder::build_id(ID *id) case ID_PA: build_particle_settings((ParticleSettings *)id); break; - case ID_GD: - build_gpencil((bGPdata *)id); - break; case ID_LI: case ID_IP: @@ -1008,11 +1006,13 @@ void DepsgraphRelationBuilder::build_object_parent(Object *object) /* Bone Parent */ case PARBONE: { - ComponentKey parent_bone_key(parent_id, NodeType::BONE, object->parsubstr); - OperationKey parent_transform_key( - parent_id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); - add_relation(parent_bone_key, object_transform_key, "Bone Parent"); - add_relation(parent_transform_key, object_transform_key, "Armature Parent"); + if (object->parsubstr[0] != '\0') { + ComponentKey parent_bone_key(parent_id, NodeType::BONE, object->parsubstr); + OperationKey parent_transform_key( + parent_id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); + add_relation(parent_bone_key, object_transform_key, "Bone Parent"); + add_relation(parent_transform_key, object_transform_key, "Armature Parent"); + } break; } @@ -2609,18 +2609,6 @@ void DepsgraphRelationBuilder::build_image(Image *image) build_parameters(&image->id); } -void DepsgraphRelationBuilder::build_gpencil(bGPdata *gpd) -{ - if (built_map_.checkIsBuiltAndTag(gpd)) { - return; - } - /* animation */ - build_animdata(&gpd->id); - build_parameters(&gpd->id); - - // TODO: parent object (when that feature is implemented) -} - void DepsgraphRelationBuilder::build_cachefile(CacheFile *cache_file) { if (built_map_.checkIsBuiltAndTag(cache_file)) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 21d1d4b6268..1ad61c25305 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -286,7 +286,6 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_freestyle_linestyle(FreestyleLineStyle *linestyle); virtual void build_texture(Tex *tex); virtual void build_image(Image *image); - virtual void build_gpencil(bGPdata *gpd); virtual void build_cachefile(CacheFile *cache_file); virtual void build_mask(Mask *mask); virtual void build_movieclip(MovieClip *clip); diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index 770d9775dd3..7af3d03d478 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -120,130 +120,6 @@ bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject return false; } -void deg_iterator_components_init(DEGObjectIterData *data, Object *object) -{ - data->geometry_component_owner = object; - data->geometry_component_id = 0; -} - -/* Returns false when iterator is exhausted. */ -bool deg_iterator_components_step(BLI_Iterator *iter) -{ - DEGObjectIterData *data = (DEGObjectIterData *)iter->data; - if (data->geometry_component_owner == nullptr) { - return false; - } - - if (data->geometry_component_owner->runtime.geometry_set_eval == nullptr) { - /* Return the object itself, if it does not have a geometry set yet. */ - iter->current = data->geometry_component_owner; - data->geometry_component_owner = nullptr; - return true; - } - - GeometrySet *geometry_set = data->geometry_component_owner->runtime.geometry_set_eval; - if (geometry_set == nullptr) { - data->geometry_component_owner = nullptr; - return false; - } - - /* The mesh component. */ - if (data->geometry_component_id == 0) { - data->geometry_component_id++; - - /* Don't use a temporary object for this component, when the owner is a mesh object. */ - if (data->geometry_component_owner->type == OB_MESH) { - iter->current = data->geometry_component_owner; - return true; - } - - const Mesh *mesh = geometry_set->get_mesh_for_read(); - if (mesh != nullptr) { - Object *temp_object = &data->temp_geometry_component_object; - *temp_object = *data->geometry_component_owner; - temp_object->type = OB_MESH; - temp_object->data = (void *)mesh; - temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; - iter->current = temp_object; - return true; - } - } - - /* The pointcloud component. */ - if (data->geometry_component_id == 1) { - data->geometry_component_id++; - - /* Don't use a temporary object for this component, when the owner is a point cloud object. */ - if (data->geometry_component_owner->type == OB_POINTCLOUD) { - iter->current = data->geometry_component_owner; - return true; - } - - const PointCloud *pointcloud = geometry_set->get_pointcloud_for_read(); - if (pointcloud != nullptr) { - Object *temp_object = &data->temp_geometry_component_object; - *temp_object = *data->geometry_component_owner; - temp_object->type = OB_POINTCLOUD; - temp_object->data = (void *)pointcloud; - temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; - iter->current = temp_object; - return true; - } - } - - /* The volume component. */ - if (data->geometry_component_id == 2) { - data->geometry_component_id++; - - /* Don't use a temporary object for this component, when the owner is a volume object. */ - if (data->geometry_component_owner->type == OB_VOLUME) { - iter->current = data->geometry_component_owner; - return true; - } - - const VolumeComponent *component = geometry_set->get_component_for_read<VolumeComponent>(); - if (component != nullptr) { - const Volume *volume = component->get_for_read(); - - if (volume != nullptr) { - Object *temp_object = &data->temp_geometry_component_object; - *temp_object = *data->geometry_component_owner; - temp_object->type = OB_VOLUME; - temp_object->data = (void *)volume; - temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; - iter->current = temp_object; - return true; - } - } - } - - /* The curve component. */ - if (data->geometry_component_id == 3) { - data->geometry_component_id++; - - const CurveComponent *component = geometry_set->get_component_for_read<CurveComponent>(); - if (component != nullptr) { - const Curve *curve = component->get_curve_for_render(); - - if (curve != nullptr) { - Object *temp_object = &data->temp_geometry_component_object; - *temp_object = *data->geometry_component_owner; - temp_object->type = OB_CURVE; - temp_object->data = (void *)curve; - /* Assign data_eval here too, because curve rendering code tries - * to use a mesh if it can find one in this pointer. */ - temp_object->runtime.data_eval = (ID *)curve; - temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; - iter->current = temp_object; - return true; - } - } - } - - data->geometry_component_owner = nullptr; - return false; -} - void deg_iterator_duplis_init(DEGObjectIterData *data, Object *object) { if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) && @@ -292,6 +168,9 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data) temp_dupli_object->dt = MIN2(temp_dupli_object->dt, dupli_parent->dt); copy_v4_v4(temp_dupli_object->color, dupli_parent->color); temp_dupli_object->runtime.select_id = dupli_parent->runtime.select_id; + if (dob->ob->data != dob->ob_data) { + BKE_object_replace_data_on_shallow_copy(temp_dupli_object, dob->ob_data); + } /* Duplicated elements shouldn't care whether their original collection is visible or not. */ temp_dupli_object->base_flag |= BASE_VISIBLE_DEPSGRAPH; @@ -308,7 +187,7 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data) copy_m4_m4(data->temp_dupli_object.obmat, dob->mat); invert_m4_m4(data->temp_dupli_object.imat, data->temp_dupli_object.obmat); - deg_iterator_components_init(data, &data->temp_dupli_object); + data->next_object = &data->temp_dupli_object; BLI_assert(deg::deg_validate_copy_on_write_datablock(&data->temp_dupli_object.id)); return true; } @@ -377,7 +256,7 @@ bool deg_iterator_objects_step(DEGObjectIterData *data) } if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) { - deg_iterator_components_init(data, object); + data->next_object = object; } data->id_node_index++; return true; @@ -400,6 +279,7 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data) return; } + data->next_object = nullptr; data->dupli_parent = nullptr; data->dupli_list = nullptr; data->dupli_object_next = nullptr; @@ -408,8 +288,6 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data) data->id_node_index = 0; data->num_id_nodes = num_id_nodes; data->eval_mode = DEG_get_mode(depsgraph); - data->geometry_component_id = 0; - data->geometry_component_owner = nullptr; deg_invalidate_iterator_work_data(data); DEG_iterator_objects_next(iter); @@ -419,7 +297,9 @@ void DEG_iterator_objects_next(BLI_Iterator *iter) { DEGObjectIterData *data = (DEGObjectIterData *)iter->data; while (true) { - if (deg_iterator_components_step(iter)) { + if (data->next_object != nullptr) { + iter->current = data->next_object; + data->next_object = nullptr; return; } if (deg_iterator_duplis_step(data)) { diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index ad88cf656ad..c816c7b8db5 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -41,6 +41,10 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + #include "atomic_ops.h" #include "intern/depsgraph.h" @@ -375,6 +379,11 @@ void deg_evaluate_on_refresh(Depsgraph *graph) graph->debug.begin_graph_evaluation(); +#ifdef WITH_PYTHON + /* Release the GIL so that Python drivers can be evaluated. See T91046. */ + BPy_BEGIN_ALLOW_THREADS; +#endif + graph->is_evaluating = true; depsgraph_ensure_view_layer(graph); /* Set up evaluation state. */ @@ -415,6 +424,10 @@ void deg_evaluate_on_refresh(Depsgraph *graph) deg_graph_clear_tags(graph); graph->is_evaluating = false; +#ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +#endif + graph->debug.end_graph_evaluation(); } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc index 30ec9e948fd..1081528ece1 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc @@ -98,7 +98,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) object->runtime = runtime; object->runtime.data_orig = data_orig; object->runtime.bb = bb; - if (ELEM(object->type, OB_MESH, OB_LATTICE) && data_eval != nullptr) { + if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVE, OB_FONT) && data_eval != nullptr) { if (object->id.recalc & ID_RECALC_GEOMETRY) { /* If geometry is tagged for update it means, that part of * evaluated mesh are not valid anymore. In this case we can not @@ -112,9 +112,11 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) BKE_object_free_derived_caches(object); } else { - /* Do same thing as object update: override actual object data - * pointer with evaluated datablock. */ - object->data = data_eval; + /* Do same thing as object update: override actual object data pointer with evaluated + * datablock, but only if the evaluated data has the same type as the original data. */ + if (GS(((ID *)object->data)->name) == GS(data_eval->name)) { + object->data = data_eval; + } /* Evaluated mesh simply copied edit_mesh pointer from * original mesh during update, need to make sure no dead diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 257eb80ae0b..71115c5ceb9 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -91,7 +91,7 @@ set(SRC intern/draw_cache_impl_particles.c intern/draw_cache_impl_pointcloud.c intern/draw_cache_impl_volume.c - intern/draw_color_management.c + intern/draw_color_management.cc intern/draw_common.c intern/draw_debug.c intern/draw_fluid.c @@ -447,6 +447,7 @@ data_to_c_simple(engines/overlay/shaders/extra_wire_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/extra_wire_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/facing_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/facing_vert.glsl SRC) +data_to_c_simple(engines/overlay/shaders/grid_background_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/grid_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/grid_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/image_vert.glsl SRC) diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 8a35ab2aeb9..a125a13eaf9 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -110,6 +110,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, bool use_obedit_skip, bool draw_surface, bool use_nearest, + const bool do_material_sub_selection, const struct rcti *rect, DRW_SelectPassFn select_pass_fn, void *select_pass_user_data, diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index c120df7e897..87f5c6f5857 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -25,9 +25,12 @@ #include "DRW_render.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" +#include "BLI_alloca.h" + #include "DNA_particle_types.h" #include "GPU_shader.h" @@ -80,6 +83,7 @@ typedef struct BASIC_PrivateData { DRWShadingGroup *depth_shgrp[2]; DRWShadingGroup *depth_shgrp_cull[2]; DRWShadingGroup *depth_hair_shgrp[2]; + bool use_material_slot_selection; } BASIC_PrivateData; /* Transient data */ /* Functions */ @@ -131,6 +135,8 @@ static void basic_cache_init(void *vedata) stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); } + stl->g_data->use_material_slot_selection = DRW_state_is_material_select(); + /* Twice for normal and in front objects. */ for (int i = 0; i < 2; i++) { DRWState clip_state = (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) ? DRW_STATE_CLIP_PLANES : 0; @@ -155,6 +161,38 @@ static void basic_cache_init(void *vedata) } } +/* TODO(fclem): DRW_cache_object_surface_material_get needs a refactor to allow passing NULL + * instead of gpumat_array. Avoiding all this boilerplate code. */ +static struct GPUBatch **basic_object_surface_material_get(Object *ob) +{ + const int materials_len = DRW_cache_object_material_count_get(ob); + struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); + memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); + + return DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); +} + +static void basic_cache_populate_particles(void *vedata, Object *ob) +{ + const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; + BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl; + for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) { + if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { + continue; + } + ParticleSettings *part = psys->part; + const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; + if (draw_as == PART_DRAW_PATH) { + struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL); + if (stl->g_data->use_material_slot_selection) { + const short material_slot = part->omat; + DRW_select_load_id(ob->runtime.select_id | (material_slot << 16)); + } + DRW_shgroup_call(stl->g_data->depth_hair_shgrp[do_in_front], hairs, NULL); + } + } +} + static void basic_cache_populate(void *vedata, Object *ob) { BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl; @@ -165,24 +203,13 @@ static void basic_cache_populate(void *vedata, Object *ob) return; } - bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; - const DRWContextState *draw_ctx = DRW_context_state_get(); if (ob != draw_ctx->object_edit) { - for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) { - if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { - continue; - } - ParticleSettings *part = psys->part; - const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; - if (draw_as == PART_DRAW_PATH) { - struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL); - DRW_shgroup_call(stl->g_data->depth_hair_shgrp[do_in_front], hairs, NULL); - } - } + basic_cache_populate_particles(vedata, ob); } /* Make flat object selectable in ortho view if wireframe is enabled. */ + const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; if ((draw_ctx->v3d->overlay.flag & V3D_OVERLAY_WIREFRAMES) || (draw_ctx->v3d->shading.type == OB_WIRE) || (ob->dtx & OB_DRAWWIRE) || (ob->dt == OB_WIRE)) { int flat_axis = 0; @@ -211,9 +238,25 @@ static void basic_cache_populate(void *vedata, Object *ob) DRW_shgroup_call_sculpt(shgrp, ob, false, false); } else { - struct GPUBatch *geom = DRW_cache_object_surface_get(ob); - if (geom) { - DRW_shgroup_call(shgrp, geom, ob); + if (stl->g_data->use_material_slot_selection && BKE_object_supports_material_slots(ob)) { + struct GPUBatch **geoms = basic_object_surface_material_get(ob); + if (geoms) { + const int materials_len = DRW_cache_object_material_count_get(ob); + for (int i = 0; i < materials_len; i++) { + if (geoms[i] == NULL) { + continue; + } + const short material_slot_select_id = i + 1; + DRW_select_load_id(ob->runtime.select_id | (material_slot_select_id << 16)); + DRW_shgroup_call(shgrp, geoms[i], ob); + } + } + } + else { + struct GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + DRW_shgroup_call(shgrp, geom, ob); + } } } } diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index d3a0c40fae5..1078cebdbff 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -240,8 +240,9 @@ void GPENCIL_cache_init(void *ved) } else { pd->do_onion = true; - pd->simplify_fill = false; - pd->simplify_fx = false; + Scene *scene = draw_ctx->scene; + pd->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, false); + pd->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, false); pd->fade_layer_opacity = -1.0f; pd->playing = false; } diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.c index fd68b319f02..5356700f156 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_text.c +++ b/source/blender/draw/engines/overlay/overlay_edit_text.c @@ -180,19 +180,12 @@ static void edit_text_cache_populate_boxes(OVERLAY_Data *vedata, Object *ob) void OVERLAY_edit_text_cache_populate(OVERLAY_Data *vedata, Object *ob) { OVERLAY_PrivateData *pd = vedata->stl->pd; - Curve *cu = ob->data; struct GPUBatch *geom; bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; - bool has_surface = (cu->flag & (CU_FRONT | CU_BACK)) || cu->ext1 != 0.0f || cu->ext2 != 0.0f; - if ((cu->flag & CU_FAST) || !has_surface) { - geom = DRW_cache_text_edge_wire_get(ob); - if (geom) { - DRW_shgroup_call(pd->edit_text_wire_grp[do_in_front], geom, ob); - } - } - else { - /* object mode draws */ + geom = DRW_cache_text_edge_wire_get(ob); + if (geom) { + DRW_shgroup_call(pd->edit_text_wire_grp[do_in_front], geom, ob); } edit_text_cache_populate_select(vedata, ob); diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c index 5bb157ed081..60cda9f2d61 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.c +++ b/source/blender/draw/engines/overlay/overlay_grid.c @@ -61,10 +61,19 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) if (pd->space_type == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; - shd->grid_flag = ED_space_image_has_buffer(sima) ? 0 : PLANE_IMAGE | SHOW_GRID; + if (sima->mode == SI_MODE_UV || !ED_space_image_has_buffer(sima)) { + shd->grid_flag = GRID_BACK | PLANE_IMAGE | SHOW_GRID; + } + else { + shd->grid_flag = 0; + } + shd->grid_distance = 1.0f; - copy_v3_fl3( - shd->grid_size, (float)sima->tile_grid_shape[0], (float)sima->tile_grid_shape[1], 1.0f); + copy_v3_fl3(shd->grid_size, 1.0f, 1.0f, 1.0f); + if (sima->mode == SI_MODE_UV) { + shd->grid_size[0] = (float)sima->tile_grid_shape[0]; + shd->grid_size[1] = (float)sima->tile_grid_shape[1]; + } for (int step = 0; step < 8; step++) { shd->grid_steps[step] = powf(4, step) * (1.0f / 16.0f); } @@ -209,11 +218,12 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *vedata) float mat[4][4]; /* add quad background */ - sh = OVERLAY_shader_grid_image(); + sh = OVERLAY_shader_grid_background(); grp = DRW_shgroup_create(sh, psl->grid_ps); float color_back[4]; interp_v4_v4v4(color_back, G_draw.block.colorBackground, G_draw.block.colorGrid, 0.5); DRW_shgroup_uniform_vec4_copy(grp, "color", color_back); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); unit_m4(mat); mat[0][0] = shd->grid_size[0]; mat[1][1] = shd->grid_size[1]; diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c index 60a90616d29..91a25fe16d5 100644 --- a/source/blender/draw/engines/overlay/overlay_paint.c +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -55,6 +55,12 @@ static bool paint_object_is_rendered_transparent(View3D *v3d, Object *ob) } } } + + /* Check object display types. */ + if (ob && ELEM(ob->dt, OB_WIRE, OB_BOUNDBOX)) { + return true; + } + return false; } diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 68f60bee779..23df571e8de 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -722,6 +722,7 @@ GPUShader *OVERLAY_shader_extra_point(void); GPUShader *OVERLAY_shader_facing(void); GPUShader *OVERLAY_shader_gpencil_canvas(void); GPUShader *OVERLAY_shader_grid(void); +GPUShader *OVERLAY_shader_grid_background(void); GPUShader *OVERLAY_shader_grid_image(void); GPUShader *OVERLAY_shader_image(void); GPUShader *OVERLAY_shader_motion_path_line(void); diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index edf9148c8c0..389704b3d66 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -91,6 +91,7 @@ extern char datatoc_extra_wire_frag_glsl[]; extern char datatoc_extra_wire_vert_glsl[]; extern char datatoc_facing_frag_glsl[]; extern char datatoc_facing_vert_glsl[]; +extern char datatoc_grid_background_frag_glsl[]; extern char datatoc_grid_frag_glsl[]; extern char datatoc_grid_vert_glsl[]; extern char datatoc_image_frag_glsl[]; @@ -198,6 +199,7 @@ typedef struct OVERLAY_Shaders { GPUShader *facing; GPUShader *gpencil_canvas; GPUShader *grid; + GPUShader *grid_background; GPUShader *grid_image; GPUShader *image; GPUShader *motion_path_line; @@ -1053,6 +1055,20 @@ GPUShader *OVERLAY_shader_grid(void) return sh_data->grid; } +GPUShader *OVERLAY_shader_grid_background(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->grid_background) { + sh_data->grid_background = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_view_lib_glsl, + datatoc_edit_uv_tiled_image_borders_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_grid_background_frag_glsl, NULL}, + }); + } + return sh_data->grid_background; +} + GPUShader *OVERLAY_shader_grid_image(void) { OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index b8a61ecc403..fde376beeb2 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -218,18 +218,10 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, struct GPUBatch *geom = NULL; switch (ob->type) { case OB_CURVE: - if (!pd->wireframe_mode && !use_wire && ob->runtime.curve_cache && - BKE_displist_has_faces(&ob->runtime.curve_cache->disp)) { - break; - } geom = DRW_cache_curve_edge_wire_get(ob); break; case OB_FONT: - if (!pd->wireframe_mode && !use_wire && ob->runtime.curve_cache && - BKE_displist_has_faces(&ob->runtime.curve_cache->disp)) { - break; - } - geom = DRW_cache_text_loose_edges_get(ob); + geom = DRW_cache_text_edge_wire_get(ob); break; case OB_SURF: geom = DRW_cache_surf_edge_wire_get(ob); diff --git a/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl new file mode 100644 index 00000000000..f09918da6dc --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl @@ -0,0 +1,12 @@ + +uniform vec4 color; +uniform sampler2D depthBuffer; + +out vec4 fragColor; + +void main() +{ + fragColor = color; + float scene_depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; + fragColor.a = (scene_depth == 1.0) ? 1.0 : 0.0; +} diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 1292eea4c12..660a4adaf51 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -63,6 +63,10 @@ #include "DEG_depsgraph.h" +#ifdef __cplusplus +extern "C" { +#endif + struct GPUBatch; struct GPUMaterial; struct GPUShader; @@ -727,9 +731,9 @@ void DRW_select_load_id(uint id); /* Draw State */ bool DRW_state_is_fbo(void); bool DRW_state_is_select(void); +bool DRW_state_is_material_select(void); bool DRW_state_is_depth(void); bool DRW_state_is_image_render(void); -bool DRW_state_do_color_management(void); bool DRW_state_is_scene_render(void); bool DRW_state_is_opengl_render(void); bool DRW_state_is_playback(void); @@ -775,3 +779,7 @@ typedef struct DRWContextState { } DRWContextState; const DRWContextState *DRW_context_state_get(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 000ab540813..a3a5d6b065a 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -794,6 +794,10 @@ GPUBatch *DRW_gpencil_dummy_buffer_get(void) /* -------------------------------------------------------------------- */ /** \name Common Object API + * + * \note Curve and text objects evaluate to the evaluated geometry set's mesh component if + * they have a surface, so curve objects themselves do not have a surface (the mesh component + * is presented to render engines as a separate object). * \{ */ GPUBatch *DRW_cache_object_all_edges_get(Object *ob) @@ -814,11 +818,11 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold) case OB_MESH: return DRW_cache_mesh_edge_detection_get(ob, r_is_manifold); case OB_CURVE: - return DRW_cache_curve_edge_detection_get(ob, r_is_manifold); + return NULL; case OB_SURF: return DRW_cache_surf_edge_detection_get(ob, r_is_manifold); case OB_FONT: - return DRW_cache_text_edge_detection_get(ob, r_is_manifold); + return NULL; case OB_MBALL: return DRW_cache_mball_edge_detection_get(ob, r_is_manifold); case OB_HAIR: @@ -838,11 +842,11 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob) case OB_MESH: return DRW_cache_mesh_face_wireframe_get(ob); case OB_CURVE: - return DRW_cache_curve_face_wireframe_get(ob); + return NULL; case OB_SURF: return DRW_cache_surf_face_wireframe_get(ob); case OB_FONT: - return DRW_cache_text_face_wireframe_get(ob); + return NULL; case OB_MBALL: return DRW_cache_mball_face_wireframe_get(ob); case OB_HAIR: @@ -865,11 +869,11 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob) case OB_MESH: return DRW_cache_mesh_loose_edges_get(ob); case OB_CURVE: - return DRW_cache_curve_loose_edges_get(ob); + return NULL; case OB_SURF: return DRW_cache_surf_loose_edges_get(ob); case OB_FONT: - return DRW_cache_text_loose_edges_get(ob); + return NULL; case OB_MBALL: return NULL; case OB_HAIR: @@ -889,11 +893,11 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob) case OB_MESH: return DRW_cache_mesh_surface_get(ob); case OB_CURVE: - return DRW_cache_curve_surface_get(ob); + return NULL; case OB_SURF: return DRW_cache_surf_surface_get(ob); case OB_FONT: - return DRW_cache_text_surface_get(ob); + return NULL; case OB_MBALL: return DRW_cache_mball_surface_get(ob); case OB_HAIR: @@ -939,9 +943,9 @@ int DRW_cache_object_material_count_get(struct Object *ob) Mesh *me = BKE_object_get_evaluated_mesh(ob); if (me != NULL && type != OB_POINTCLOUD) { - /* Some object types (e.g. curves) can have a Curve in ob->data, but will be rendered as mesh. - * For point clouds this never happens. Ideally this check would happen at another level and we - * would just have to care about ob->data here. */ + /* Some object types can have one data type in ob->data, but will be rendered as mesh. + * For point clouds this never happens. Ideally this check would happen at another level + * and we would just have to care about ob->data here. */ type = OB_MESH; } @@ -974,11 +978,11 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, case OB_MESH: return DRW_cache_mesh_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_CURVE: - return DRW_cache_curve_surface_shaded_get(ob, gpumat_array, gpumat_array_len); + return NULL; case OB_SURF: return DRW_cache_surf_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_FONT: - return DRW_cache_text_surface_shaded_get(ob, gpumat_array, gpumat_array_len); + return NULL; case OB_MBALL: return DRW_cache_mball_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_HAIR: @@ -2929,20 +2933,13 @@ GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(Object *ob) GPUBatch *DRW_cache_curve_edge_wire_get(Object *ob) { BLI_assert(ob->type == OB_CURVE); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_loose_edges(mesh_eval); - } - return DRW_curve_batch_cache_get_wire_edge(cu); } GPUBatch *DRW_cache_curve_edge_normal_get(Object *ob) { BLI_assert(ob->type == OB_CURVE); - struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_normal_edge(cu); } @@ -2963,75 +2960,6 @@ GPUBatch *DRW_cache_curve_vert_overlay_get(Object *ob) return DRW_curve_batch_cache_get_edit_verts(cu); } -GPUBatch *DRW_cache_curve_surface_get(Object *ob) -{ - BLI_assert(ob->type == OB_CURVE); - - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface(mesh_eval); - } - - return DRW_curve_batch_cache_get_triangles_with_normals(cu); -} - -GPUBatch *DRW_cache_curve_loose_edges_get(Object *ob) -{ - BLI_assert(ob->type == OB_CURVE); - - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_loose_edges(mesh_eval); - } - - /* TODO */ - UNUSED_VARS(cu); - return NULL; -} - -GPUBatch *DRW_cache_curve_face_wireframe_get(Object *ob) -{ - BLI_assert(ob->type == OB_CURVE); - - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_wireframes_face(mesh_eval); - } - - return DRW_curve_batch_cache_get_wireframes_face(cu); -} - -GPUBatch *DRW_cache_curve_edge_detection_get(Object *ob, bool *r_is_manifold) -{ - BLI_assert(ob->type == OB_CURVE); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_edge_detection(mesh_eval, r_is_manifold); - } - - return DRW_curve_batch_cache_get_edge_detection(cu, r_is_manifold); -} - -/* Return list of batches */ -GPUBatch **DRW_cache_curve_surface_shaded_get(Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len) -{ - BLI_assert(ob->type == OB_CURVE); - - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); - } - - return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -3075,96 +3003,9 @@ GPUBatch *DRW_cache_text_edge_wire_get(Object *ob) { BLI_assert(ob->type == OB_FONT); struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - const bool has_surface = (cu->flag & (CU_FRONT | CU_BACK)) || cu->ext1 != 0.0f || - cu->ext2 != 0.0f; - if (!has_surface) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_loose_edges(mesh_eval); - } - - return DRW_curve_batch_cache_get_wire_edge(cu); -} - -GPUBatch *DRW_cache_text_surface_get(Object *ob) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface(mesh_eval); - } - - return DRW_curve_batch_cache_get_triangles_with_normals(cu); -} - -GPUBatch *DRW_cache_text_edge_detection_get(Object *ob, bool *r_is_manifold) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_edge_detection(mesh_eval, r_is_manifold); - } - - return DRW_curve_batch_cache_get_edge_detection(cu, r_is_manifold); -} - -GPUBatch *DRW_cache_text_loose_edges_get(Object *ob) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_loose_edges(mesh_eval); - } - return DRW_curve_batch_cache_get_wire_edge(cu); } -GPUBatch *DRW_cache_text_face_wireframe_get(Object *ob) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_wireframes_face(mesh_eval); - } - - return DRW_curve_batch_cache_get_wireframes_face(cu); -} - -GPUBatch **DRW_cache_text_surface_shaded_get(Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); - } - - return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -3544,6 +3385,8 @@ void drw_batch_cache_validate(Object *ob) break; case OB_CURVE: case OB_FONT: + DRW_curve_batch_cache_validate((Curve *)ob->data); + break; case OB_SURF: if (mesh_eval != NULL) { DRW_mesh_batch_cache_validate(mesh_eval); @@ -3592,6 +3435,8 @@ void drw_batch_cache_generate_requested(Object *ob) break; case OB_CURVE: case OB_FONT: + DRW_curve_batch_cache_create_requested(ob, scene); + break; case OB_SURF: if (mesh_eval) { DRW_mesh_batch_cache_create_requested( @@ -3618,8 +3463,6 @@ void DRW_batch_cache_free_old(Object *ob, int ctime) case OB_MESH: DRW_mesh_batch_cache_free_old((Mesh *)ob->data, ctime); break; - case OB_CURVE: - case OB_FONT: case OB_SURF: if (mesh_eval) { DRW_mesh_batch_cache_free_old(mesh_eval, ctime); diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index c58dd85b6ed..5863ada2ccf 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -22,6 +22,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct GPUBatch; struct GPUMaterial; struct ModifierData; @@ -150,28 +154,14 @@ struct GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_face_wireframe_get(struct Object *ob); /* Curve */ -struct GPUBatch *DRW_cache_curve_surface_get(struct Object *ob); -struct GPUBatch **DRW_cache_curve_surface_shaded_get(struct Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len); -struct GPUBatch *DRW_cache_curve_loose_edges_get(struct Object *ob); struct GPUBatch *DRW_cache_curve_edge_wire_get(struct Object *ob); -struct GPUBatch *DRW_cache_curve_face_wireframe_get(struct Object *ob); -struct GPUBatch *DRW_cache_curve_edge_detection_get(struct Object *ob, bool *r_is_manifold); /* edit-mode */ struct GPUBatch *DRW_cache_curve_edge_normal_get(struct Object *ob); struct GPUBatch *DRW_cache_curve_edge_overlay_get(struct Object *ob); struct GPUBatch *DRW_cache_curve_vert_overlay_get(struct Object *ob); /* Font */ -struct GPUBatch *DRW_cache_text_surface_get(struct Object *ob); -struct GPUBatch *DRW_cache_text_edge_detection_get(struct Object *ob, bool *r_is_manifold); -struct GPUBatch *DRW_cache_text_loose_edges_get(struct Object *ob); struct GPUBatch *DRW_cache_text_edge_wire_get(struct Object *ob); -struct GPUBatch **DRW_cache_text_surface_shaded_get(struct Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len); -struct GPUBatch *DRW_cache_text_face_wireframe_get(struct Object *ob); /* Surface */ struct GPUBatch *DRW_cache_surf_surface_get(struct Object *ob); @@ -263,3 +253,7 @@ struct GPUBatch *DRW_cache_gpencil_face_wireframe_get(struct Object *ob); struct bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(struct Object *ob); void DRW_cache_gpencil_sbuffer_clear(struct Object *ob); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 1efe0c080be..0804745fab5 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -112,43 +112,6 @@ static void curve_render_overlay_verts_edges_len_get(ListBase *lb, } } -static void curve_render_wire_verts_edges_len_get(const CurveCache *ob_curve_cache, - int *r_curve_len, - int *r_vert_len, - int *r_edge_len) -{ - BLI_assert(r_vert_len || r_edge_len); - int vert_len = 0; - int edge_len = 0; - int curve_len = 0; - LISTBASE_FOREACH (const BevList *, bl, &ob_curve_cache->bev) { - if (bl->nr > 0) { - const bool is_cyclic = bl->poly != -1; - edge_len += (is_cyclic) ? bl->nr : bl->nr - 1; - vert_len += bl->nr; - curve_len += 1; - } - } - LISTBASE_FOREACH (const DispList *, dl, &ob_curve_cache->disp) { - if (ELEM(dl->type, DL_SEGM, DL_POLY)) { - BLI_assert(dl->parts == 1); - const bool is_cyclic = dl->type == DL_POLY; - edge_len += (is_cyclic) ? dl->nr : dl->nr - 1; - vert_len += dl->nr; - curve_len += 1; - } - } - if (r_vert_len) { - *r_vert_len = vert_len; - } - if (r_edge_len) { - *r_edge_len = edge_len; - } - if (r_curve_len) { - *r_curve_len = curve_len; - } -} - static void curve_eval_render_wire_verts_edges_len_get(const CurveEval &curve_eval, int *r_curve_len, int *r_vert_len, @@ -243,7 +206,7 @@ enum { }; /* - * ob_curve_cache can be NULL, only needed for CU_DATATYPE_WIRE + * ob_curve_cache can be NULL */ static CurveRenderData *curve_render_data_create(Curve *cu, CurveCache *ob_curve_cache, @@ -267,12 +230,6 @@ static CurveRenderData *curve_render_data_create(Curve *cu, &rdata->wire.vert_len, &rdata->wire.edge_len); } - else { - curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache, - &rdata->wire.curve_len, - &rdata->wire.vert_len, - &rdata->wire.edge_len); - } } if (cu->editnurb) { @@ -594,6 +551,10 @@ void DRW_curve_batch_cache_free(Curve *cu) /* GPUBatch cache usage. */ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos) { + if (rdata->curve_eval == nullptr) { + return; + } + static GPUVertFormat format = {0}; static struct { uint pos; @@ -606,46 +567,26 @@ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curv GPU_vertbuf_init_with_format(vbo_curves_pos, &format); GPU_vertbuf_data_alloc(vbo_curves_pos, vert_len); - if (rdata->curve_eval != nullptr) { - const CurveEval &curve_eval = *rdata->curve_eval; - Span<SplinePtr> splines = curve_eval.splines(); - Array<int> offsets = curve_eval.evaluated_point_offsets(); - BLI_assert(offsets.last() == vert_len); - - for (const int i_spline : splines.index_range()) { - Span<float3> positions = splines[i_spline]->evaluated_positions(); - for (const int i_point : positions.index_range()) { - GPU_vertbuf_attr_set( - vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]); - } - } - } - else { - BLI_assert(rdata->ob_curve_cache != nullptr); - - int v_idx = 0; - LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { - if (bl->nr <= 0) { - continue; - } - const int i_end = v_idx + bl->nr; - for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) { - GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec); - } - } - LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { - if (ELEM(dl->type, DL_SEGM, DL_POLY)) { - for (int i = 0; i < dl->nr; v_idx++, i++) { - GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]); - } - } + const CurveEval &curve_eval = *rdata->curve_eval; + Span<SplinePtr> splines = curve_eval.splines(); + Array<int> offsets = curve_eval.evaluated_point_offsets(); + BLI_assert(offsets.last() == vert_len); + + for (const int i_spline : splines.index_range()) { + Span<float3> positions = splines[i_spline]->evaluated_positions(); + for (const int i_point : positions.index_range()) { + GPU_vertbuf_attr_set( + vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]); } - BLI_assert(v_idx == vert_len); } } static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines) { + if (rdata->curve_eval == nullptr) { + return; + } + const int vert_len = curve_render_data_wire_verts_len_get(rdata); const int edge_len = curve_render_data_wire_edges_len_get(rdata); const int curve_len = curve_render_data_wire_curve_len_get(rdata); @@ -655,54 +596,20 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c GPUIndexBufBuilder elb; GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len); - if (rdata->curve_eval != nullptr) { - const CurveEval &curve_eval = *rdata->curve_eval; - Span<SplinePtr> splines = curve_eval.splines(); - Array<int> offsets = curve_eval.evaluated_point_offsets(); - BLI_assert(offsets.last() == vert_len); - - for (const int i_spline : splines.index_range()) { - const int eval_size = splines[i_spline]->evaluated_points_size(); - if (splines[i_spline]->is_cyclic() && splines[i_spline]->evaluated_edges_size() > 1) { - GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1); - } - for (const int i_point : IndexRange(eval_size)) { - GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + i_point); - } - GPU_indexbuf_add_primitive_restart(&elb); - } - } - else { - BLI_assert(rdata->ob_curve_cache != nullptr); + const CurveEval &curve_eval = *rdata->curve_eval; + Span<SplinePtr> splines = curve_eval.splines(); + Array<int> offsets = curve_eval.evaluated_point_offsets(); + BLI_assert(offsets.last() == vert_len); - int v_idx = 0; - LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { - if (bl->nr <= 0) { - continue; - } - const bool is_cyclic = bl->poly != -1; - if (is_cyclic) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1)); - } - for (int i = 0; i < bl->nr; i++) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + i); - } - GPU_indexbuf_add_primitive_restart(&elb); - v_idx += bl->nr; + for (const int i_spline : splines.index_range()) { + const int eval_size = splines[i_spline]->evaluated_points_size(); + if (splines[i_spline]->is_cyclic() && splines[i_spline]->evaluated_edges_size() > 1) { + GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1); } - LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { - if (ELEM(dl->type, DL_SEGM, DL_POLY)) { - const bool is_cyclic = dl->type == DL_POLY; - if (is_cyclic) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1)); - } - for (int i = 0; i < dl->nr; i++) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + i); - } - GPU_indexbuf_add_primitive_restart(&elb); - v_idx += dl->nr; - } + for (const int i_point : IndexRange(eval_size)) { + GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + i_point); } + GPU_indexbuf_add_primitive_restart(&elb); } GPU_indexbuf_build_in_place(&elb, ibo_curve_lines); diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 6ed4148a7fe..e78e41b917a 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -446,6 +446,8 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr for (int i = 0; i < 2; i++) { iter.verts[iter.vert_len + i].mat = -1; } + /* Also mark first vert as invalid. */ + iter.verts[0].mat = -1; /* Finish the IBO. */ cache->ibo = GPU_indexbuf_build(&iter.ibo); diff --git a/source/blender/draw/intern/draw_color_management.c b/source/blender/draw/intern/draw_color_management.c deleted file mode 100644 index bd851dc4ba7..00000000000 --- a/source/blender/draw/intern/draw_color_management.c +++ /dev/null @@ -1,63 +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. - * - * Copyright 2020, Blender Foundation. - */ - -/** \file - * \ingroup draw - */ - -#include <stdio.h> - -#include "draw_manager.h" - -#include "DRW_render.h" - -#include "GPU_batch.h" -#include "GPU_framebuffer.h" -#include "GPU_matrix.h" -#include "GPU_texture.h" - -#include "BKE_colortools.h" - -#include "IMB_colormanagement.h" - -#include "draw_color_management.h" - -/* -------------------------------------------------------------------- */ -/** \name Color Management - * \{ */ - -/* Draw texture to framebuffer without any color transforms */ -void DRW_transform_none(GPUTexture *tex) -{ - drw_state_set(DRW_STATE_WRITE_COLOR); - - GPU_matrix_identity_set(); - GPU_matrix_identity_projection_set(); - - /* Draw as texture for final render (without immediate mode). */ - GPUBatch *geom = DRW_cache_fullscreen_quad_get(); - GPU_batch_program_set_builtin(geom, GPU_SHADER_2D_IMAGE_COLOR); - GPU_batch_uniform_4f(geom, "color", 1.0f, 1.0f, 1.0f, 1.0f); - GPU_batch_texture_bind(geom, "image", tex); - - GPU_batch_draw(geom); - - GPU_texture_unbind(tex); -} - -/** \} */ diff --git a/source/blender/draw/intern/draw_color_management.cc b/source/blender/draw/intern/draw_color_management.cc new file mode 100644 index 00000000000..23fa18c83c5 --- /dev/null +++ b/source/blender/draw/intern/draw_color_management.cc @@ -0,0 +1,202 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +#include "draw_manager.h" + +#include "DRW_render.h" + +#include "GPU_batch.h" +#include "GPU_framebuffer.h" +#include "GPU_matrix.h" +#include "GPU_texture.h" + +#include "DNA_space_types.h" + +#include "BKE_colortools.h" + +#include "IMB_colormanagement.h" + +#include "draw_color_management.h" + +namespace blender::draw::color_management { + +enum class eDRWColorManagementType { + ViewTransform = 0, + ViewTransformAndLook, + UseRenderSettings, +}; + +static float dither_get(eDRWColorManagementType color_management_type, const Scene &scene) +{ + if (ELEM(color_management_type, + eDRWColorManagementType::ViewTransformAndLook, + eDRWColorManagementType::UseRenderSettings)) { + return scene.r.dither_intensity; + } + return 0.0f; +} + +static eDRWColorManagementType drw_color_management_type_for_v3d(const Scene &scene, + const View3D &v3d) +{ + + const bool use_workbench = BKE_scene_uses_blender_workbench(&scene); + const bool use_scene_lights = ((v3d.shading.type == OB_MATERIAL) && + (v3d.shading.flag & V3D_SHADING_SCENE_LIGHTS)) || + ((v3d.shading.type == OB_RENDER) && + (v3d.shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); + const bool use_scene_world = ((v3d.shading.type == OB_MATERIAL) && + (v3d.shading.flag & V3D_SHADING_SCENE_WORLD)) || + ((v3d.shading.type == OB_RENDER) && + (v3d.shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + + if ((use_workbench && v3d.shading.type == OB_RENDER) || use_scene_lights || use_scene_world) { + return eDRWColorManagementType::UseRenderSettings; + } + if (v3d.shading.type >= OB_MATERIAL) { + return eDRWColorManagementType::ViewTransformAndLook; + } + return eDRWColorManagementType::ViewTransform; +} + +static eDRWColorManagementType drw_color_management_type_for_space_image(const SpaceImage &sima) +{ + Image *image = sima.image; + + /* Use inverse logic as there isn't a setting for `Color And Alpha`. */ + const eSpaceImage_Flag display_channels_mode = static_cast<eSpaceImage_Flag>(sima.flag); + const bool display_color_channel = (display_channels_mode & (SI_SHOW_ALPHA | SI_SHOW_ZBUF)) == 0; + + if (display_color_channel && image && (image->source != IMA_SRC_GENERATED) && + ((image->flag & IMA_VIEW_AS_RENDER) != 0)) { + return eDRWColorManagementType::UseRenderSettings; + } + return eDRWColorManagementType::ViewTransform; +} + +static eDRWColorManagementType drw_color_management_type_for_space_node(const SpaceNode &snode) +{ + const eSpaceNode_Flag display_channels_mode = static_cast<eSpaceNode_Flag>(snode.flag); + const bool display_color_channel = (display_channels_mode & SNODE_SHOW_ALPHA) == 0; + if (display_color_channel) { + return eDRWColorManagementType::UseRenderSettings; + } + return eDRWColorManagementType::ViewTransform; +} + +static eDRWColorManagementType drw_color_management_type_get(const Scene &scene, + const View3D *v3d, + const SpaceLink *space_data) +{ + if (v3d) { + return drw_color_management_type_for_v3d(scene, *v3d); + } + if (space_data) { + switch (space_data->spacetype) { + case SPACE_IMAGE: { + const SpaceImage *sima = static_cast<const SpaceImage *>( + static_cast<const void *>(space_data)); + return drw_color_management_type_for_space_image(*sima); + } + case SPACE_NODE: { + const SpaceNode *snode = static_cast<const SpaceNode *>( + static_cast<const void *>(space_data)); + return drw_color_management_type_for_space_node(*snode); + } + } + } + return eDRWColorManagementType::UseRenderSettings; +} + +static void viewport_settings_apply(GPUViewport &viewport, + const Scene &scene, + const eDRWColorManagementType color_management_type) +{ + const ColorManagedDisplaySettings *display_settings = &scene.display_settings; + ColorManagedViewSettings view_settings; + + switch (color_management_type) { + case eDRWColorManagementType::ViewTransform: { + /* For workbench use only default view transform in configuration, + * using no scene settings. */ + BKE_color_managed_view_settings_init_render(&view_settings, display_settings, nullptr); + break; + } + case eDRWColorManagementType::ViewTransformAndLook: { + /* Use only view transform + look and nothing else for lookdev without + * scene lighting, as exposure depends on scene light intensity. */ + BKE_color_managed_view_settings_init_render(&view_settings, display_settings, nullptr); + STRNCPY(view_settings.view_transform, scene.view_settings.view_transform); + STRNCPY(view_settings.look, scene.view_settings.look); + break; + } + case eDRWColorManagementType::UseRenderSettings: { + /* Use full render settings, for renders with scene lighting. */ + view_settings = scene.view_settings; + break; + } + } + + const float dither = dither_get(color_management_type, scene); + GPU_viewport_colorspace_set(&viewport, &view_settings, display_settings, dither); +} + +static void viewport_color_management_set(GPUViewport &viewport) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + + const eDRWColorManagementType color_management_type = drw_color_management_type_get( + *draw_ctx->scene, draw_ctx->v3d, draw_ctx->space_data); + viewport_settings_apply(viewport, *draw_ctx->scene, color_management_type); +} + +} // namespace blender::draw::color_management + +/* -------------------------------------------------------------------- */ +/** \name Color Management + * \{ */ + +void DRW_viewport_colormanagement_set(GPUViewport *viewport) +{ + blender::draw::color_management::viewport_color_management_set(*viewport); +} + +/* Draw texture to framebuffer without any color transforms */ +void DRW_transform_none(GPUTexture *tex) +{ + drw_state_set(DRW_STATE_WRITE_COLOR); + + GPU_matrix_identity_set(); + GPU_matrix_identity_projection_set(); + + /* Draw as texture for final render (without immediate mode). */ + GPUBatch *geom = DRW_cache_fullscreen_quad_get(); + GPU_batch_program_set_builtin(geom, GPU_SHADER_2D_IMAGE_COLOR); + GPU_batch_uniform_4f(geom, "color", 1.0f, 1.0f, 1.0f, 1.0f); + GPU_batch_texture_bind(geom, "image", tex); + + GPU_batch_draw(geom); + + GPU_texture_unbind(tex); +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_color_management.h b/source/blender/draw/intern/draw_color_management.h index 3150ec72138..9be105b88ec 100644 --- a/source/blender/draw/intern/draw_color_management.h +++ b/source/blender/draw/intern/draw_color_management.h @@ -22,4 +22,15 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + +struct GPUViewport; + void DRW_transform_none(struct GPUTexture *tex); +void DRW_viewport_colormanagement_set(struct GPUViewport *viewport); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 027ab8ce32b..47adc0acc60 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -312,90 +312,6 @@ struct DupliObject *DRW_object_get_dupli(const Object *UNUSED(ob)) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Color Management - * \{ */ - -/* TODO(fclem): This should be a render engine callback to determine if we need CM or not. */ -static void drw_viewport_colormanagement_set(void) -{ - Scene *scene = DST.draw_ctx.scene; - View3D *v3d = DST.draw_ctx.v3d; - - ColorManagedDisplaySettings *display_settings = &scene->display_settings; - ColorManagedViewSettings view_settings; - float dither = 0.0f; - - bool use_render_settings = false; - bool use_view_transform = false; - - if (v3d) { - bool use_workbench = BKE_scene_uses_blender_workbench(scene); - - bool use_scene_lights = (!v3d || - ((v3d->shading.type == OB_MATERIAL) && - (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || - ((v3d->shading.type == OB_RENDER) && - (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER))); - bool use_scene_world = (!v3d || - ((v3d->shading.type == OB_MATERIAL) && - (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) || - ((v3d->shading.type == OB_RENDER) && - (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER))); - use_view_transform = v3d && (v3d->shading.type >= OB_MATERIAL); - use_render_settings = v3d && ((use_workbench && use_view_transform) || use_scene_lights || - use_scene_world); - } - else if (DST.draw_ctx.space_data && DST.draw_ctx.space_data->spacetype == SPACE_IMAGE) { - SpaceImage *sima = (SpaceImage *)DST.draw_ctx.space_data; - Image *image = sima->image; - - /* Use inverse logic as there isn't a setting for `Color And Alpha`. */ - const eSpaceImage_Flag display_channels_mode = sima->flag; - const bool display_color_channel = (display_channels_mode & (SI_SHOW_ALPHA | SI_SHOW_ZBUF)) == - 0; - if (display_color_channel && image && (image->source != IMA_SRC_GENERATED) && - ((image->flag & IMA_VIEW_AS_RENDER) != 0)) { - use_render_settings = true; - } - } - else if (DST.draw_ctx.space_data && DST.draw_ctx.space_data->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)DST.draw_ctx.space_data; - const eSpaceNode_Flag display_channels_mode = snode->flag; - const bool display_color_channel = (display_channels_mode & SNODE_SHOW_ALPHA) == 0; - if (display_color_channel) { - use_render_settings = true; - } - } - else { - use_render_settings = true; - use_view_transform = false; - } - - if (use_render_settings) { - /* Use full render settings, for renders with scene lighting. */ - view_settings = scene->view_settings; - dither = scene->r.dither_intensity; - } - else if (use_view_transform) { - /* Use only view transform + look and nothing else for lookdev without - * scene lighting, as exposure depends on scene light intensity. */ - BKE_color_managed_view_settings_init_render(&view_settings, display_settings, NULL); - STRNCPY(view_settings.view_transform, scene->view_settings.view_transform); - STRNCPY(view_settings.look, scene->view_settings.look); - dither = scene->r.dither_intensity; - } - else { - /* For workbench use only default view transform in configuration, - * using no scene settings. */ - BKE_color_managed_view_settings_init_render(&view_settings, display_settings, NULL); - } - - GPU_viewport_colorspace_set(DST.viewport, &view_settings, display_settings, dither); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Viewport (DRW_viewport) * \{ */ @@ -716,14 +632,29 @@ void DRW_viewport_request_redraw(void) /** \name Duplis * \{ */ -static void drw_duplidata_load(DupliObject *dupli) +static uint dupli_key_hash(const void *key) { + const DupliKey *dupli_key = (const DupliKey *)key; + return BLI_ghashutil_ptrhash(dupli_key->ob) ^ BLI_ghashutil_ptrhash(dupli_key->ob_data); +} + +static bool dupli_key_cmp(const void *key1, const void *key2) +{ + const DupliKey *dupli_key1 = (const DupliKey *)key1; + const DupliKey *dupli_key2 = (const DupliKey *)key2; + return dupli_key1->ob != dupli_key2->ob || dupli_key1->ob_data != dupli_key2->ob_data; +} + +static void drw_duplidata_load(Object *ob) +{ + DupliObject *dupli = DST.dupli_source; if (dupli == NULL) { return; } - if (DST.dupli_origin != dupli->ob) { + if (DST.dupli_origin != dupli->ob || (DST.dupli_origin_data != dupli->ob_data)) { DST.dupli_origin = dupli->ob; + DST.dupli_origin_data = dupli->ob_data; } else { /* Same data as previous iter. No need to poll ghash for this. */ @@ -731,16 +662,23 @@ static void drw_duplidata_load(DupliObject *dupli) } if (DST.dupli_ghash == NULL) { - DST.dupli_ghash = BLI_ghash_ptr_new(__func__); + DST.dupli_ghash = BLI_ghash_new(dupli_key_hash, dupli_key_cmp, __func__); } + DupliKey *key = MEM_callocN(sizeof(DupliKey), __func__); + key->ob = dupli->ob; + key->ob_data = dupli->ob_data; + void **value; - if (!BLI_ghash_ensure_p(DST.dupli_ghash, DST.dupli_origin, &value)) { + if (!BLI_ghash_ensure_p(DST.dupli_ghash, key, &value)) { *value = MEM_callocN(sizeof(void *) * DST.enabled_engine_count, __func__); /* TODO: Meh a bit out of place but this is nice as it is - * only done once per "original" object. */ - drw_batch_cache_validate(DST.dupli_origin); + * only done once per instance type. */ + drw_batch_cache_validate(ob); + } + else { + MEM_freeN(key); } DST.dupli_datas = *(void ***)value; } @@ -754,12 +692,24 @@ static void duplidata_value_free(void *val) MEM_freeN(val); } +static void duplidata_key_free(void *key) +{ + DupliKey *dupli_key = (DupliKey *)key; + if (dupli_key->ob_data == dupli_key->ob->data) { + drw_batch_cache_generate_requested(dupli_key->ob); + } + else { + Object temp_object = *dupli_key->ob; + BKE_object_replace_data_on_shallow_copy(&temp_object, dupli_key->ob_data); + drw_batch_cache_generate_requested(&temp_object); + } + MEM_freeN(key); +} + static void drw_duplidata_free(void) { if (DST.dupli_ghash != NULL) { - BLI_ghash_free(DST.dupli_ghash, - (void (*)(void *key))drw_batch_cache_generate_requested, - duplidata_value_free); + BLI_ghash_free(DST.dupli_ghash, duplidata_key_free, duplidata_value_free); DST.dupli_ghash = NULL; } } @@ -1524,7 +1474,6 @@ void DRW_draw_view(const bContext *C) (v3d->overlay.flag & V3D_OVERLAY_HIDE_TEXT) != 0); DST.options.draw_background = (scene->r.alphamode == R_ADDSKY) || (v3d->shading.type != OB_RENDER); - DST.options.do_color_management = true; DRW_draw_render_loop_ex(depsgraph, engine_type, region, v3d, viewport, C); } else { @@ -1572,7 +1521,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, drw_context_state_init(); drw_viewport_var_init(); - drw_viewport_colormanagement_set(); + DRW_viewport_colormanagement_set(DST.viewport); const int object_type_exclude_viewport = v3d->object_type_exclude_viewport; /* Check if scene needs to perform the populate loop */ @@ -1608,6 +1557,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, /* Only iterate over objects for internal engines or when overlays are enabled */ if (do_populate_loop) { DST.dupli_origin = NULL; + DST.dupli_origin_data = NULL; DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) { if ((object_type_exclude_viewport & (1 << ob->type)) != 0) { continue; @@ -1617,7 +1567,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, } DST.dupli_parent = data_.dupli_parent; DST.dupli_source = data_.dupli_object_current; - drw_duplidata_load(DST.dupli_source); + drw_duplidata_load(ob); drw_engines_cache_populate(ob); } DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; @@ -1716,7 +1666,6 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); DST.options.is_image_render = is_image_render; - DST.options.do_color_management = do_color_management; DST.options.draw_background = draw_background; DRW_draw_render_loop_ex(depsgraph, engine_type, region, v3d, render_viewport, NULL); @@ -1967,12 +1916,13 @@ void DRW_render_object_iter( draw_ctx->v3d->object_type_exclude_viewport : 0; DST.dupli_origin = NULL; + DST.dupli_origin_data = NULL; DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) { if ((object_type_exclude_viewport & (1 << ob->type)) == 0) { DST.dupli_parent = data_.dupli_parent; DST.dupli_source = data_.dupli_object_current; DST.ob_handle = 0; - drw_duplidata_load(DST.dupli_source); + drw_duplidata_load(ob); if (!DST.dupli_source) { drw_batch_cache_validate(ob); @@ -2093,7 +2043,7 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, drw_context_state_init(); drw_viewport_var_init(); - drw_viewport_colormanagement_set(); + DRW_viewport_colormanagement_set(DST.viewport); /* TODO(jbakker): Only populate when editor needs to draw object. * for the image editor this is when showing UV's. */ @@ -2282,6 +2232,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, bool use_obedit_skip, bool draw_surface, bool UNUSED(use_nearest), + const bool do_material_sub_selection, const rcti *rect, DRW_SelectPassFn select_pass_fn, void *select_pass_user_data, @@ -2349,6 +2300,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DST.viewport = viewport; DST.options.is_select = true; + DST.options.is_material_select = do_material_sub_selection; drw_task_graph_init(); /* Get list of enabled engines */ if (use_obedit) { @@ -2416,6 +2368,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, v3d->object_type_exclude_select); bool filter_exclude = false; DST.dupli_origin = NULL; + DST.dupli_origin_data = NULL; DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) { if (!BKE_object_is_visible_in_viewport(v3d, ob)) { continue; @@ -2448,7 +2401,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DRW_select_load_id(ob->runtime.select_id); DST.dupli_parent = data_.dupli_parent; DST.dupli_source = data_.dupli_object_current; - drw_duplidata_load(DST.dupli_source); + drw_duplidata_load(ob); drw_engines_cache_populate(ob); } } @@ -2561,6 +2514,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, const int object_type_exclude_viewport = v3d->object_type_exclude_viewport; DST.dupli_origin = NULL; + DST.dupli_origin_data = NULL; DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (DST.draw_ctx.depsgraph, ob) { if ((object_type_exclude_viewport & (1 << ob->type)) != 0) { continue; @@ -2570,7 +2524,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, } DST.dupli_parent = data_.dupli_parent; DST.dupli_source = data_.dupli_object_current; - drw_duplidata_load(DST.dupli_source); + drw_duplidata_load(ob); drw_engines_cache_populate(ob); } DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; @@ -2824,6 +2778,11 @@ bool DRW_state_is_select(void) return DST.options.is_select; } +bool DRW_state_is_material_select(void) +{ + return DST.options.is_material_select; +} + bool DRW_state_is_depth(void) { return DST.options.is_depth; @@ -2838,14 +2797,6 @@ bool DRW_state_is_image_render(void) } /** - * Whether the view transform should be applied. - */ -bool DRW_state_do_color_management(void) -{ - return DST.options.do_color_management; -} - -/** * Whether we are rendering only the render engine, * or if we should also render the mode engines. */ diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 512c2775850..c09126c98ef 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -43,6 +43,10 @@ #include "draw_instance_data.h" +#ifdef __cplusplus +extern "C" { +#endif + struct DupliObject; struct Object; @@ -493,6 +497,11 @@ typedef struct DRWDebugSphere { /* ------------- DRAW MANAGER ------------ */ +typedef struct DupliKey { + struct Object *ob; + struct ID *ob_data; +} DupliKey; + #define DST_MAX_SLOTS 64 /* Cannot be changed without modifying RST.bound_tex_slots */ #define MAX_CLIP_PLANES 6 /* GL_MAX_CLIP_PLANES is at least 6 */ #define STENCIL_UNDEFINED 256 @@ -511,15 +520,19 @@ typedef struct DRWManager { /** Handle of next DRWPass to be allocated. */ DRWResourceHandle pass_handle; - /** Dupli state. NULL if not dupli. */ + /** Dupli object that corresponds to the current object. */ struct DupliObject *dupli_source; + /** Object that created the dupli-list the current object is part of. */ struct Object *dupli_parent; + /** Object referenced by the current dupli object. */ struct Object *dupli_origin; - /** Ghash containing original objects. */ + /** Object-data referenced by the current dupli object. */ + struct ID *dupli_origin_data; + /** Ghash: #DupliKey -> void pointer for each enabled engine. */ struct GHash *dupli_ghash; /** TODO(fclem): try to remove usage of this. */ DRWInstanceData *object_instance_data[MAX_INSTANCE_DATA_SIZE]; - /* Array of dupli_data (one for each enabled engine) to handle duplis. */ + /* Dupli data for the current dupli for each enabled engine. */ void **dupli_datas; /* Rendering state */ @@ -540,10 +553,10 @@ typedef struct DRWManager { struct { uint is_select : 1; + uint is_material_select : 1; uint is_depth : 1; uint is_image_render : 1; uint is_scene_render : 1; - uint do_color_management : 1; uint draw_background : 1; uint draw_text : 1; } options; @@ -627,3 +640,7 @@ void drw_uniform_attrs_pool_update(struct GHash *table, struct Object *ob, struct Object *dupli_parent, struct DupliObject *dupli_source); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index 42fdb714127..97679723d84 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -206,21 +206,17 @@ static void animchan_sync_fcurve_scene(bAnimListElem *ale) BLI_assert(GS(owner_id->name) == ID_SCE); Scene *scene = (Scene *)owner_id; FCurve *fcu = (FCurve *)ale->data; + Sequence *seq = NULL; - /* only affect if F-Curve involves sequence_editor.sequences */ - if (!strstr(fcu->rna_path, "sequences_all")) { + /* Only affect if F-Curve involves sequence_editor.sequences. */ + char seq_name[sizeof(seq->name)]; + if (!BLI_str_quoted_substr(fcu->rna_path, "sequences_all[", seq_name, sizeof(seq_name))) { return; } - Editing *ed = SEQ_editing_get(scene, false); - - /* get strip name, and check if this strip is selected */ - char *seq_name = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all["); - if (seq_name == NULL) { - return; - } - - Sequence *seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); + /* Check if this strip is selected. */ + Editing *ed = SEQ_editing_get(scene); + seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); MEM_freeN(seq_name); if (seq == NULL) { diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 6d272bfc180..993d10cf303 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -521,6 +521,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ MaskLayer *masklay = BKE_mask_layer_active(mask); mask_to_keylist(&ads, masklay, keylist); } + ED_keylist_prepare_for_direct_access(keylist); /* TODO(jbakker): Keylists are ordered, no need to do any searching at all. */ /* find matching keyframe in the right direction */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 020518b5813..b12e0ae5cab 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1061,20 +1061,17 @@ static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id if (GS(owner_id->name) == ID_OB) { Object *ob = (Object *)owner_id; + bPoseChannel *pchan = NULL; + char bone_name[sizeof(pchan->name)]; - /* only consider if F-Curve involves pose.bones */ - if ((fcu->rna_path) && strstr(fcu->rna_path, "pose.bones")) { - - /* get bone-name, and check if this bone is selected */ - bPoseChannel *pchan = NULL; - char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (bone_name) { - pchan = BKE_pose_channel_find_name(ob->pose, bone_name); - MEM_freeN(bone_name); - } + /* Only consider if F-Curve involves `pose.bones`. */ + if (fcu->rna_path && + BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { + /* Get bone-name, and check if this bone is selected. */ + pchan = BKE_pose_channel_find_name(ob->pose, bone_name); /* check whether to continue or skip */ - if ((pchan) && (pchan->bone)) { + if (pchan && pchan->bone) { /* If only visible channels, * skip if bone not visible unless user wants channels from hidden data too. */ if (skip_hidden) { @@ -1101,24 +1098,41 @@ static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id } else if (GS(owner_id->name) == ID_SCE) { Scene *scene = (Scene *)owner_id; - - /* only consider if F-Curve involves sequence_editor.sequences */ - if ((fcu->rna_path) && strstr(fcu->rna_path, "sequences_all")) { - Editing *ed = SEQ_editing_get(scene, false); - Sequence *seq = NULL; - + Sequence *seq = NULL; + char seq_name[sizeof(seq->name)]; + + /* Only consider if F-Curve involves `sequence_editor.sequences`. */ + if (fcu->rna_path && + BLI_str_quoted_substr(fcu->rna_path, "sequences_all[", seq_name, sizeof(seq_name))) { + /* Get strip name, and check if this strip is selected. */ + Editing *ed = SEQ_editing_get(scene); if (ed) { - /* get strip name, and check if this strip is selected */ - char *seq_name = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all["); - if (seq_name) { - seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); - MEM_freeN(seq_name); - } + seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); } - /* can only add this F-Curve if it is selected */ + /* Can only add this F-Curve if it is selected. */ if (ads->filterflag & ADS_FILTER_ONLYSEL) { - if ((seq == NULL) || (seq->flag & SELECT) == 0) { + + /* NOTE(@campbellbarton): The `seq == NULL` check doesn't look right + * (compared to other checks in this function which skip data that can't be found). + * + * This is done since the search for sequence strips doesn't use a global lookup: + * - Nested meta-strips are excluded. + * - When inside a meta-strip - strips outside the meta-strip excluded. + * + * Instead, only the strips directly visible to the user are considered for selection. + * The NULL check here means everything else is considered unselected and is not shown. + * + * There is a subtle difference between nodes, pose-bones ... etc + * since data-paths that point to missing strips are not shown. + * If this is an important difference, the NULL case could perform a global lookup, + * only returning `true` if the sequence strip exists elsewhere + * (ignoring it's selection state). */ + if (seq == NULL) { + return true; + } + + if ((seq->flag & SELECT) == 0) { return true; } } @@ -1126,22 +1140,21 @@ static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id } else if (GS(owner_id->name) == ID_NT) { bNodeTree *ntree = (bNodeTree *)owner_id; + bNode *node = NULL; + char node_name[sizeof(node->name)]; - /* check for selected nodes */ - if ((fcu->rna_path) && strstr(fcu->rna_path, "nodes")) { - bNode *node = NULL; - - /* get strip name, and check if this strip is selected */ - char *node_name = BLI_str_quoted_substrN(fcu->rna_path, "nodes["); - if (node_name) { - node = nodeFindNodebyName(ntree, node_name); - MEM_freeN(node_name); - } + /* Check for selected nodes. */ + if (fcu->rna_path && + (BLI_str_quoted_substr(fcu->rna_path, "nodes[", node_name, sizeof(node_name)))) { + /* Get strip name, and check if this strip is selected. */ + node = nodeFindNodebyName(ntree, node_name); - /* can only add this F-Curve if it is selected */ - if (ads->filterflag & ADS_FILTER_ONLYSEL) { - if ((node) && (node->flag & NODE_SELECT) == 0) { - return true; + /* Can only add this F-Curve if it is selected. */ + if (node) { + if (ads->filterflag & ADS_FILTER_ONLYSEL) { + if ((node->flag & NODE_SELECT) == 0) { + return true; + } } } } diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index eda87cf1897..33b4882927a 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -106,23 +106,14 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) * - If a pointer just refers to the ID-block, then don't repeat this info * since this just introduces clutter. */ - if (strstr(fcu->rna_path, "bones") && strstr(fcu->rna_path, "constraints")) { - /* perform string 'chopping' to get "Bone Name : Constraint Name" */ - char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones["); - char *constName = BLI_str_quoted_substrN(fcu->rna_path, "constraints["); + + char pchanName[256], constName[256]; + if (BLI_str_quoted_substr(fcu->rna_path, "bones[", pchanName, sizeof(pchanName)) && + BLI_str_quoted_substr(fcu->rna_path, "constraints[", constName, sizeof(constName))) { /* assemble the string to display in the UI... */ - structname = BLI_sprintfN( - "%s : %s", pchanName ? pchanName : "", constName ? constName : ""); + structname = BLI_sprintfN("%s : %s", pchanName, constName); free_structname = 1; - - /* free the temp names */ - if (pchanName) { - MEM_freeN(pchanName); - } - if (constName) { - MEM_freeN(constName); - } } else if (ptr.data != ptr.owner_id) { PropertyRNA *nameprop = RNA_struct_name_property(ptr.type); @@ -139,18 +130,15 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) * displaying the struct name alone is no meaningful information (and also cannot be * filtered well), same for modifiers. So display strip name alongside as well. */ if (GS(ptr.owner_id->name) == ID_SCE) { - if (BLI_str_startswith(fcu->rna_path, "sequence_editor.sequences_all[\"")) { + char stripname[256]; + if (BLI_str_quoted_substr( + fcu->rna_path, "sequence_editor.sequences_all[", stripname, sizeof(stripname))) { if (strstr(fcu->rna_path, ".transform.") || strstr(fcu->rna_path, ".crop.") || strstr(fcu->rna_path, ".modifiers[")) { - char *stripname = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all["); - const char *structname_all = BLI_sprintfN( - "%s : %s", stripname ? stripname : "", structname); + const char *structname_all = BLI_sprintfN("%s : %s", stripname, structname); if (free_structname) { MEM_freeN((void *)structname); } - if (stripname) { - MEM_freeN(stripname); - } structname = structname_all; free_structname = 1; } diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index d976f5f72ad..d130941b9bc 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -329,6 +329,7 @@ static void motionpath_calculate_update_range(MPathTarget *mpt, for (FCurve *fcu = fcurve_list->first; fcu != NULL; fcu = fcu->next) { struct AnimKeylist *keylist = ED_keylist_create(); fcurve_to_keylist(adt, fcu, keylist, 0); + ED_keylist_prepare_for_direct_access(keylist); int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, current_frame); int fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, current_frame); @@ -443,6 +444,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, action_to_keylist(adt, adt->action, mpt->keylist, 0); } } + ED_keylist_prepare_for_direct_access(mpt->keylist); if (range == ANIMVIZ_CALC_RANGE_CHANGED) { int mpt_sfra, mpt_efra; diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 6f3277397c5..450d7cd100e 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -105,7 +105,7 @@ static void seq_frame_snap_update_best(const int position, static int seq_frame_apply_snap(bContext *C, Scene *scene, const int timeline_frame) { - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene, false)); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); SeqCollection *strips = SEQ_query_all_strips(seqbase); int best_frame = 0; diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 61918871b90..ac7db9f4f46 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -30,6 +30,7 @@ #include "BLI_dlrbTree.h" #include "BLI_listbase.h" #include "BLI_rect.h" +#include "BLI_task.h" #include "DNA_anim_types.h" #include "DNA_gpencil_types.h" @@ -346,10 +347,12 @@ static void draw_keylist_block(const DrawKeylistUIData *ctx, const ActKeyColumn } static void draw_keylist_blocks(const DrawKeylistUIData *ctx, - const ListBase * /*ActKeyColumn*/ columns, + const ActKeyColumn *keys, + const int key_len, float ypos) { - LISTBASE_FOREACH (ActKeyColumn *, ab, columns) { + for (int i = 0; i < key_len; i++) { + const ActKeyColumn *ab = &keys[i]; draw_keylist_block(ctx, ab, ypos); } } @@ -362,13 +365,15 @@ static bool draw_keylist_is_visible_key(const View2D *v2d, const ActKeyColumn *a static void draw_keylist_keys(const DrawKeylistUIData *ctx, View2D *v2d, const KeyframeShaderBindings *sh_bindings, - const ListBase * /*ActKeyColumn*/ keys, + const ActKeyColumn *keys, + const int key_len, float ypos, eSAction_Flag saction_flag) { short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE; - LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { + for (int i = 0; i < key_len; i++) { + const ActKeyColumn *ak = &keys[i]; if (draw_keylist_is_visible_key(v2d, ak)) { if (ctx->show_ipo) { handle_type = ak->handle_type; @@ -469,8 +474,9 @@ static void ED_keylist_draw_list_elem_draw_blocks(AnimKeylistDrawListElem *elem, DrawKeylistUIData ctx; draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag); - const ListBase *keys = ED_keylist_listbase(elem->keylist); - draw_keylist_blocks(&ctx, keys, elem->ypos); + const int key_len = ED_keylist_array_len(elem->keylist); + const ActKeyColumn *keys = ED_keylist_array(elem->keylist); + draw_keylist_blocks(&ctx, keys, key_len, elem->ypos); } static void ED_keylist_draw_list_elem_draw_keys(AnimKeylistDrawListElem *elem, @@ -479,8 +485,15 @@ static void ED_keylist_draw_list_elem_draw_keys(AnimKeylistDrawListElem *elem, { DrawKeylistUIData ctx; draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag); - const ListBase *keys = ED_keylist_listbase(elem->keylist); - draw_keylist_keys(&ctx, v2d, sh_bindings, keys, elem->ypos, elem->saction_flag); + + const int key_len = ED_keylist_array_len(elem->keylist); + const ActKeyColumn *keys = ED_keylist_array(elem->keylist); + draw_keylist_keys(&ctx, v2d, sh_bindings, keys, key_len, elem->ypos, elem->saction_flag); +} + +static void ED_keylist_draw_list_elem_prepare_for_drawing(AnimKeylistDrawListElem *elem) +{ + ED_keylist_prepare_for_direct_access(elem->keylist); } typedef struct AnimKeylistDrawList { @@ -492,11 +505,25 @@ AnimKeylistDrawList *ED_keylist_draw_list_create(void) return MEM_callocN(sizeof(AnimKeylistDrawList), __func__); } +static void ED_keylist_draw_list_elem_build_task(void *__restrict UNUSED(userdata), + void *item, + int UNUSED(index), + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + AnimKeylistDrawListElem *elem = item; + ED_keylist_draw_list_elem_build_keylist(elem); + ED_keylist_draw_list_elem_prepare_for_drawing(elem); +} + static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) { - LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { - ED_keylist_draw_list_elem_build_keylist(elem); - } + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + /* Create a task per item, a single item is complex enough to deserve its own task. */ + settings.min_iter_per_thread = 1; + + BLI_task_parallel_listbase( + &draw_list->channels, NULL, ED_keylist_draw_list_elem_build_task, &settings); } static void ED_keylist_draw_list_draw_blocks(AnimKeylistDrawList *draw_list, View2D *v2d) diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 75ce62d5e27..ec33a42af3b 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -761,11 +761,10 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) if ((aci->id_type == ID_OB) && (((Object *)aci->id)->type == OB_ARMATURE) && aci->rna_path) { Object *ob = (Object *)aci->id; - char *bone_name = BLI_str_quoted_substrN(aci->rna_path, "pose.bones["); - if (bone_name) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name); - MEM_freeN(bone_name); - + bPoseChannel *pchan; + char bone_name[sizeof(pchan->name)]; + if (BLI_str_quoted_substr(aci->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { + pchan = BKE_pose_channel_find_name(ob->pose, bone_name); if (pchan) { aci->is_bone = true; } @@ -824,31 +823,35 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) return 0; } -static void flip_names(tAnimCopybufItem *aci, char **name) +static void flip_names(tAnimCopybufItem *aci, char **r_name) { if (aci->is_bone) { - char *str_start; - if ((str_start = strstr(aci->rna_path, "pose.bones["))) { - /* ninja coding, try to change the name */ + int ofs_start; + int ofs_end; + + if (BLI_str_quoted_substr_range(aci->rna_path, "pose.bones[", &ofs_start, &ofs_end)) { + char *str_start = aci->rna_path + ofs_start; + const char *str_end = aci->rna_path + ofs_end; + + /* Swap out the name. + * Note that there is no need to un-escape the string to flip it. */ char bname_new[MAX_VGROUP_NAME]; - char *str_iter, *str_end; + char *str_iter; int length, prefix_l, postfix_l; - str_start += 12; prefix_l = str_start - aci->rna_path; - str_end = strchr(str_start, '\"'); - length = str_end - str_start; postfix_l = strlen(str_end); - /* more ninja stuff, temporary substitute with NULL terminator */ + /* Temporary substitute with NULL terminator. */ + BLI_assert(str_start[length] == '\"'); str_start[length] = 0; BLI_string_flip_side_name(bname_new, str_start, false, sizeof(bname_new)); str_start[length] = '\"'; - str_iter = *name = MEM_mallocN(sizeof(char) * (prefix_l + postfix_l + length + 1), - "flipped_path"); + str_iter = *r_name = MEM_mallocN(sizeof(char) * (prefix_l + postfix_l + length + 1), + "flipped_path"); BLI_strncpy(str_iter, aci->rna_path, prefix_l + 1); str_iter += prefix_l; diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index f6ade11a517..c1a18196a3a 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -23,15 +23,20 @@ /* System includes ----------------------------------------------------- */ +#include <algorithm> #include <cfloat> #include <cmath> #include <cstdlib> #include <cstring> +#include <functional> +#include <optional> #include "MEM_guardedalloc.h" +#include "BLI_array.hh" #include "BLI_dlrbTree.h" #include "BLI_listbase.h" +#include "BLI_math.h" #include "BLI_range.h" #include "BLI_utildefines.h" @@ -50,117 +55,294 @@ extern "C" { /* *************************** Keyframe Processing *************************** */ -struct AnimKeylist { - DLRBT_Tree keys; -}; +/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ + +BLI_INLINE bool is_cfra_eq(const float a, const float b) +{ + return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); +} -static void ED_keylist_init(AnimKeylist *keylist) +BLI_INLINE bool is_cfra_lt(const float a, const float b) { - BLI_dlrbTree_init(&keylist->keys); + return (b - a) > BEZT_BINARYSEARCH_THRESH; } +/* --------------- */ + +struct AnimKeylist { + /* Number of ActKeyColumn's in the keylist. */ + size_t column_len = 0; + + bool is_runtime_initialized = false; + + /* Before initializing the runtime, the key_columns list base is used to quickly add columns. + * Contains `ActKeyColumn`. Should not be used after runtime is initialized. */ + ListBase /* ActKeyColumn */ key_columns; + /* Last accessed column in the key_columns list base. Inserting columns are typically done in + * order. The last accessed column is used as starting point to search for a location to add or + * update the next column.*/ + std::optional<ActKeyColumn *> last_accessed_column = std::nullopt; + + struct { + /* When initializing the runtime the columns from the list base `AnimKeyList.key_columns` are + * transferred to an array to support binary searching and index based access. */ + blender::Array<ActKeyColumn> key_columns; + /* Wrapper around runtime.key_columns so it can still be accessed as a ListBase. Elements are + * owned by runtime.key_columns. */ + ListBase /* ActKeyColumn */ list_wrapper; + } runtime; + + AnimKeylist() + { + BLI_listbase_clear(&this->key_columns); + BLI_listbase_clear(&this->runtime.list_wrapper); + } + + ~AnimKeylist() + { + BLI_freelistN(&this->key_columns); + BLI_listbase_clear(&this->runtime.list_wrapper); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("editors:AnimKeylist") +#endif +}; + AnimKeylist *ED_keylist_create(void) { - AnimKeylist *keylist = static_cast<AnimKeylist *>(MEM_callocN(sizeof(AnimKeylist), __func__)); - ED_keylist_init(keylist); + AnimKeylist *keylist = new AnimKeylist(); return keylist; } void ED_keylist_free(AnimKeylist *keylist) { BLI_assert(keylist); - BLI_dlrbTree_free(&keylist->keys); - MEM_freeN(keylist); + delete keylist; } -const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra) +static void ED_keylist_convert_key_columns_to_array(AnimKeylist *keylist) { - return (const ActKeyColumn *)BLI_dlrbTree_search_exact( - &keylist->keys, compare_ak_cfraPtr, &cfra); + size_t index; + LISTBASE_FOREACH_INDEX (ActKeyColumn *, key, &keylist->key_columns, index) { + keylist->runtime.key_columns[index] = *key; + } } -const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra) +static void ED_keylist_runtime_update_key_column_next_prev(AnimKeylist *keylist) { - return (const ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra); + for (size_t index = 0; index < keylist->column_len; index++) { + const bool is_first = (index == 0); + keylist->runtime.key_columns[index].prev = is_first ? nullptr : + &keylist->runtime.key_columns[index - 1]; + const bool is_last = (index == keylist->column_len - 1); + keylist->runtime.key_columns[index].next = is_last ? nullptr : + &keylist->runtime.key_columns[index + 1]; + } } -const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) +static void ED_keylist_runtime_init_listbase(AnimKeylist *keylist) { - return (const ActKeyColumn *)BLI_dlrbTree_search_prev(&keylist->keys, compare_ak_cfraPtr, &cfra); + if (ED_keylist_is_empty(keylist)) { + BLI_listbase_clear(&keylist->runtime.list_wrapper); + return; + } + + keylist->runtime.list_wrapper.first = &keylist->runtime.key_columns[0]; + keylist->runtime.list_wrapper.last = &keylist->runtime.key_columns[keylist->column_len - 1]; } -/* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check - * boundary of `max_fra`. */ -const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, - const Range2f frame_range) +static void ED_keylist_runtime_init(AnimKeylist *keylist) { - for (const ActKeyColumn *ak = static_cast<const ActKeyColumn *>(keylist->keys.root); ak; - ak = static_cast<const ActKeyColumn *>((ak->cfra < frame_range.min) ? ak->right : - ak->left)) { - if (range2f_in_range(&frame_range, ak->cfra)) { - return ak; - } + BLI_assert(!keylist->is_runtime_initialized); + + keylist->runtime.key_columns = blender::Array<ActKeyColumn>(keylist->column_len); + + /* Convert linked list to array to support fast searching. */ + ED_keylist_convert_key_columns_to_array(keylist); + /* Ensure that the array can also be used as a listbase for external usages. */ + ED_keylist_runtime_update_key_column_next_prev(keylist); + ED_keylist_runtime_init_listbase(keylist); + + keylist->is_runtime_initialized = true; +} + +static void ED_keylist_reset_last_accessed(AnimKeylist *keylist) +{ + BLI_assert(!keylist->is_runtime_initialized); + keylist->last_accessed_column.reset(); +} + +void ED_keylist_prepare_for_direct_access(AnimKeylist *keylist) +{ + if (keylist->is_runtime_initialized) { + return; + } + ED_keylist_runtime_init(keylist); +} + +static const ActKeyColumn *ED_keylist_find_lower_bound(const AnimKeylist *keylist, + const float cfra) +{ + BLI_assert(!ED_keylist_is_empty(keylist)); + const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns); + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + ActKeyColumn value; + value.cfra = cfra; + + const ActKeyColumn *found_column = std::lower_bound( + begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) { + return is_cfra_lt(column.cfra, other.cfra); + }); + return found_column; +} + +static const ActKeyColumn *ED_keylist_find_upper_bound(const AnimKeylist *keylist, + const float cfra) +{ + BLI_assert(!ED_keylist_is_empty(keylist)); + const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns); + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + ActKeyColumn value; + value.cfra = cfra; + + const ActKeyColumn *found_column = std::upper_bound( + begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) { + return is_cfra_lt(column.cfra, other.cfra); + }); + return found_column; +} + +const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra) +{ + BLI_assert_msg(keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before searching."); + + if (ED_keylist_is_empty(keylist)) { + return nullptr; + } + + const ActKeyColumn *found_column = ED_keylist_find_lower_bound(keylist, cfra); + + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + if (found_column == end) { + return nullptr; + } + if (is_cfra_eq(found_column->cfra, cfra)) { + return found_column; } return nullptr; } -bool ED_keylist_is_empty(const struct AnimKeylist *keylist) +const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, const float cfra) { - return keylist->keys.root == nullptr; + BLI_assert_msg(keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before searching."); + + if (ED_keylist_is_empty(keylist)) { + return nullptr; + } + + const ActKeyColumn *found_column = ED_keylist_find_upper_bound(keylist, cfra); + + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + if (found_column == end) { + return nullptr; + } + return found_column; } -const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) +const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra) { - return (ListBase *)&keylist->keys; + BLI_assert_msg(keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before searching."); + + if (ED_keylist_is_empty(keylist)) { + return nullptr; + } + + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + const ActKeyColumn *found_column = ED_keylist_find_lower_bound(keylist, cfra); + + if (found_column == end) { + /* Nothing found, return the last item. */ + return end - 1; + } + + const ActKeyColumn *prev_column = found_column->prev; + return prev_column; } -bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) +const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, + const Range2f frame_range) { - BLI_assert(r_frame_range); + BLI_assert_msg(keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before searching."); if (ED_keylist_is_empty(keylist)) { - return false; + return nullptr; } - const ActKeyColumn *first_column = (const ActKeyColumn *)keylist->keys.first; - r_frame_range->min = first_column->cfra; + const ActKeyColumn *column = ED_keylist_find_lower_bound(keylist, frame_range.min); + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + if (column == end) { + return nullptr; + } + if (column->cfra >= frame_range.max) { + return nullptr; + } + return column; +} - const ActKeyColumn *last_column = (const ActKeyColumn *)keylist->keys.last; - r_frame_range->max = last_column->cfra; +const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist) +{ + BLI_assert_msg( + keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before accessing array."); + return keylist->runtime.key_columns.data(); +} - return true; +int64_t ED_keylist_array_len(const struct AnimKeylist *keylist) +{ + return keylist->column_len; } -/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ -BLI_INLINE bool is_cfra_eq(const float a, const float b) +bool ED_keylist_is_empty(const struct AnimKeylist *keylist) { - return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); + return keylist->column_len == 0; } -BLI_INLINE bool is_cfra_lt(const float a, const float b) +const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) { - return (b - a) > BEZT_BINARYSEARCH_THRESH; + if (keylist->is_runtime_initialized) { + return &keylist->runtime.list_wrapper; + } + return &keylist->key_columns; } -/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ -/* NOTE: this is exported to other modules that use the ActKeyColumns for finding keyframes */ -short compare_ak_cfraPtr(void *node, void *data) +bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) { - ActKeyColumn *ak = (ActKeyColumn *)node; - const float *cframe = static_cast<const float *>(data); - const float val = *cframe; + BLI_assert(r_frame_range); - if (is_cfra_eq(val, ak->cfra)) { - return 0; + if (ED_keylist_is_empty(keylist)) { + return false; } - if (val < ak->cfra) { - return -1; + const ActKeyColumn *first_column; + const ActKeyColumn *last_column; + if (keylist->is_runtime_initialized) { + first_column = &keylist->runtime.key_columns[0]; + last_column = &keylist->runtime.key_columns[keylist->column_len - 1]; } - return 1; -} + else { + first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first); + last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last); + } + r_frame_range->min = first_column->cfra; + r_frame_range->max = last_column->cfra; -/* --------------- */ + return true; +} /* Set of references to three logically adjacent keys. */ struct BezTripleChain { @@ -243,16 +425,8 @@ static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain) return KEYFRAME_EXTREME_NONE; } -/* Comparator callback used for ActKeyColumns and BezTripleChain */ -static short compare_ak_bezt(void *node, void *data) -{ - BezTripleChain *chain = static_cast<BezTripleChain *>(data); - - return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]); -} - /* New node callback used for building ActKeyColumns from BezTripleChain */ -static DLRBT_Node *nalloc_ak_bezt(void *data) +static ActKeyColumn *nalloc_ak_bezt(void *data) { ActKeyColumn *ak = static_cast<ActKeyColumn *>( MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn")); @@ -269,13 +443,12 @@ static DLRBT_Node *nalloc_ak_bezt(void *data) /* count keyframes in this column */ ak->totkey = 1; - return (DLRBT_Node *)ak; + return ak; } /* Node updater callback used for building ActKeyColumns from BezTripleChain */ -static void nupdate_ak_bezt(void *node, void *data) +static void nupdate_ak_bezt(ActKeyColumn *ak, void *data) { - ActKeyColumn *ak = static_cast<ActKeyColumn *>(node); const BezTripleChain *chain = static_cast<const BezTripleChain *>(data); const BezTriple *bezt = chain->cur; @@ -312,17 +485,8 @@ static void nupdate_ak_bezt(void *node, void *data) /* ......... */ -/* Comparator callback used for ActKeyColumns and GPencil frame */ -static short compare_ak_gpframe(void *node, void *data) -{ - const bGPDframe *gpf = (bGPDframe *)data; - - float frame = gpf->framenum; - return compare_ak_cfraPtr(node, &frame); -} - /* New node callback used for building ActKeyColumns from GPencil frames */ -static DLRBT_Node *nalloc_ak_gpframe(void *data) +static ActKeyColumn *nalloc_ak_gpframe(void *data) { ActKeyColumn *ak = static_cast<ActKeyColumn *>( MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF")); @@ -340,14 +504,13 @@ static DLRBT_Node *nalloc_ak_gpframe(void *data) ak->block.sel = ak->sel; ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL; - return (DLRBT_Node *)ak; + return ak; } /* Node updater callback used for building ActKeyColumns from GPencil frames */ -static void nupdate_ak_gpframe(void *node, void *data) +static void nupdate_ak_gpframe(ActKeyColumn *ak, void *data) { - ActKeyColumn *ak = (ActKeyColumn *)node; - const bGPDframe *gpf = (bGPDframe *)data; + bGPDframe *gpf = (bGPDframe *)data; /* set selection status and 'touched' status */ if (gpf->flag & GP_FRAME_SELECT) { @@ -366,17 +529,8 @@ static void nupdate_ak_gpframe(void *node, void *data) /* ......... */ -/* Comparator callback used for ActKeyColumns and GPencil frame */ -static short compare_ak_masklayshape(void *node, void *data) -{ - const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; - - float frame = masklay_shape->frame; - return compare_ak_cfraPtr(node, &frame); -} - /* New node callback used for building ActKeyColumns from GPencil frames */ -static DLRBT_Node *nalloc_ak_masklayshape(void *data) +static ActKeyColumn *nalloc_ak_masklayshape(void *data) { ActKeyColumn *ak = static_cast<ActKeyColumn *>( MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF")); @@ -389,14 +543,13 @@ static DLRBT_Node *nalloc_ak_masklayshape(void *data) /* count keyframes in this column */ ak->totkey = 1; - return (DLRBT_Node *)ak; + return ak; } /* Node updater callback used for building ActKeyColumns from GPencil frames */ -static void nupdate_ak_masklayshape(void *node, void *data) +static void nupdate_ak_masklayshape(ActKeyColumn *ak, void *data) { - ActKeyColumn *ak = (ActKeyColumn *)node; - const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; + MaskLayerShape *masklay_shape = (MaskLayerShape *)data; /* set selection status and 'touched' status */ if (masklay_shape->flag & MASK_SHAPE_SELECT) { @@ -408,6 +561,95 @@ static void nupdate_ak_masklayshape(void *node, void *data) } /* --------------- */ +using KeylistCreateColumnFunction = std::function<ActKeyColumn *(void *userdata)>; +using KeylistUpdateColumnFunction = std::function<void(ActKeyColumn *, void *)>; + +/* `ED_keylist_find_neighbour_front_to_back` is called before the runtime can be initialized so we + * cannot use bin searching. */ +static ActKeyColumn *ED_keylist_find_neighbour_front_to_back(ActKeyColumn *cursor, float cfra) +{ + while (cursor->next && cursor->next->cfra <= cfra) { + cursor = cursor->next; + } + return cursor; +} + +/* `ED_keylist_find_neighbour_back_to_front` is called before the runtime can be initialized so we + * cannot use bin searching. */ +static ActKeyColumn *ED_keylist_find_neighbour_back_to_front(ActKeyColumn *cursor, float cfra) +{ + while (cursor->prev && cursor->prev->cfra >= cfra) { + cursor = cursor->prev; + } + return cursor; +} + +/* + * `ED_keylist_find_exact_or_neighbour_column` is called before the runtime can be initialized so + * we cannot use bin searching. + * + * This function is called to add or update columns in the keylist. + * Typically columns are sorted by frame number so keeping track of the last_accessed_column + * reduces searching. + */ +static ActKeyColumn *ED_keylist_find_exact_or_neighbour_column(AnimKeylist *keylist, float cfra) +{ + BLI_assert(!keylist->is_runtime_initialized); + if (ED_keylist_is_empty(keylist)) { + return nullptr; + } + + ActKeyColumn *cursor = keylist->last_accessed_column.value_or( + static_cast<ActKeyColumn *>(keylist->key_columns.first)); + if (!is_cfra_eq(cursor->cfra, cfra)) { + const bool walking_direction_front_to_back = cursor->cfra <= cfra; + if (walking_direction_front_to_back) { + cursor = ED_keylist_find_neighbour_front_to_back(cursor, cfra); + } + else { + cursor = ED_keylist_find_neighbour_back_to_front(cursor, cfra); + } + } + + keylist->last_accessed_column = cursor; + return cursor; +} + +static void ED_keylist_add_or_update_column(AnimKeylist *keylist, + float cfra, + KeylistCreateColumnFunction create_func, + KeylistUpdateColumnFunction update_func, + void *userdata) +{ + BLI_assert_msg( + !keylist->is_runtime_initialized, + "Modifying AnimKeylist isn't allowed after runtime is initialized " + "keylist->key_columns/columns_len will get out of sync with runtime.key_columns."); + if (ED_keylist_is_empty(keylist)) { + ActKeyColumn *key_column = create_func(userdata); + BLI_addhead(&keylist->key_columns, key_column); + keylist->column_len += 1; + keylist->last_accessed_column = key_column; + return; + } + + ActKeyColumn *nearest = ED_keylist_find_exact_or_neighbour_column(keylist, cfra); + if (is_cfra_eq(nearest->cfra, cfra)) { + update_func(nearest, userdata); + } + else if (is_cfra_lt(nearest->cfra, cfra)) { + ActKeyColumn *key_column = create_func(userdata); + BLI_insertlinkafter(&keylist->key_columns, nearest, key_column); + keylist->column_len += 1; + keylist->last_accessed_column = key_column; + } + else { + ActKeyColumn *key_column = create_func(userdata); + BLI_insertlinkbefore(&keylist->key_columns, nearest, key_column); + keylist->column_len += 1; + keylist->last_accessed_column = key_column; + } +} /* Add the given BezTriple to the given 'list' of Keyframes */ static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt) @@ -416,7 +658,8 @@ static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *be return; } - BLI_dlrbTree_add(&keylist->keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); + float cfra = bezt->cur->vec[1][0]; + ED_keylist_add_or_update_column(keylist, cfra, nalloc_ak_bezt, nupdate_ak_bezt, bezt); } /* Add the given GPencil Frame to the given 'list' of Keyframes */ @@ -426,7 +669,8 @@ static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf) return; } - BLI_dlrbTree_add(&keylist->keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); + float cfra = gpf->framenum; + ED_keylist_add_or_update_column(keylist, cfra, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); } /* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */ @@ -436,11 +680,9 @@ static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape return; } - BLI_dlrbTree_add(&keylist->keys, - compare_ak_masklayshape, - nalloc_ak_masklayshape, - nupdate_ak_masklayshape, - masklay_shape); + float cfra = masklay_shape->frame; + ED_keylist_add_or_update_column( + keylist, cfra, nalloc_ak_masklayshape, nupdate_ak_masklayshape, masklay_shape); } /* ActKeyBlocks (Long Keyframes) ------------------------------------------ */ @@ -476,7 +718,7 @@ static void compute_keyblock_data(ActKeyBlockInfo *info, hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) && IS_EQF(prev->vec[1][1], prev->vec[2][1]); } - /* This interpolation type induces movement even between identical keys. */ + /* This interpolation type induces movement even between identical columns. */ else { hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC); } @@ -514,7 +756,7 @@ static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) { - ActKeyColumn *col = static_cast<ActKeyColumn *>(keylist->keys.first); + ActKeyColumn *col = static_cast<ActKeyColumn *>(keylist->key_columns.first); if (bezt && bezt_len >= 2) { ActKeyBlockInfo block; @@ -532,19 +774,15 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, co if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { /* Backtrack to find the right location. */ if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { - ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact( - &keylist->keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); + ActKeyColumn *newcol = ED_keylist_find_exact_or_neighbour_column(keylist, col->cfra); - if (newcol != nullptr) { - col = newcol; + BLI_assert(newcol); + BLI_assert(newcol->cfra == col->cfra); - /* The previous keyblock is garbage too. */ - if (col->prev != nullptr) { - add_keyblock_info(col->prev, &dummy_keyblock); - } - } - else { - BLI_assert(false); + col = newcol; + /* The previous keyblock is garbage too. */ + if (col->prev != nullptr) { + add_keyblock_info(col->prev, &dummy_keyblock); } } @@ -577,20 +815,17 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, co */ static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) { - /* Recompute the prev/next linked list. */ - BLI_dlrbTree_linkedlist_sync(&keylist->keys); - /* Find the curve count */ int max_curve = 0; - LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->key_columns) { max_curve = MAX2(max_curve, col->totcurve); } /* Propagate blocks to inserted keys */ ActKeyColumn *prev_ready = nullptr; - LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->key_columns) { /* Pre-existing column. */ if (col->totcurve > 0) { prev_ready = col; @@ -775,6 +1010,7 @@ void cachefile_to_keylist(bDopeSheet *ads, void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag) { if (fcu && fcu->totvert && fcu->bezt) { + ED_keylist_reset_last_accessed(keylist); /* apply NLA-mapping (if applicable) */ if (adt) { ANIM_nla_mapping_apply_fcurve(adt, fcu, false, false); @@ -790,7 +1026,7 @@ void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const i for (int v = 0; v < fcu->totvert; v++) { chain.cur = &fcu->bezt[v]; - /* Neighbor keys, accounting for being cyclic. */ + /* Neighbor columns, accounting for being cyclic. */ if (do_extremes) { chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : @@ -856,6 +1092,7 @@ void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, con void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylist) { if (gpl && keylist) { + ED_keylist_reset_last_accessed(keylist); /* Although the frames should already be in an ordered list, * they are not suitable for displaying yet. */ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { @@ -869,6 +1106,7 @@ void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylis void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist) { if (masklay && keylist) { + ED_keylist_reset_last_accessed(keylist); LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) { add_masklay_to_keycolumns_list(keylist, masklay_shape); } diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 0a499232ba9..8dc4aed9f0e 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2216,14 +2216,13 @@ static int clear_anim_v3d_exec(bContext *C, wmOperator *UNUSED(op)) /* in pose mode, only delete the F-Curve if it belongs to a selected bone */ if (ob->mode & OB_MODE_POSE) { - if ((fcu->rna_path) && strstr(fcu->rna_path, "pose.bones[")) { - - /* get bone-name, and check if this bone is selected */ - char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (bone_name) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name); - MEM_freeN(bone_name); - + if (fcu->rna_path) { + /* Get bone-name, and check if this bone is selected. */ + bPoseChannel *pchan = NULL; + char bone_name[sizeof(pchan->name)]; + if (BLI_str_quoted_substr( + fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { + pchan = BKE_pose_channel_find_name(ob->pose, bone_name); /* Delete if bone is selected. */ if ((pchan) && (pchan->bone)) { if (pchan->bone->flag & BONE_SELECTED) { @@ -2320,15 +2319,15 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) * NOTE: This is only done in pose mode. * In object mode, we're dealing with the entire object. */ - if ((ob->mode & OB_MODE_POSE) && strstr(fcu->rna_path, "pose.bones[\"")) { + if (ob->mode & OB_MODE_POSE) { bPoseChannel *pchan = NULL; - /* get bone-name, and check if this bone is selected */ - char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (bone_name) { - pchan = BKE_pose_channel_find_name(ob->pose, bone_name); - MEM_freeN(bone_name); + /* Get bone-name, and check if this bone is selected. */ + char bone_name[sizeof(pchan->name)]; + if (!BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { + continue; } + pchan = BKE_pose_channel_find_name(ob->pose, bone_name); /* skip if bone is not selected */ if ((pchan) && (pchan->bone)) { diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 45bf18fe1bb..21a5c6c2865 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -259,7 +259,7 @@ static int armature_click_extrude_invoke(bContext *C, wmOperator *op, const wmEv void ARMATURE_OT_click_extrude(wmOperatorType *ot) { /* identifiers */ - ot->name = "Click-Extrude"; + ot->name = "Extrude to Cursor"; ot->idname = "ARMATURE_OT_click_extrude"; ot->description = "Create a new bone going from the last selected joint to the mouse position"; @@ -269,7 +269,7 @@ void ARMATURE_OT_click_extrude(wmOperatorType *ot) ot->poll = ED_operator_editarmature; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* props */ } diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 1dbb859fd6c..e5b8983af93 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -1106,20 +1106,18 @@ static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool ex for (ksp = ks->paths.first; ksp; ksp = ksp->next) { /* only items related to this object will be relevant */ if ((ksp->id == &ob->id) && (ksp->rna_path != NULL)) { - if (strstr(ksp->rna_path, "bones")) { - char *boneName = BLI_str_quoted_substrN(ksp->rna_path, "bones["); - - if (boneName) { - bPoseChannel *pchan = BKE_pose_channel_find_name(pose, boneName); - MEM_freeN(boneName); - - if (pchan) { - /* select if bone is visible and can be affected */ - if (PBONE_SELECTABLE(arm, pchan->bone)) { - pchan->bone->flag |= BONE_SELECTED; - changed = true; - } - } + bPoseChannel *pchan = NULL; + char boneName[sizeof(pchan->name)]; + if (!BLI_str_quoted_substr(ksp->rna_path, "bones[", boneName, sizeof(boneName))) { + continue; + } + pchan = BKE_pose_channel_find_name(pose, boneName); + + if (pchan) { + /* select if bone is visible and can be affected */ + if (PBONE_SELECTABLE(arm, pchan->bone)) { + pchan->bone->flag |= BONE_SELECTED; + changed = true; } } } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index bc5cbd92deb..f23376867af 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -976,6 +976,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent * } /* Cancel if no keyframes found. */ + ED_keylist_prepare_for_direct_access(pso->keylist); if (ED_keylist_is_empty(pso->keylist)) { BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); pose_slide_exit(C, op); @@ -1267,6 +1268,8 @@ 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) { + RNA_float_set(op->ptr, "factor", ED_slider_factor_get(pso->slider)); + /* Update percentage indicator in header. */ pose_slide_draw_status(C, pso); @@ -1712,6 +1715,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st FCurve *fcu = (FCurve *)ld->data; fcurve_to_keylist(adt, fcu, keylist, 0); } + ED_keylist_prepare_for_direct_access(keylist); /* Find the long keyframe (i.e. hold), and hence obtain the endFrame value * - the best case would be one that starts on the frame itself diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index c399abfa52d..9b43e23bd32 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5620,7 +5620,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) void CURVE_OT_vertex_add(wmOperatorType *ot) { /* identifiers */ - ot->name = "Add Vertex"; + ot->name = "Extrude to Cursor or Add"; ot->idname = "CURVE_OT_vertex_add"; ot->description = "Add a new control point (linked to only selected end-curve one, if any)"; @@ -5630,7 +5630,7 @@ void CURVE_OT_vertex_add(wmOperatorType *ot) ot->poll = ED_operator_editcurve; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ RNA_def_float_vector_xyz(ot->srna, diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index d1fe162fc4a..75fb17e8cc1 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -515,7 +515,6 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) bool newob = false; bool enter_editmode; ushort local_view_bits; - float dia; float loc[3], rot[3]; float mat[4][4]; @@ -535,7 +534,6 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) newob = true; cu = (Curve *)obedit->data; - cu->flag |= CU_DEFORM_FILL; if (type & CU_PRIM_PATH) { cu->flag |= CU_PATH | CU_3D; @@ -556,9 +554,10 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) } } - ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); - dia = RNA_float_get(op->ptr, "radius"); - mul_mat3_m4_fl(mat, dia); + float radius = RNA_float_get(op->ptr, "radius"); + float scale[3]; + copy_v3_fl(scale, radius); + ED_object_new_primitive_matrix(C, obedit, loc, rot, scale, mat); nu = ED_curve_add_nurbs_primitive(C, obedit, mat, type, newob); editnurb = object_editcurve_get(obedit); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index c4916b9182f..702fd2e375a 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -336,6 +336,7 @@ set(ICON_NAMES lightprobe_cubemap lightprobe_planar lightprobe_grid + mod_dash color_red color_green color_blue @@ -390,6 +391,9 @@ set(ICON_NAMES small_caps modifier con_action + mod_length + mod_dash + mod_lineart holdout_off holdout_on indirect_only_off diff --git a/source/blender/editors/gizmo_library/CMakeLists.txt b/source/blender/editors/gizmo_library/CMakeLists.txt index eeb1e60166b..bfe8334b390 100644 --- a/source/blender/editors/gizmo_library/CMakeLists.txt +++ b/source/blender/editors/gizmo_library/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/clog ../../../../intern/eigen ../../../../intern/glew-mx ../../../../intern/guardedalloc diff --git a/source/blender/editors/gizmo_library/gizmo_library_utils.c b/source/blender/editors/gizmo_library/gizmo_library_utils.c index 7d0ae5afb9b..a0db2a8e627 100644 --- a/source/blender/editors/gizmo_library/gizmo_library_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_library_utils.c @@ -39,9 +39,13 @@ #include "ED_view3d.h" +#include "CLG_log.h" + /* own includes */ #include "gizmo_library_intern.h" +static CLG_LogRef LOG = {"ed.gizmo.library_utils"}; + /* factor for precision tweaking */ #define GIZMO_PRECISION_FAC 0.05f @@ -182,7 +186,7 @@ bool gizmo_window_project_2d(bContext *C, bool use_offset, float r_co[2]) { - float mat[4][4]; + float mat[4][4], imat[4][4]; { float mat_identity[4][4]; struct WM_GizmoMatrixParams params = {NULL}; @@ -193,6 +197,14 @@ bool gizmo_window_project_2d(bContext *C, WM_gizmo_calc_matrix_final_params(gz, ¶ms, mat); } + if (!invert_m4_m4(imat, mat)) { + CLOG_WARN(&LOG, + "Gizmo \"%s\" of group \"%s\" has matrix that could not be inverted " + "(projection will fail)", + gz->type->idname, + gz->parent_gzgroup->type->idname); + } + /* rotate mouse in relation to the center and relocate it */ if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) { /* For 3d views, transform 2D mouse pos onto plane. */ @@ -202,8 +214,6 @@ bool gizmo_window_project_2d(bContext *C, plane_from_point_normal_v3(plane, mat[3], mat[2]); bool clip_ray = ((RegionView3D *)region->regiondata)->is_persp; if (ED_view3d_win_to_3d_on_plane(region, plane, mval, clip_ray, co)) { - float imat[4][4]; - invert_m4_m4(imat, mat); mul_m4_v3(imat, co); r_co[0] = co[(axis + 1) % 3]; r_co[1] = co[(axis + 2) % 3]; @@ -213,8 +223,6 @@ bool gizmo_window_project_2d(bContext *C, } float co[3] = {mval[0], mval[1], 0.0f}; - float imat[4][4]; - invert_m4_m4(imat, mat); mul_m4_v3(imat, co); copy_v2_v2(r_co, co); return true; @@ -223,7 +231,7 @@ bool gizmo_window_project_2d(bContext *C, bool gizmo_window_project_3d( bContext *C, const struct wmGizmo *gz, const float mval[2], bool use_offset, float r_co[3]) { - float mat[4][4]; + float mat[4][4], imat[4][4]; { float mat_identity[4][4]; struct WM_GizmoMatrixParams params = {NULL}; @@ -234,20 +242,25 @@ bool gizmo_window_project_3d( WM_gizmo_calc_matrix_final_params(gz, ¶ms, mat); } + if (!invert_m4_m4(imat, mat)) { + CLOG_WARN(&LOG, + "Gizmo \"%s\" of group \"%s\" has matrix that could not be inverted " + "(projection will fail)", + gz->type->idname, + gz->parent_gzgroup->type->idname); + } + if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) { View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); /* NOTE: we might want a custom reference point passed in, * instead of the gizmo center. */ ED_view3d_win_to_3d(v3d, region, mat[3], mval, r_co); - invert_m4(mat); - mul_m4_v3(mat, r_co); + mul_m4_v3(imat, r_co); return true; } float co[3] = {mval[0], mval[1], 0.0f}; - float imat[4][4]; - invert_m4_m4(imat, mat); mul_m4_v3(imat, co); copy_v2_v2(r_co, co); return true; diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index bf47704746b..65c5b8ee573 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -2423,7 +2423,7 @@ static void annotation_add_missing_events(bContext *C, static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { tGPsdata *p = op->customdata; - /* default exit state - pass through to support MMB view nav, etc. */ + /* Default exit state - pass through to support MMB view navigation, etc. */ int estate = OPERATOR_PASS_THROUGH; /* NOTE(mike erwin): Not quite what I was looking for, but a good start! diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 8baac26bed3..a77d3bee025 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -1347,6 +1347,8 @@ static int gpencil_merge_layer_exec(bContext *C, wmOperator *op) bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum)); if (!gpf_dst) { gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf_src->framenum, GP_GETFRAME_ADD_COPY); + /* Use same frame type. */ + gpf_dst->key_type = gpf_src->key_type; BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum), gpf_dst); } } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 8d1f841da6c..75ddfa47c57 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3618,7 +3618,7 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op) } elem = &strokes_list[i]; /* Join new_stroke and stroke B. */ - BKE_gpencil_stroke_join(gps_new, elem->gps, leave_gaps, true); + BKE_gpencil_stroke_join(gps_new, elem->gps, leave_gaps, true, false); elem->used = true; } @@ -3967,7 +3967,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op) /* perform smoothing */ if (smooth_position) { - BKE_gpencil_stroke_smooth(gps, i, factor); + BKE_gpencil_stroke_smooth_point(gps, i, factor); } if (smooth_strength) { BKE_gpencil_stroke_smooth_strength(gps, i, factor); @@ -5243,7 +5243,7 @@ void GPENCIL_OT_stroke_cutter(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* flag */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ WM_operator_properties_gesture_lasso(ot); diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 0c88d678ef4..f5474a7cdc3 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1569,7 +1569,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) float smoothfac = 1.0f; for (int r = 0; r < 1; r++) { for (int i = 0; i < gps->totpoints; i++) { - BKE_gpencil_stroke_smooth(gps, i, smoothfac - reduce); + BKE_gpencil_stroke_smooth_point(gps, i, smoothfac - reduce); } reduce += 0.25f; /* reduce the factor */ } diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index a8bd3b11bb1..fdd9f44605e 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -333,7 +333,7 @@ static void gpencil_interpolate_smooth_stroke(bGPDstroke *gps, float reduce = 0.0f; for (int r = 0; r < smooth_steps; r++) { for (int i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_stroke_smooth(gps, i, smooth_factor - reduce); + BKE_gpencil_stroke_smooth_point(gps, i, smooth_factor - reduce); BKE_gpencil_stroke_smooth_strength(gps, i, smooth_factor); } reduce += 0.25f; /* reduce the factor */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index d2dbf6ab2a6..9b157224178 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1209,7 +1209,8 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) float reduce = 0.0f; for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) { for (i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_stroke_smooth(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); + BKE_gpencil_stroke_smooth_point( + gps, i, brush->gpencil_settings->draw_smoothfac - reduce); BKE_gpencil_stroke_smooth_strength(gps, i, brush->gpencil_settings->draw_smoothfac); } reduce += 0.25f; /* reduce the factor */ @@ -1221,7 +1222,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) float ifac = (float)brush->gpencil_settings->input_samples / 10.0f; float sfac = interpf(1.0f, 0.2f, ifac); for (i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_stroke_smooth(gps, i, sfac); + BKE_gpencil_stroke_smooth_point(gps, i, sfac); BKE_gpencil_stroke_smooth_strength(gps, i, sfac); } } @@ -1288,11 +1289,23 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) /* Join with existing strokes. */ if (ts->gpencil_flags & GP_TOOL_FLAG_AUTOMERGE_STROKE) { if (gps->prev != NULL) { + BKE_gpencil_stroke_boundingbox_calc(gps); + float diff_mat[4][4], ctrl1[2], ctrl2[2]; + BKE_gpencil_layer_transform_matrix_get(depsgraph, p->ob, gpl, diff_mat); + ED_gpencil_stroke_extremes_to2d(&p->gsc, diff_mat, gps, ctrl1, ctrl2); + int pt_index = 0; bool doit = true; while (doit && gps) { - bGPDstroke *gps_target = ED_gpencil_stroke_nearest_to_ends( - p->C, &p->gsc, gpl, gpl->actframe, gps, GPENCIL_MINIMUM_JOIN_DIST, &pt_index); + bGPDstroke *gps_target = ED_gpencil_stroke_nearest_to_ends(p->C, + &p->gsc, + gpl, + gpl->actframe, + gps, + ctrl1, + ctrl2, + GPENCIL_MINIMUM_JOIN_DIST, + &pt_index); if (gps_target != NULL) { gps = ED_gpencil_stroke_join_and_trim(p->gpd, p->gpf, gps, gps_target, pt_index); } @@ -3568,7 +3581,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) // ToolSettings *ts = CTX_data_tool_settings(C); GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - /* default exit state - pass through to support MMB view nav, etc. */ + /* Default exit state - pass through to support MMB view navigation, etc. */ int estate = OPERATOR_PASS_THROUGH; /* NOTE(mike erwin): Not quite what I was looking for, but a good start! diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 5ecb6d9a212..f8cfc130e35 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1382,11 +1382,23 @@ static void gpencil_primitive_interaction_end(bContext *C, if (ts->gpencil_flags & GP_TOOL_FLAG_AUTOMERGE_STROKE) { if (ELEM(tgpi->type, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_CURVE, GP_STROKE_POLYLINE)) { if (gps->prev != NULL) { + BKE_gpencil_stroke_boundingbox_calc(gps); + float diff_mat[4][4], ctrl1[2], ctrl2[2]; + BKE_gpencil_layer_transform_matrix_get(tgpi->depsgraph, tgpi->ob, tgpi->gpl, diff_mat); + ED_gpencil_stroke_extremes_to2d(&tgpi->gsc, diff_mat, gps, ctrl1, ctrl2); + int pt_index = 0; bool doit = true; while (doit && gps) { - bGPDstroke *gps_target = ED_gpencil_stroke_nearest_to_ends( - C, &tgpi->gsc, tgpi->gpl, gpf, gps, GPENCIL_MINIMUM_JOIN_DIST, &pt_index); + bGPDstroke *gps_target = ED_gpencil_stroke_nearest_to_ends(C, + &tgpi->gsc, + tgpi->gpl, + gpf, + gps, + ctrl1, + ctrl2, + GPENCIL_MINIMUM_JOIN_DIST, + &pt_index); if (gps_target != NULL) { gps = ED_gpencil_stroke_join_and_trim(tgpi->gpd, gpf, gps, gps_target, pt_index); } diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 869254cef3b..e9a6beab798 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -337,7 +337,7 @@ static bool gpencil_brush_smooth_apply(tGP_BrushEditData *gso, /* perform smoothing */ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) { - BKE_gpencil_stroke_smooth(gps, pt_index, inf); + BKE_gpencil_stroke_smooth_point(gps, pt_index, inf); } if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) { BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf); diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 93bae7d3614..6ad2fffc773 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -2347,7 +2347,7 @@ void GPENCIL_OT_select_lasso(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* flags */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ WM_operator_properties_select_operation(ot); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 5cc52303cd6..bb05b93ad81 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -3213,11 +3213,28 @@ bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, return hit; } +/* Get extremes of stroke in 2D using current view. */ +void ED_gpencil_stroke_extremes_to2d(const GP_SpaceConversion *gsc, + const float diff_mat[4][4], + bGPDstroke *gps, + float r_ctrl1[2], + float r_ctrl2[2]) +{ + bGPDspoint pt_dummy_ps; + + gpencil_point_to_parent_space(&gps->points[0], diff_mat, &pt_dummy_ps); + gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &r_ctrl1[0], &r_ctrl1[1]); + gpencil_point_to_parent_space(&gps->points[gps->totpoints - 1], diff_mat, &pt_dummy_ps); + gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &r_ctrl2[0], &r_ctrl2[1]); +} + bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, const GP_SpaceConversion *gsc, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, + const float ctrl1[2], + const float ctrl2[2], const float radius, int *r_index) { @@ -3267,6 +3284,15 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, gpencil_point_to_parent_space(pt, diff_mat, &pt_parent); gpencil_point_to_xy_fl(gsc, gps, &pt_parent, &pt2d_target_end[0], &pt2d_target_end[1]); + /* If the distance to the original stroke extremes is too big, the stroke must not be joined. + */ + if ((len_squared_v2v2(ctrl1, pt2d_target_start) > radius_sqr) && + (len_squared_v2v2(ctrl1, pt2d_target_end) > radius_sqr) && + (len_squared_v2v2(ctrl2, pt2d_target_start) > radius_sqr) && + (len_squared_v2v2(ctrl2, pt2d_target_end) > radius_sqr)) { + continue; + } + if ((len_squared_v2v2(pt2d_start, pt2d_target_start) > radius_sqr) && (len_squared_v2v2(pt2d_start, pt2d_target_end) > radius_sqr) && (len_squared_v2v2(pt2d_end, pt2d_target_start) > radius_sqr) && @@ -3350,7 +3376,7 @@ bGPDstroke *ED_gpencil_stroke_join_and_trim( /* Join both strokes. */ int totpoint = gps_final->totpoints; - BKE_gpencil_stroke_join(gps_final, gps, false, true); + BKE_gpencil_stroke_join(gps_final, gps, false, true, true); /* Select the join points and merge if the distance is very small. */ pt = &gps_final->points[totpoint - 1]; diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c index 402bccce2f7..5c3a7cf9e6f 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_ops.c +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -588,6 +588,7 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op) changed = true; copy_v3_v3(gps->vert_color_fill, brush->rgb); gps->vert_color_fill[3] = factor; + srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill); } /* Stroke points. */ @@ -596,10 +597,13 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op) int i; bGPDspoint *pt; + float color[4]; + copy_v3_v3(color, brush->rgb); + color[3] = factor; + srgb_to_linearrgb_v4(color, color); for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if ((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) { - copy_v3_v3(pt->vert_color, brush->rgb); - pt->vert_color[3] = factor; + copy_v3_v3(pt->vert_color, color); } } } diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 75c02082bd3..6a0a42ee77b 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -678,6 +678,10 @@ void ANIM_draw_framerange(struct Scene *scene, struct View2D *v2d); /* ------------- UI Panel Drawing -------------- */ +bool ANIM_nla_context_track_ptr(const struct bContext *C, struct PointerRNA *r_ptr); +bool ANIM_nla_context_strip_ptr(const struct bContext *C, struct PointerRNA *r_ptr); + +struct NlaTrack *ANIM_nla_context_track(const struct bContext *C); struct NlaStrip *ANIM_nla_context_strip(const struct bContext *C); struct FCurve *ANIM_graph_context_fcurve(const struct bContext *C); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 8a8d91a570c..c760b661373 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -391,8 +391,15 @@ struct bGPDstroke *ED_gpencil_stroke_nearest_to_ends(struct bContext *C, struct bGPDlayer *gpl, struct bGPDframe *gpf, struct bGPDstroke *gps, + const float ctrl1[2], + const float ctrl2[2], const float radius, int *r_index); +void ED_gpencil_stroke_extremes_to2d(const struct GP_SpaceConversion *gsc, + const float diff_mat[4][4], + struct bGPDstroke *gps, + float r_ctrl1[2], + float r_ctrl2[2]); struct bGPDstroke *ED_gpencil_stroke_join_and_trim(struct bGPdata *gpd, struct bGPDframe *gpf, diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index ec525806b81..6b0b9f4a27c 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -46,7 +46,6 @@ struct wmWindowManager; struct Image *ED_space_image(struct SpaceImage *sima); void ED_space_image_set(struct Main *bmain, struct SpaceImage *sima, - struct Object *obedit, struct Image *ima, bool automatic); void ED_space_image_auto_set(const struct bContext *C, struct SpaceImage *sima); diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index 3a9750c1206..4194444ca0f 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -139,14 +139,20 @@ typedef enum eKeyframeExtremeDrawOpts { struct AnimKeylist *ED_keylist_create(void); void ED_keylist_free(struct AnimKeylist *keylist); -const struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, float cfra); -const struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, float cfra); -const struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, float cfra); +void ED_keylist_prepare_for_direct_access(struct AnimKeylist *keylist); +const struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, + const float cfra); +const struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, + const float cfra); +const struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, + const float cfra); const struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist *keylist, const Range2f frame_range); bool ED_keylist_is_empty(const struct AnimKeylist *keylist); const struct ListBase /* ActKeyColumn */ *ED_keylist_listbase(const struct AnimKeylist *keylist); bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range); +const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist); +int64_t ED_keylist_array_len(const struct AnimKeylist *keylist); /* Key-data Generation --------------- */ @@ -197,8 +203,6 @@ void mask_to_keylist(struct bDopeSheet *ads, struct AnimKeylist *keylist); /* ActKeyColumn API ---------------- */ -/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ -short compare_ak_cfraPtr(void *node, void *data); /* Checks if ActKeyColumn has any block data */ bool actkeyblock_is_valid(const ActKeyColumn *ac); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index a9cf04e1ad7..5397cd95ace 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -286,6 +286,7 @@ float ED_object_new_primitive_matrix(struct bContext *C, struct Object *obedit, const float loc[3], const float rot[3], + const float scale[3], float primmat[4][4]); /* Avoid allowing too much insane values even by typing diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 69ac48d842f..bedd0e2fa35 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -109,7 +109,8 @@ bool BIF_createTransformOrientation(struct bContext *C, const bool overwrite); void BIF_selectTransformOrientation(struct bContext *C, struct TransformOrientation *target); -void ED_getTransformOrientationMatrix(const struct bContext *C, +void ED_getTransformOrientationMatrix(struct ViewLayer *view_layer, + const struct View3D *v3d, struct Object *ob, struct Object *obedit, const short around, @@ -145,15 +146,15 @@ void Transform_Properties(struct wmOperatorType *ot, int flags); /* *** transform_orientations.c *** */ void ED_transform_calc_orientation_from_type(const struct bContext *C, float r_mat[3][3]); -short ED_transform_calc_orientation_from_type_ex(const struct bContext *C, - float r_mat[3][3], - /* extra args */ - struct Scene *scene, - struct RegionView3D *rv3d, +short ED_transform_calc_orientation_from_type_ex(const struct Scene *scene, + struct ViewLayer *view_layer, + const struct View3D *v3d, + const struct RegionView3D *rv3d, struct Object *ob, struct Object *obedit, const short orientation_index, - const int pivot_point); + const int pivot_point, + float r_mat[3][3]); /* transform gizmos */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 2c958d282f9..cf8dcbd7995 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -566,6 +566,13 @@ eV3DSelectObjectFilter ED_view3d_select_filter_from_mode(const struct Scene *sce void view3d_opengl_select_cache_begin(void); void view3d_opengl_select_cache_end(void); +int view3d_opengl_select_ex(struct ViewContext *vc, + unsigned int *buffer, + unsigned int bufsize, + const struct rcti *input, + eV3DSelectMode select_mode, + eV3DSelectObjectFilter select_filter, + const bool do_material_slot_selection); int view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, @@ -638,6 +645,9 @@ void ED_view3d_draw_setup_view(const struct wmWindowManager *wm, struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); struct Object *ED_view3d_give_object_under_cursor(struct bContext *C, const int mval[2]); +struct Object *ED_view3d_give_material_slot_under_cursor(struct bContext *C, + const int mval[2], + int *r_material_slot); bool ED_view3d_is_object_under_cursor(struct bContext *C, const int mval[2]); void ED_view3d_quadview_update(struct ScrArea *area, struct ARegion *region, bool do_clip); void ED_view3d_update_viewmat(struct Depsgraph *depsgraph, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 1708c3598b1..ddd9ca4a98c 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -490,9 +490,9 @@ DEF_ICON_MODIFIER(CON_ACTION) DEF_ICON_BLANK(745) DEF_ICON_BLANK(746) DEF_ICON_BLANK(747) -DEF_ICON_BLANK(748) -DEF_ICON_BLANK(749) -DEF_ICON_BLANK(750) +DEF_ICON_MODIFIER(MOD_LENGTH) +DEF_ICON_MODIFIER(MOD_DASH) +DEF_ICON_MODIFIER(MOD_LINEART) DEF_ICON_BLANK(751) DEF_ICON(HOLDOUT_OFF) DEF_ICON(HOLDOUT_ON) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 7211cf9f893..916105b0f8e 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2209,6 +2209,11 @@ enum uiTemplateListFlags { UI_TEMPLATE_LIST_SORT_LOCK = (1 << 1), /* Don't allow resizing the list, i.e. don't add the grip button. */ UI_TEMPLATE_LIST_NO_GRIP = (1 << 2), + /** Do not show filtering options, not even the button to expand/collapse them. Also hides the + * grip button. */ + UI_TEMPLATE_LIST_NO_FILTER_OPTIONS = (1 << 3), + /** For #UILST_LAYOUT_BIG_PREVIEW_GRID, don't reserve space for the name label. */ + UI_TEMPLATE_LIST_NO_NAMES = (1 << 4), UI_TEMPLATE_LIST_FLAGS_LAST }; @@ -2289,6 +2294,12 @@ int uiTemplateRecentFiles(struct uiLayout *layout, int rows); void uiTemplateFileSelectPath(uiLayout *layout, struct bContext *C, struct FileSelectParams *params); + +enum { + UI_TEMPLATE_ASSET_DRAW_NO_NAMES = (1 << 0), + UI_TEMPLATE_ASSET_DRAW_NO_FILTER = (1 << 1), + UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY = (1 << 2), +}; void uiTemplateAssetView(struct uiLayout *layout, struct bContext *C, const char *list_id, @@ -2299,6 +2310,7 @@ void uiTemplateAssetView(struct uiLayout *layout, struct PointerRNA *active_dataptr, const char *active_propname, const struct AssetFilterSettings *filter_settings, + const int display_flags, const char *activate_opname, struct PointerRNA *r_activate_op_properties, const char *drag_opname, diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index b953d88c896..bb9e813ea50 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -70,26 +70,12 @@ static IDProperty *shortcut_property_from_rna(bContext *C, uiBut *but) /* If this returns null, we won't be able to bind shortcuts to these RNA properties. * Support can be added at #wm_context_member_from_ptr. */ - const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin); - if (member_id == NULL) { + char *final_data_path = WM_context_path_resolve_property_full( + C, &but->rnapoin, but->rnaprop, but->rnaindex); + if (final_data_path == NULL) { return NULL; } - const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin); - const char *member_id_data_path = member_id; - - if (data_path) { - member_id_data_path = BLI_sprintfN("%s.%s", member_id, data_path); - MEM_freeN((void *)data_path); - } - - const char *prop_id = RNA_property_identifier(but->rnaprop); - const char *final_data_path = BLI_sprintfN("%s.%s", member_id_data_path, prop_id); - - if (member_id != member_id_data_path) { - MEM_freeN((void *)member_id_data_path); - } - /* Create ID property of data path, to pass to the operator. */ const IDPropertyTemplate val = {0}; IDProperty *prop = IDP_New(IDP_GROUP, &val, __func__); @@ -329,10 +315,24 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) static bool ui_but_is_user_menu_compatible(bContext *C, uiBut *but) { - return (but->optype || - (but->rnaprop && (RNA_property_type(but->rnaprop) == PROP_BOOLEAN) && - (WM_context_member_from_ptr(C, &but->rnapoin) != NULL)) || - UI_but_menutype_get(but)); + bool result = false; + if (but->optype) { + result = true; + } + else if (but->rnaprop) { + if (RNA_property_type(but->rnaprop) == PROP_BOOLEAN) { + char *data_path = WM_context_path_resolve_full(C, &but->rnapoin); + if (data_path != NULL) { + MEM_freeN(data_path); + result = true; + } + } + } + else if (UI_but_menutype_get(but)) { + result = true; + } + + return result; } static bUserMenuItem *ui_but_user_menu_find(bContext *C, uiBut *but, bUserMenu *um) @@ -343,21 +343,11 @@ static bUserMenuItem *ui_but_user_menu_find(bContext *C, uiBut *but, bUserMenu * &um->items, but->optype, prop, but->opcontext); } if (but->rnaprop) { - const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin); - const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin); - const char *member_id_data_path = member_id; - if (data_path) { - member_id_data_path = BLI_sprintfN("%s.%s", member_id, data_path); - } + char *member_id_data_path = WM_context_path_resolve_full(C, &but->rnapoin); const char *prop_id = RNA_property_identifier(but->rnaprop); bUserMenuItem *umi = (bUserMenuItem *)ED_screen_user_menu_item_find_prop( &um->items, member_id_data_path, prop_id, but->rnaindex); - if (data_path) { - MEM_freeN((void *)data_path); - } - if (member_id != member_id_data_path) { - MEM_freeN((void *)member_id_data_path); - } + MEM_freeN(member_id_data_path); return umi; } @@ -412,21 +402,11 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um) } else if (but->rnaprop) { /* NOTE: 'member_id' may be a path. */ - const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin); - const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin); - const char *member_id_data_path = member_id; - if (data_path) { - member_id_data_path = BLI_sprintfN("%s.%s", member_id, data_path); - } + char *member_id_data_path = WM_context_path_resolve_full(C, &but->rnapoin); const char *prop_id = RNA_property_identifier(but->rnaprop); /* NOTE: ignore 'drawstr', use property idname always. */ ED_screen_user_menu_item_add_prop(&um->items, "", member_id_data_path, prop_id, but->rnaindex); - if (data_path) { - MEM_freeN((void *)data_path); - } - if (member_id != member_id_data_path) { - MEM_freeN((void *)member_id_data_path); - } + MEM_freeN(member_id_data_path); } else if ((mt = UI_but_menutype_get(but))) { ED_screen_user_menu_item_add_menu(&um->items, drawstr, mt); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 977e9661dd9..77ae16d7cc7 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -508,6 +508,7 @@ typedef struct uiAfterFunc { bContextStore *context; char undostr[BKE_UNDO_STR_MAX]; + char drawstr[UI_MAX_DRAW_STR]; } uiAfterFunc; static void button_activate_init(bContext *C, @@ -790,6 +791,10 @@ static void ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot, if (context_but && context_but->context) { after->context = CTX_store_copy(context_but->context); } + + if (context_but) { + ui_but_drawstr_without_sep_char(context_but, after->drawstr, sizeof(after->drawstr)); + } } void ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext) @@ -900,6 +905,8 @@ static void ui_apply_but_func(bContext *C, uiBut *but) after->context = CTX_store_copy(but->context); } + ui_but_drawstr_without_sep_char(but, after->drawstr, sizeof(after->drawstr)); + but->optype = NULL; but->opcontext = 0; but->opptr = NULL; @@ -1021,7 +1028,8 @@ static void ui_apply_but_funcs_after(bContext *C) } if (after.optype) { - WM_operator_name_call_ptr(C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL); + WM_operator_name_call_ptr_with_depends_on_cursor( + C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL, after.drawstr); } if (after.opptr) { @@ -4190,10 +4198,11 @@ static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtra ui_apply_but(C, but->block, but, but->active, true); } button_activate_state(C, but, BUTTON_STATE_EXIT); - WM_operator_name_call_ptr(C, - op_icon->optype_params->optype, - op_icon->optype_params->opcontext, - op_icon->optype_params->opptr); + WM_operator_name_call_ptr_with_depends_on_cursor(C, + op_icon->optype_params->optype, + op_icon->optype_params->opcontext, + op_icon->optype_params->opptr, + NULL); /* Force recreation of extra operator icons (pseudo update). */ ui_but_extra_operator_icons_free(but); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index ec5a30f7793..66c75c63050 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -921,10 +921,10 @@ static void ui_keymap_but_cb(bContext *UNUSED(C), void *but_v, void *UNUSED(key_ { uiBut *but = but_v; - RNA_boolean_set(&but->rnapoin, "shift", (but->modifier_key & KM_SHIFT) != 0); - RNA_boolean_set(&but->rnapoin, "ctrl", (but->modifier_key & KM_CTRL) != 0); - RNA_boolean_set(&but->rnapoin, "alt", (but->modifier_key & KM_ALT) != 0); - RNA_boolean_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) != 0); + RNA_int_set(&but->rnapoin, "shift", (but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set(&but->rnapoin, "ctrl", (but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set(&but->rnapoin, "alt", (but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING); } /** diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index 9b601727e29..f27b37a27de 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -46,6 +46,7 @@ struct AssetViewListData { AssetLibraryReference asset_library_ref; bScreen *screen; + bool show_names; }; static void asset_view_item_but_drag_set(uiBut *but, @@ -95,14 +96,15 @@ static void asset_view_draw_item(uiList *ui_list, uiLayoutSetContextPointer(layout, "asset_handle", itemptr); uiBlock *block = uiLayoutGetBlock(layout); + const bool show_names = list_data->show_names; /* TODO ED_fileselect_init_layout(). Share somehow? */ const float size_x = (96.0f / 20.0f) * UI_UNIT_X; - const float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + const float size_y = (96.0f / 20.0f) * UI_UNIT_Y - (show_names ? 0 : UI_UNIT_Y); uiBut *but = uiDefIconTextBut(block, UI_BTYPE_PREVIEW_TILE, 0, ED_asset_handle_get_preview_icon_id(asset_handle), - ED_asset_handle_get_name(asset_handle), + show_names ? ED_asset_handle_get_name(asset_handle) : "", 0, 0, size_x, @@ -202,6 +204,7 @@ void uiTemplateAssetView(uiLayout *layout, PointerRNA *active_dataptr, const char *active_propname, const AssetFilterSettings *filter_settings, + const int display_flags, const char *activate_opname, PointerRNA *r_activate_op_properties, const char *drag_opname, @@ -220,9 +223,11 @@ void uiTemplateAssetView(uiLayout *layout, RNA_property_enum_get(asset_library_dataptr, asset_library_prop)); uiLayout *row = uiLayoutRow(col, true); - uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); - if (asset_library_ref.type != ASSET_LIBRARY_LOCAL) { - uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); + if ((display_flags & UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY) == 0) { + uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); + if (asset_library_ref.type != ASSET_LIBRARY_LOCAL) { + uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); + } } ED_assetlist_storage_fetch(&asset_library_ref, C); @@ -236,6 +241,15 @@ void uiTemplateAssetView(uiLayout *layout, "AssetViewListData"); list_data->asset_library_ref = asset_library_ref; list_data->screen = CTX_wm_screen(C); + list_data->show_names = (display_flags & UI_TEMPLATE_ASSET_DRAW_NO_NAMES) == 0; + + uiTemplateListFlags template_list_flags = UI_TEMPLATE_LIST_NO_GRIP; + if ((display_flags & UI_TEMPLATE_ASSET_DRAW_NO_NAMES) != 0) { + template_list_flags |= UI_TEMPLATE_LIST_NO_NAMES; + } + if ((display_flags & UI_TEMPLATE_ASSET_DRAW_NO_FILTER) != 0) { + template_list_flags |= UI_TEMPLATE_LIST_NO_FILTER_OPTIONS; + } /* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading * (of previews) of the items? */ @@ -252,7 +266,7 @@ void uiTemplateAssetView(uiLayout *layout, 0, UILST_LAYOUT_BIG_PREVIEW_GRID, 0, - UI_TEMPLATE_LIST_NO_GRIP, + template_list_flags, list_data); if (!list) { /* List creation failed. */ diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 0ab45ea0f81..8246759ad36 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -944,10 +944,16 @@ static void ui_template_list_layout_draw(bContext *C, /* For scrollbar. */ row = uiLayoutRow(glob, false); + const bool show_names = (flags & UI_TEMPLATE_LIST_NO_NAMES) == 0; + /* TODO ED_fileselect_init_layout(). Share somehow? */ float size_x = (96.0f / 20.0f) * UI_UNIT_X; float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + if (!show_names) { + size_y -= UI_UNIT_Y; + } + const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1); uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true); @@ -1033,7 +1039,8 @@ static void ui_template_list_layout_draw(bContext *C, break; } - if (glob) { + const bool add_filters_but = (flags & UI_TEMPLATE_LIST_NO_FILTER_OPTIONS) == 0; + if (glob && add_filters_but) { const bool add_grip_but = (flags & UI_TEMPLATE_LIST_NO_GRIP) == 0; /* About #UI_BTYPE_GRIP drag-resize: diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index 3105891142f..672f1b64943 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -955,7 +955,8 @@ static void menu_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) switch (item->type) { case MENU_SEARCH_TYPE_OP: { CTX_store_set(C, item->op.context); - WM_operator_name_call_ptr(C, item->op.type, item->op.opcontext, item->op.opptr); + WM_operator_name_call_ptr_with_depends_on_cursor( + C, item->op.type, item->op.opcontext, item->op.opptr, item->drawstr); CTX_store_set(C, NULL); break; } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 08d78552710..0c9eb20af19 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -673,8 +673,8 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) } } else { - if (BKE_lib_id_make_local(bmain, id, false, 0)) { - BKE_main_id_newptr_and_tag_clear(bmain); + if (BKE_lib_id_make_local(bmain, id, 0)) { + BKE_id_newptr_and_tag_clear(id); /* Reassign to get proper updates/notifiers. */ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); @@ -1031,7 +1031,7 @@ static void template_ID(const bContext *C, UI_but_flag_enable(but, UI_BUT_DISABLED); } else { - const bool disabled = (!BKE_lib_id_make_local(CTX_data_main(C), id, true /* test */, 0) || + const bool disabled = (!BKE_idtype_idcode_is_localizable(GS(id->name)) || (idfrom && idfrom->lib)); but = uiDefIconBut(block, UI_BTYPE_BUT, diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index a2b86ccd947..0dc7c2d3f9a 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -5443,13 +5443,20 @@ void ui_draw_preview_item_stateless(const uiFontStyle *fstyle, rcti trect = *rect; const float text_size = UI_UNIT_Y; float font_dims[2] = {0.0f, 0.0f}; + const bool has_text = name && name[0]; - /* draw icon in rect above the space reserved for the label */ - rect->ymin += text_size; + if (has_text) { + /* draw icon in rect above the space reserved for the label */ + rect->ymin += text_size; + } GPU_blend(GPU_BLEND_ALPHA); widget_draw_preview(iconid, 1.0f, rect); GPU_blend(GPU_BLEND_NONE); + if (!has_text) { + return; + } + BLF_width_and_height( fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]); diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 6a1be8dcef3..fe6acac7d29 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -629,7 +629,7 @@ void MASK_OT_select_lasso(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* flags */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ WM_operator_properties_gesture_lasso(ot); diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index a64b90e15a3..c826da74010 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -73,11 +73,7 @@ static Object *make_prim_init(bContext *C, r_creation_data->was_editmode = true; } - ED_object_new_primitive_matrix(C, obedit, loc, rot, r_creation_data->mat); - - if (scale) { - rescale_m4(r_creation_data->mat, scale); - } + ED_object_new_primitive_matrix(C, obedit, loc, rot, scale, r_creation_data->mat); return obedit; } @@ -351,7 +347,7 @@ static int add_primitive_cylinder_exec(bContext *C, wmOperator *op) op, "verts.out", false, - "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b " + "create_cone segments=%i radius1=%f radius2=%f cap_ends=%b " "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b", RNA_int_get(op->ptr, "vertices"), RNA_float_get(op->ptr, "radius"), @@ -427,7 +423,7 @@ static int add_primitive_cone_exec(bContext *C, wmOperator *op) op, "verts.out", false, - "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b " + "create_cone segments=%i radius1=%f radius2=%f cap_ends=%b " "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b", RNA_int_get(op->ptr, "vertices"), RNA_float_get(op->ptr, "radius1"), @@ -642,7 +638,7 @@ static int add_primitive_uvsphere_exec(bContext *C, wmOperator *op) op, "verts.out", false, - "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b", + "create_uvsphere u_segments=%i v_segments=%i radius=%f matrix=%m4 calc_uvs=%b", RNA_int_get(op->ptr, "segments"), RNA_int_get(op->ptr, "ring_count"), RNA_float_get(op->ptr, "radius"), @@ -710,7 +706,7 @@ static int add_primitive_icosphere_exec(bContext *C, wmOperator *op) op, "verts.out", false, - "create_icosphere subdivisions=%i diameter=%f matrix=%m4 calc_uvs=%b", + "create_icosphere subdivisions=%i radius=%f matrix=%m4 calc_uvs=%b", RNA_int_get(op->ptr, "subdivisions"), RNA_float_get(op->ptr, "radius"), creation_data.mat, diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 01736f2919a..0d74187b50e 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -97,7 +97,6 @@ typedef struct { int launch_event; float mcenter[2]; void *draw_handle_pixel; - short gizmo_flag; short value_mode; /* Which value does mouse movement and numeric input affect? */ float segments; /* Segments as float so smooth mouse pan works in small increments */ @@ -307,11 +306,6 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) opdata->draw_handle_pixel = ED_region_draw_cb_activate( region->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); G.moving = G_TRANSFORM_EDIT; - - if (v3d) { - opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag = V3D_GIZMO_HIDE; - } } return true; @@ -433,15 +427,11 @@ static void edbm_bevel_exit(bContext *C, wmOperator *op) } if (opdata->is_modal) { - View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup); } ED_region_draw_cb_exit(region->type, opdata->draw_handle_pixel); - if (v3d) { - v3d->gizmo_flag = opdata->gizmo_flag; - } G.moving = 0; } MEM_SAFE_FREE(opdata->ob_store); diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 3c8afe8e7db..27a1bf9658f 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -72,7 +72,6 @@ typedef struct { bool is_dirty; } * backup; int backup_len; - short gizmo_flag; } BisectData; static void mesh_bisect_interactive_calc(bContext *C, @@ -88,6 +87,7 @@ static void mesh_bisect_interactive_calc(bContext *C, int y_start = RNA_int_get(op->ptr, "ystart"); int x_end = RNA_int_get(op->ptr, "xend"); int y_end = RNA_int_get(op->ptr, "yend"); + const bool use_flip = RNA_boolean_get(op->ptr, "flip"); /* reference location (some point in front of the view) for finding a point on a plane */ const float *co_ref = rv3d->ofs; @@ -105,6 +105,9 @@ static void mesh_bisect_interactive_calc(bContext *C, /* cross both to get a normal */ cross_v3_v3v3(plane_no, co_a, co_b); normalize_v3(plane_no); /* not needed but nicer for user */ + if (use_flip) { + negate_v3(plane_no); + } /* point on plane, can use either start or endpoint */ ED_view3d_win_to_3d(v3d, region, co_ref, co_a_ss, plane_co); @@ -116,7 +119,7 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) int valid_objects = 0; /* If the properties are set or there is no rv3d, - * skip model and exec immediately. */ + * skip modal and exec immediately. */ if ((CTX_wm_region_view3d(C) == NULL) || (RNA_struct_property_is_set(op->ptr, "plane_co") && RNA_struct_property_is_set(op->ptr, "plane_no"))) { return mesh_bisect_exec(C, op); @@ -140,10 +143,19 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - int ret = WM_gesture_straightline_invoke(C, op, event); - if (ret & OPERATOR_RUNNING_MODAL) { - View3D *v3d = CTX_wm_view3d(C); + /* Support flipping if side matters. */ + int ret; + const bool clear_inner = RNA_boolean_get(op->ptr, "clear_inner"); + const bool clear_outer = RNA_boolean_get(op->ptr, "clear_outer"); + const bool use_fill = RNA_boolean_get(op->ptr, "use_fill"); + if ((clear_inner != clear_outer) || use_fill) { + ret = WM_gesture_straightline_active_side_invoke(C, op, event); + } + else { + ret = WM_gesture_straightline_invoke(C, op, event); + } + if (ret & OPERATOR_RUNNING_MODAL) { wmGesture *gesture = op->customdata; BisectData *opdata; @@ -166,8 +178,6 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* Misc other vars. */ G.moving = G_TRANSFORM_EDIT; - opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag = V3D_GIZMO_HIDE; /* Initialize modal callout. */ ED_workspace_status_text(C, TIP_("LMB: Click and drag to draw cut line")); @@ -176,10 +186,8 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) return ret; } -static void edbm_bisect_exit(bContext *C, BisectData *opdata) +static void edbm_bisect_exit(BisectData *opdata) { - View3D *v3d = CTX_wm_view3d(C); - v3d->gizmo_flag = opdata->gizmo_flag; G.moving = 0; for (int ob_index = 0; ob_index < opdata->backup_len; ob_index++) { @@ -210,7 +218,7 @@ static int mesh_bisect_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (ret & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) { - edbm_bisect_exit(C, &opdata_back); + edbm_bisect_exit(&opdata_back); #ifdef USE_GIZMO /* Setup gizmos */ @@ -769,7 +777,7 @@ static void MESH_GGT_bisect(struct wmGizmoGroupType *gzgt) gzgt->name = "Mesh Bisect"; gzgt->idname = "MESH_GGT_bisect"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 907881a44f3..e4cd48d95bb 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -925,7 +925,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot) { /* identifiers */ - ot->name = "Duplicate or Extrude to Cursor"; + ot->name = "Extrude to Cursor or Add"; ot->idname = "MESH_OT_dupli_extrude_cursor"; ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor"; @@ -935,7 +935,7 @@ void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot) ot->poll = ED_operator_editmesh_region_view3d; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; RNA_def_boolean(ot->srna, "rotate_source", diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index 18f51ae9df2..7be169f70f4 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -76,7 +76,6 @@ typedef struct { int launch_event; float mcenter[2]; void *draw_handle_pixel; - short gizmo_flag; } InsetData; static void edbm_inset_update_header(wmOperator *op, bContext *C) @@ -177,7 +176,6 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal) opdata->num_input.unit_type[1] = B_UNIT_LENGTH; if (is_modal) { - View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { @@ -189,10 +187,6 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal) opdata->draw_handle_pixel = ED_region_draw_cb_activate( region->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); G.moving = G_TRANSFORM_EDIT; - if (v3d) { - opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag = V3D_GIZMO_HIDE; - } } return true; @@ -206,15 +200,11 @@ static void edbm_inset_exit(bContext *C, wmOperator *op) opdata = op->customdata; if (opdata->is_modal) { - View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup); } ED_region_draw_cb_exit(region->type, opdata->draw_handle_pixel); - if (v3d) { - v3d->gizmo_flag = opdata->gizmo_flag; - } G.moving = 0; } diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index 5a2a090b725..d1df063d9d0 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -1085,7 +1085,7 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) EDBM_update(obedit->data, &(const struct EDBMUpdate_Params){ .calc_looptri = true, - .calc_normals = false, + .calc_normals = true, .is_destructive = true, }); } @@ -1124,7 +1124,7 @@ void MESH_OT_rip(wmOperatorType *ot) ot->poll = EDBM_view3d_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* to give to transform */ Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c index f7e88284d93..ce49f0f80a3 100644 --- a/source/blender/editors/mesh/editmesh_rip_edge.c +++ b/source/blender/editors/mesh/editmesh_rip_edge.c @@ -249,7 +249,7 @@ void MESH_OT_rip_edge(wmOperatorType *ot) ot->poll = EDBM_view3d_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* to give to transform */ Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 8e38d41f971..2fcf8fa6f8f 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -4871,8 +4871,15 @@ static int edbm_select_axis_exec(bContext *C, wmOperator *op) float axis_mat[3][3]; /* 3D view variables may be NULL, (no need to check in poll function). */ - ED_transform_calc_orientation_from_type_ex( - C, axis_mat, scene, CTX_wm_region_view3d(C), obedit, obedit, orientation, V3D_AROUND_ACTIVE); + ED_transform_calc_orientation_from_type_ex(scene, + view_layer, + CTX_wm_view3d(C), + CTX_wm_region_view3d(C), + obedit, + obedit, + orientation, + V3D_AROUND_ACTIVE, + axis_mat); const float *axis_vector = axis_mat[axis]; diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 956658bd2b7..122214b87d5 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4323,7 +4323,7 @@ void MESH_OT_knife_cut(wmOperatorType *ot) ot->poll = EDBM_view3d_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ PropertyRNA *prop; diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 27fb21e1dfb..1b720f2c14d 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -424,6 +424,11 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* remove tessface to ensure we don't hold references to invalid faces */ BKE_mesh_tessface_clear(me); + /* Clear any run-time data. + * Even though this mesh wont typically have run-time data, the Python API can for e.g. + * create loop-triangle cache here, which is confusing when left in the mesh, see: T90798. */ + BKE_mesh_runtime_clear_geometry(me); + /* new material indices and material array */ if (totmat) { matar = MEM_callocN(sizeof(*matar) * totmat, "join_mesh matar"); diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 18f2b58eb65..040b5cd5066 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -37,6 +37,9 @@ set(INC ../../../../intern/clog ../../../../intern/glew-mx ../../../../intern/guardedalloc + + # dna_type_offsets.h in BLO_read_write.h + ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern ) set(SRC @@ -93,3 +96,5 @@ if(WITH_EXPERIMENTAL_FEATURES) endif() blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +add_dependencies(bf_editor_object bf_dna) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 2e34284f46e..beadbf2689e 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -332,8 +332,12 @@ void ED_object_base_init_transform_on_add(Object *object, const float loc[3], co /* Uses context to figure out transform for primitive. * Returns standard diameter. */ -float ED_object_new_primitive_matrix( - bContext *C, Object *obedit, const float loc[3], const float rot[3], float r_primmat[4][4]) +float ED_object_new_primitive_matrix(bContext *C, + Object *obedit, + const float loc[3], + const float rot[3], + const float scale[3], + float r_primmat[4][4]) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); @@ -356,6 +360,10 @@ float ED_object_new_primitive_matrix( invert_m3_m3(imat, mat); mul_m3_v3(imat, r_primmat[3]); + if (scale != NULL) { + rescale_m4(r_primmat, scale); + } + { const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL); @@ -863,7 +871,7 @@ static int effector_add_exec(bContext *C, wmOperator *op) ED_object_editmode_enter_ex(bmain, scene, ob, 0); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat); mul_mat3_m4_fl(mat, dia); BLI_addtail(&cu->editnurb->nurbs, ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1)); @@ -999,7 +1007,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) } float mat[4][4]; - ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); + ED_object_new_primitive_matrix(C, obedit, loc, rot, NULL, mat); /* Halving here is done to account for constant values from #BKE_mball_element_add. * While the default radius of the resulting meta element is 2, * we want to pass in 1 so other values such as resolution are scaled by 1.0. */ @@ -1365,30 +1373,28 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) case GP_EMPTY: { float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat); ED_gpencil_create_blank(C, ob, mat); break; } case GP_STROKE: { float radius = RNA_float_get(op->ptr, "radius"); + float scale[3]; + copy_v3_fl(scale, radius); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); - mul_v3_fl(mat[0], radius); - mul_v3_fl(mat[1], radius); - mul_v3_fl(mat[2], radius); + ED_object_new_primitive_matrix(C, ob, loc, rot, scale, mat); ED_gpencil_create_stroke(C, ob, mat); break; } case GP_MONKEY: { float radius = RNA_float_get(op->ptr, "radius"); + float scale[3]; + copy_v3_fl(scale, radius); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); - mul_v3_fl(mat[0], radius); - mul_v3_fl(mat[1], radius); - mul_v3_fl(mat[2], radius); + ED_object_new_primitive_matrix(C, ob, loc, rot, scale, mat); ED_gpencil_create_monkey(C, ob, mat); break; @@ -1397,12 +1403,11 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) case GP_LRT_COLLECTION: case GP_LRT_OBJECT: { float radius = RNA_float_get(op->ptr, "radius"); + float scale[3]; + copy_v3_fl(scale, radius); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); - mul_v3_fl(mat[0], radius); - mul_v3_fl(mat[1], radius); - mul_v3_fl(mat[2], radius); + ED_object_new_primitive_matrix(C, ob, loc, rot, scale, mat); ED_gpencil_create_lineart(C, ob); @@ -2246,13 +2251,6 @@ static bool dupliobject_instancer_cmp(const void *a_, const void *b_) return false; } -static bool object_has_geometry_set_instances(const Object *object_eval) -{ - struct GeometrySet *geometry_set = object_eval->runtime.geometry_set_eval; - - return (geometry_set != NULL) && BKE_geometry_set_has_instances(geometry_set); -} - static void make_object_duplilist_real(bContext *C, Depsgraph *depsgraph, Scene *scene, @@ -2266,7 +2264,8 @@ static void make_object_duplilist_real(bContext *C, Object *object_eval = DEG_get_evaluated_object(depsgraph, base->object); - if (!(base->object->transflag & OB_DUPLI) && !object_has_geometry_set_instances(object_eval)) { + if (!(base->object->transflag & OB_DUPLI) && + !BKE_object_has_geometry_set_instances(object_eval)) { return; } diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index e0419e0a4cc..8702b18a46f 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1786,7 +1786,8 @@ static bool constraint_copy_to_selected_poll(bContext *C) if (pchan) { bool found = false; - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, UNUSED(ob)) { + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { + UNUSED_VARS(ob); if (pchan != chan) { /** NOTE: Can not return here, because CTX_DATA_BEGIN_WITH_ID allocated * a list that needs to be freed by CTX_DATA_END. */ @@ -1862,6 +1863,7 @@ static int constraint_move_down_exec(bContext *C, wmOperator *op) BLI_remlink(conlist, con); BLI_insertlinkafter(conlist, nextCon, con); + ED_object_constraint_update(CTX_data_main(C), ob); WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob); return OPERATOR_FINISHED; @@ -1917,6 +1919,7 @@ static int constraint_move_up_exec(bContext *C, wmOperator *op) BLI_remlink(conlist, con); BLI_insertlinkbefore(conlist, prevCon, con); + ED_object_constraint_update(CTX_data_main(C), ob); WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob); return OPERATOR_FINISHED; @@ -1970,6 +1973,8 @@ static int constraint_move_to_index_exec(bContext *C, wmOperator *op) if (con) { ED_object_constraint_move_to_index(ob, con, new_index); + ED_object_constraint_update(CTX_data_main(C), ob); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c index 3995728c428..e3c2932e17a 100644 --- a/source/blender/editors/object/object_gpencil_modifier.c +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -28,6 +28,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" #include "DNA_object_types.h" @@ -35,6 +36,7 @@ #include "BLI_listbase.h" #include "BLI_string_utf8.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -55,6 +57,8 @@ #include "ED_object.h" #include "ED_screen.h" +#include "BLT_translation.h" + #include "UI_interface.h" #include "WM_api.h" @@ -939,3 +943,237 @@ void OBJECT_OT_gpencil_modifier_copy_to_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; gpencil_edit_modifier_properties(ot); } + +/************************* Dash Modifier *******************************/ + +static bool dash_segment_poll(bContext *C) +{ + return gpencil_edit_modifier_poll_generic(C, &RNA_DashGpencilModifierData, 0, false); +} + +static bool dash_segment_name_exists_fn(void *arg, const char *name) +{ + const DashGpencilModifierData *dmd = (const DashGpencilModifierData *)arg; + for (int i = 0; i < dmd->segments_len; i++) { + if (STREQ(dmd->segments[i].name, name)) { + return true; + } + } + return false; +} + +static int dash_segment_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + DashGpencilModifierData *dmd = (DashGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Dash); + + const int new_active_index = dmd->segment_active_index + 1; + DashGpencilModifierSegment *new_segments = MEM_malloc_arrayN( + dmd->segments_len + 1, sizeof(DashGpencilModifierSegment), __func__); + + if (dmd->segments_len != 0) { + /* Copy the segments before the new segment. */ + memcpy(new_segments, dmd->segments, sizeof(DashGpencilModifierSegment) * new_active_index); + /* Copy the segments after the new segment. */ + memcpy(new_segments + new_active_index + 1, + dmd->segments + new_active_index, + sizeof(DashGpencilModifierSegment) * (dmd->segments_len - new_active_index)); + } + + /* Create the new segment. */ + DashGpencilModifierSegment *ds = &new_segments[new_active_index]; + memcpy( + ds, DNA_struct_default_get(DashGpencilModifierSegment), sizeof(DashGpencilModifierSegment)); + BLI_uniquename_cb( + dash_segment_name_exists_fn, dmd, DATA_("Segment"), '.', ds->name, sizeof(ds->name)); + ds->dmd = dmd; + + MEM_SAFE_FREE(dmd->segments); + dmd->segments = new_segments; + dmd->segments_len++; + dmd->segment_active_index++; + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int dash_segment_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return dash_segment_add_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_segment_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Segment"; + ot->description = "Add a segment to the dash modifier"; + ot->idname = "GPENCIL_OT_segment_add"; + + /* api callbacks */ + ot->poll = dash_segment_poll; + ot->invoke = dash_segment_add_invoke; + ot->exec = dash_segment_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +static int dash_segment_remove_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + + DashGpencilModifierData *dmd = (DashGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Dash); + + if (dmd->segment_active_index < 0 || dmd->segment_active_index >= dmd->segments_len) { + return OPERATOR_CANCELLED; + } + + if (dmd->segments_len == 1) { + MEM_SAFE_FREE(dmd->segments); + dmd->segment_active_index = -1; + } + else { + DashGpencilModifierSegment *new_segments = MEM_malloc_arrayN( + dmd->segments_len, sizeof(DashGpencilModifierSegment), __func__); + + /* Copy the segments before the deleted segment. */ + memcpy(new_segments, + dmd->segments, + sizeof(DashGpencilModifierSegment) * dmd->segment_active_index); + + /* Copy the segments after the deleted segment. */ + memcpy(new_segments + dmd->segment_active_index, + dmd->segments + dmd->segment_active_index + 1, + sizeof(DashGpencilModifierSegment) * + (dmd->segments_len - dmd->segment_active_index - 1)); + + MEM_freeN(dmd->segments); + dmd->segments = new_segments; + dmd->segment_active_index = MAX2(dmd->segment_active_index - 1, 0); + } + + dmd->segments_len--; + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int dash_segment_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return dash_segment_remove_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_segment_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Dash Segment"; + ot->description = "Remove the active segment from the dash modifier"; + ot->idname = "GPENCIL_OT_segment_remove"; + + /* api callbacks */ + ot->poll = dash_segment_poll; + ot->invoke = dash_segment_remove_invoke; + ot->exec = dash_segment_remove_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + RNA_def_int( + ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX); +} + +enum { + GP_SEGEMENT_MOVE_UP = -1, + GP_SEGEMENT_MOVE_DOWN = 1, +}; + +static int dash_segment_move_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + + DashGpencilModifierData *dmd = (DashGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Dash); + + if (dmd->segments_len < 2) { + return OPERATOR_CANCELLED; + } + + const int direction = RNA_enum_get(op->ptr, "type"); + if (direction == GP_SEGEMENT_MOVE_UP) { + if (dmd->segment_active_index == 0) { + return OPERATOR_CANCELLED; + } + + SWAP(DashGpencilModifierSegment, + dmd->segments[dmd->segment_active_index], + dmd->segments[dmd->segment_active_index - 1]); + + dmd->segment_active_index--; + } + else if (direction == GP_SEGEMENT_MOVE_DOWN) { + if (dmd->segment_active_index == dmd->segments_len - 1) { + return OPERATOR_CANCELLED; + } + + SWAP(DashGpencilModifierSegment, + dmd->segments[dmd->segment_active_index], + dmd->segments[dmd->segment_active_index + 1]); + + dmd->segment_active_index++; + } + else { + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int dash_segment_move_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return dash_segment_move_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_segment_move(wmOperatorType *ot) +{ + static const EnumPropertyItem segment_move[] = { + {GP_SEGEMENT_MOVE_UP, "UP", 0, "Up", ""}, + {GP_SEGEMENT_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Dash Segment"; + ot->description = "Move the active dash segment up or down"; + ot->idname = "GPENCIL_OT_segment_move"; + + /* api callbacks */ + ot->poll = dash_segment_poll; + ot->invoke = dash_segment_move_invoke; + ot->exec = dash_segment_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + ot->prop = RNA_def_enum(ot->srna, "type", segment_move, 0, "Type", ""); +} diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 10e016738d0..50dd9322c5c 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -191,6 +191,7 @@ void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot); void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot); void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot); void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot); +void OBJECT_OT_geometry_nodes_input_attribute_toggle(struct wmOperatorType *ot); /* object_gpencil_modifiers.c */ void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot); @@ -202,6 +203,10 @@ void OBJECT_OT_gpencil_modifier_apply(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_copy_to_selected(struct wmOperatorType *ot); +void GPENCIL_OT_segment_add(struct wmOperatorType *ot); +void GPENCIL_OT_segment_remove(struct wmOperatorType *ot); +void GPENCIL_OT_segment_move(struct wmOperatorType *ot); + /* object_shader_fx.c */ void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot); void OBJECT_OT_shaderfx_copy(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index e1e0a0600be..b9942bc563a 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -3100,7 +3100,7 @@ void OBJECT_OT_ocean_bake(wmOperatorType *ot) /** \} */ /* ------------------------------------------------------------------- */ -/** \name Laplaciandeform Bind Operator +/** \name Laplacian-Deform Bind Operator * \{ */ static bool laplaciandeform_poll(bContext *C) @@ -3242,3 +3242,54 @@ void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot) } /** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Toggle Value or Attribute Operator + * + * \note This operator basically only exists to provide a better tooltip for the toggle button, + * since it is stored as an IDProperty. It also stops the button from being highlighted when + * "use_attribute" is on, which isn't expected. + * \{ */ + +static int geometry_nodes_input_attribute_toggle_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + + char modifier_name[MAX_NAME]; + RNA_string_get(op->ptr, "modifier_name", modifier_name); + NodesModifierData *nmd = (NodesModifierData *)BKE_modifiers_findby_name(ob, modifier_name); + if (nmd == NULL) { + return OPERATOR_CANCELLED; + } + + char prop_path[MAX_NAME]; + RNA_string_get(op->ptr, "prop_path", prop_path); + + PointerRNA mod_ptr; + RNA_pointer_create(&ob->id, &RNA_Modifier, nmd, &mod_ptr); + + const int old_value = RNA_int_get(&mod_ptr, prop_path); + const int new_value = !old_value; + RNA_int_set(&mod_ptr, prop_path, new_value); + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + return OPERATOR_FINISHED; +} + +void OBJECT_OT_geometry_nodes_input_attribute_toggle(wmOperatorType *ot) +{ + ot->name = "Input Attribute Toggle"; + ot->description = + "Switch between an attribute and a single value to define the data for every element"; + ot->idname = "OBJECT_OT_geometry_nodes_input_attribute_toggle"; + + ot->exec = geometry_nodes_input_attribute_toggle_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string(ot->srna, "prop_path", NULL, 0, "Prop Path", ""); + RNA_def_string(ot->srna, "modifier_name", NULL, MAX_NAME, "Modifier Name", ""); +} + +/** \} */ diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index c1928cf7f8a..aa9ae082317 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -145,6 +145,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_skin_loose_mark_clear); WM_operatortype_append(OBJECT_OT_skin_radii_equalize); WM_operatortype_append(OBJECT_OT_skin_armature_create); + WM_operatortype_append(OBJECT_OT_geometry_nodes_input_attribute_toggle); /* grease pencil modifiers */ WM_operatortype_append(OBJECT_OT_gpencil_modifier_add); @@ -156,6 +157,10 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy); WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy_to_selected); + WM_operatortype_append(GPENCIL_OT_segment_add); + WM_operatortype_append(GPENCIL_OT_segment_remove); + WM_operatortype_append(GPENCIL_OT_segment_move); + /* grease pencil line art */ WM_operatortypes_lineart(); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 699efd9ce75..2d535b12b08 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -87,6 +87,7 @@ #include "BKE_modifier.h" #include "BKE_node.h" #include "BKE_object.h" +#include "BKE_paint.h" #include "BKE_pointcloud.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -94,7 +95,6 @@ #include "BKE_speaker.h" #include "BKE_texture.h" #include "BKE_volume.h" -#include "BKE_paint.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -2734,90 +2734,94 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, PointerRNA *properties, const wmEvent *event) { - Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); + int mat_slot = 0; + Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); if (ob == NULL) { return BLI_strdup(""); } + mat_slot = max_ii(mat_slot, 1); char name[MAX_ID_NAME - 2]; RNA_string_get(properties, "name", name); - int active_mat_slot = max_ii(ob->actcol, 1); - Material *prev_mat = BKE_object_material_get(ob, active_mat_slot); + Material *prev_mat = BKE_object_material_get(ob, mat_slot); char *result; if (prev_mat) { const char *tooltip = TIP_("Drop %s on %s (slot %d, replacing %s)"); - result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot, prev_mat->id.name + 2); + result = BLI_sprintfN(tooltip, name, ob->id.name + 2, mat_slot, prev_mat->id.name + 2); } else { const char *tooltip = TIP_("Drop %s on %s (slot %d)"); - result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot); + result = BLI_sprintfN(tooltip, name, ob->id.name + 2, mat_slot); } return result; } -static void drop_named_material_face_set_slots_update(bContext *C, Object *ob, const wmEvent *event) { - Main *bmain = CTX_data_main(C); - Mesh *mesh = BKE_mesh_from_object(ob); +static void drop_named_material_face_set_slots_update(bContext *C, + Object *ob, + const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + Mesh *mesh = BKE_mesh_from_object(ob); - bScreen *screen = CTX_wm_screen(C); - ARegion *region = BKE_screen_find_main_region_at_xy( - screen, SPACE_VIEW3D, event->x, event->y); + bScreen *screen = CTX_wm_screen(C); + ARegion *region = BKE_screen_find_main_region_at_xy(screen, SPACE_VIEW3D, event->x, event->y); - const float mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; - const int face_set_id = ED_sculpt_face_sets_active_update_and_get(C, ob, mval); + const float mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; + const int face_set_id = ED_sculpt_face_sets_active_update_and_get(C, ob, mval); - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); - short face_set_nr = -1; - for (int i = 0; i < mesh->totpoly; i++) { - if (face_sets[i] != face_set_id) { - continue; - } - face_set_nr = mesh->mpoly[i].mat_nr; - break; + short face_set_nr = -1; + for (int i = 0; i < mesh->totpoly; i++) { + if (face_sets[i] != face_set_id) { + continue; } + face_set_nr = mesh->mpoly[i].mat_nr; + break; + } - bool create_new_slot = false; - for (int i = 0; i < mesh->totpoly; i++) { - if (face_sets[i] == face_set_id) { - if (mesh->mpoly[i].mat_nr != face_set_nr) { - create_new_slot = true; - break; - } - } - else { - if (mesh->mpoly[i].mat_nr == face_set_nr) { - create_new_slot = true; - break; - } + bool create_new_slot = false; + for (int i = 0; i < mesh->totpoly; i++) { + if (face_sets[i] == face_set_id) { + if (mesh->mpoly[i].mat_nr != face_set_nr) { + create_new_slot = true; + break; } } - - - if (create_new_slot) { - BKE_object_material_slot_add(bmain, ob); - } else { - ob->actcol = face_set_nr + 1; + if (mesh->mpoly[i].mat_nr == face_set_nr) { + create_new_slot = true; + break; + } } + } - const short active_mat_slot = ob->actcol; - const short material_nr = active_mat_slot - 1; - for (int i = 0; i < mesh->totpoly; i++) { - if (face_sets[i] != face_set_id) { - continue; - } - mesh->mpoly[i].mat_nr = material_nr; - } + if (create_new_slot) { + BKE_object_material_slot_add(bmain, ob); + } + else { + ob->actcol = face_set_nr + 1; + } + const short active_mat_slot = ob->actcol; + const short material_nr = active_mat_slot - 1; + for (int i = 0; i < mesh->totpoly; i++) { + if (face_sets[i] != face_set_id) { + continue; + } + mesh->mpoly[i].mat_nr = material_nr; + } } static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Main *bmain = CTX_data_main(C); - Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); + int mat_slot = 0; + Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); + mat_slot = max_ii(mat_slot, 1); + Material *ma; char name[MAX_ID_NAME - 2]; @@ -2831,10 +2835,7 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent drop_named_material_face_set_slots_update(C, ob, event); } - const short active_mat_slot = ob->actcol; - - BKE_object_material_assign( - CTX_data_main(C), ob, ma, active_mat_slot, BKE_MAT_ASSIGN_USERPREF); + BKE_object_material_assign(CTX_data_main(C), ob, ma, mat_slot, BKE_MAT_ASSIGN_USERPREF); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index e08a4e946f6..9546035375c 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -882,7 +882,7 @@ static void area_azone_init(wmWindow *win, const bScreen *screen, ScrArea *area) return; } - if (U.app_flag & USER_APP_LOCK_UI_LAYOUT) { + if (U.app_flag & USER_APP_LOCK_CORNER_SPLIT) { return; } @@ -1058,6 +1058,14 @@ static bool region_azone_edge_poll(const ARegion *region, const bool is_fullscre return false; } + if (is_hidden && (U.app_flag & USER_APP_HIDE_REGION_TOGGLE)) { + return false; + } + + if (!is_hidden && (U.app_flag & USER_APP_LOCK_EDGE_RESIZE)) { + return false; + } + return true; } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index b0181de96a0..2ccefb993c7 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -56,6 +56,7 @@ #include "ED_armature.h" #include "ED_gpencil.h" +#include "SEQ_select.h" #include "SEQ_sequencer.h" #include "UI_interface.h" @@ -91,10 +92,13 @@ const char *screen_context_dir[] = { "image_paint_object", "particle_edit_object", "pose_object", + "active_sequence_strip", "sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */ - "selected_nla_strips", /* nla editor */ + "active_nla_track", + "active_nla_strip", + "selected_nla_strips", /* nla editor */ "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "annotation_data", @@ -603,11 +607,23 @@ static eContextResult screen_ctx_pose_object(const bContext *C, bContextDataResu } return CTX_RESULT_OK; } +static eContextResult screen_ctx_active_sequence_strip(const bContext *C, + bContextDataResult *result) +{ + wmWindow *win = CTX_wm_window(C); + Scene *scene = WM_window_get_active_scene(win); + Sequence *seq = SEQ_select_active_get(scene); + if (seq) { + CTX_data_pointer_set(result, &scene->id, &RNA_Sequence, seq); + return CTX_RESULT_OK; + } + return CTX_RESULT_NO_DATA; +} static eContextResult screen_ctx_sequences(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); @@ -621,7 +637,7 @@ static eContextResult screen_ctx_selected_sequences(const bContext *C, bContextD { wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { if (seq->flag & SELECT) { @@ -638,7 +654,7 @@ static eContextResult screen_ctx_selected_editable_sequences(const bContext *C, { wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { if (seq->flag & SELECT && !(seq->flag & SEQ_LOCK)) { @@ -650,6 +666,24 @@ static eContextResult screen_ctx_selected_editable_sequences(const bContext *C, } return CTX_RESULT_NO_DATA; } +static eContextResult screen_ctx_active_nla_track(const bContext *C, bContextDataResult *result) +{ + PointerRNA ptr; + if (ANIM_nla_context_track_ptr(C, &ptr)) { + CTX_data_pointer_set_ptr(result, &ptr); + return CTX_RESULT_OK; + } + return CTX_RESULT_NO_DATA; +} +static eContextResult screen_ctx_active_nla_strip(const bContext *C, bContextDataResult *result) +{ + PointerRNA ptr; + if (ANIM_nla_context_strip_ptr(C, &ptr)) { + CTX_data_pointer_set_ptr(result, &ptr); + return CTX_RESULT_OK; + } + return CTX_RESULT_NO_DATA; +} static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContextDataResult *result) { bAnimContext ac; @@ -707,7 +741,7 @@ static eContextResult screen_ctx_gpencil_data_owner(const bContext *C, bContextD bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(area, obact, &ptr); if (gpd_ptr) { - CTX_data_pointer_set(result, ptr.owner_id, ptr.type, ptr.data); + CTX_data_pointer_set_ptr(result, &ptr); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; @@ -739,7 +773,7 @@ static eContextResult screen_ctx_annotation_data_owner(const bContext *C, bGPdata **gpd_ptr = ED_annotation_data_get_pointers_direct((ID *)screen, area, scene, &ptr); if (gpd_ptr) { - CTX_data_pointer_set(result, ptr.owner_id, ptr.type, ptr.data); + CTX_data_pointer_set_ptr(result, &ptr); return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; @@ -1097,9 +1131,12 @@ static void ensure_ed_screen_context_functions(void) register_context_function("image_paint_object", screen_ctx_image_paint_object); register_context_function("particle_edit_object", screen_ctx_particle_edit_object); register_context_function("pose_object", screen_ctx_pose_object); + register_context_function("active_sequence_strip", screen_ctx_active_sequence_strip); register_context_function("sequences", screen_ctx_sequences); register_context_function("selected_sequences", screen_ctx_selected_sequences); register_context_function("selected_editable_sequences", screen_ctx_selected_editable_sequences); + register_context_function("active_nla_track", screen_ctx_active_nla_track); + register_context_function("active_nla_strip", screen_ctx_active_nla_strip); register_context_function("selected_nla_strips", screen_ctx_selected_nla_strips); register_context_function("gpencil_data", screen_ctx_gpencil_data); register_context_function("gpencil_data_owner", screen_ctx_gpencil_data_owner); diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c index 51edad0332b..e67c933cb8e 100644 --- a/source/blender/editors/screen/screen_geometry.c +++ b/source/blender/editors/screen/screen_geometry.c @@ -130,6 +130,10 @@ ScrEdge *screen_geom_find_active_scredge(const wmWindow *win, const int mx, const int my) { + if (U.app_flag & USER_APP_LOCK_EDGE_RESIZE) { + return NULL; + } + /* Use layout size (screen excluding global areas) for screen-layout area edges */ rcti screen_rect; WM_window_screen_rect_calc(win, &screen_rect); diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 683f2844371..4016ef84bfd 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -128,6 +128,7 @@ extern const char *screen_context_dir[]; /* doc access */ /* screendump.c */ void SCREEN_OT_screenshot(struct wmOperatorType *ot); +void SCREEN_OT_screenshot_area(struct wmOperatorType *ot); /* workspace_layout_edit.c */ bool workspace_layout_set_poll(const struct WorkSpaceLayout *layout); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index daac196a90c..674a2deb929 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1134,14 +1134,22 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) if ((ED_area_actionzone_find_xy(sad->sa1, &event->x) != sad->az) && (screen_geom_area_map_find_active_scredge( AREAMAP_FROM_SCREEN(screen), &screen_rect, event->x, event->y) == NULL)) { - /* Are we still in same area? */ - if (BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y) == sad->sa1) { + + /* What area are we now in? */ + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + + if (area == sad->sa1) { /* Same area, so possible 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 if (!area || area->global) { + /* No area or Top bar or Status bar. */ + WM_cursor_set(win, WM_CURSOR_STOP); + is_gesture = false; + } else { /* Different area, so possible join. */ if (sad->gesture_dir == SCREEN_DIR_N) { @@ -1161,7 +1169,7 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else { - WM_cursor_set(CTX_wm_window(C), WM_CURSOR_CROSS); + WM_cursor_set(win, WM_CURSOR_CROSS); is_gesture = false; } } @@ -1466,15 +1474,39 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) * Close selected area, replace by expanding a neighbor * \{ */ -/* operator callback */ -static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +/** + * \note This can be used interactively or from Python. + * + * \note Most of the window management operators don't support execution from Python. + * An exception is made for closing areas since it allows application templates + * to customize the layout. + */ +static int area_close_exec(bContext *C, wmOperator *op) { + bScreen *screen = CTX_wm_screen(C); 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; + + /* This operator is scriptable, so the area passed could be invalid. */ + if (BLI_findindex(&screen->areabase, area) == -1) { + BKE_report(op->reports, RPT_ERROR, "Area not found in the active screen"); + return OPERATOR_CANCELLED; } - return OPERATOR_CANCELLED; + + if (!screen_area_close(C, screen, area)) { + BKE_report(op->reports, RPT_ERROR, "Unable to close area"); + return OPERATOR_CANCELLED; + } + + /* Ensure the event loop doesn't attempt to continue handling events. + * + * This causes execution from the Python console fail to return to the prompt as it should. + * This glitch could be solved in the event loop handling as other operators may also + * destructively manipulate windowing data. */ + CTX_wm_window_set(C, NULL); + + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } static bool area_close_poll(bContext *C) @@ -1506,7 +1538,7 @@ 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->exec = area_close_exec; ot->poll = area_close_poll; } @@ -3075,6 +3107,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) mask_to_keylist(&ads, masklay, keylist); } } + ED_keylist_prepare_for_direct_access(keylist); /* find matching keyframe in the right direction */ const ActKeyColumn *ak; @@ -5657,6 +5690,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_back_to_previous); WM_operatortype_append(SCREEN_OT_spacedata_cleanup); WM_operatortype_append(SCREEN_OT_screenshot); + WM_operatortype_append(SCREEN_OT_screenshot_area); WM_operatortype_append(SCREEN_OT_userpref_show); WM_operatortype_append(SCREEN_OT_drivers_editor_show); WM_operatortype_append(SCREEN_OT_info_log_show); diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 6df96b1e30f..8056e02d17a 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -42,6 +42,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_report.h" +#include "BKE_screen.h" #include "RNA_access.h" #include "RNA_define.h" @@ -57,12 +58,13 @@ typedef struct ScreenshotData { uint *dumprect; int dumpsx, dumpsy; rcti crop; + bool use_crop; ImageFormatData im_format; } ScreenshotData; /* call from both exec and invoke */ -static int screenshot_data_create(bContext *C, wmOperator *op) +static int screenshot_data_create(bContext *C, wmOperator *op, ScrArea *area) { int dumprect_size[2]; @@ -76,7 +78,6 @@ static int screenshot_data_create(bContext *C, wmOperator *op) if (dumprect) { ScreenshotData *scd = MEM_callocN(sizeof(ScreenshotData), "screenshot"); - ScrArea *area = CTX_wm_area(C); scd->dumpsx = dumprect_size[0]; scd->dumpsy = dumprect_size[1]; @@ -110,12 +111,13 @@ static void screenshot_data_free(wmOperator *op) static int screenshot_exec(bContext *C, wmOperator *op) { + const bool use_crop = STREQ(op->idname, "SCREEN_OT_screenshot_area"); ScreenshotData *scd = op->customdata; bool ok = false; if (scd == NULL) { /* when running exec directly */ - screenshot_data_create(C, op); + screenshot_data_create(C, op, use_crop ? CTX_wm_area(C) : NULL); scd = op->customdata; } @@ -132,7 +134,7 @@ static int screenshot_exec(bContext *C, wmOperator *op) ibuf->rect = scd->dumprect; /* crop to show only single editor */ - if (!RNA_boolean_get(op->ptr, "full")) { + if (use_crop) { IMB_rect_crop(ibuf, &scd->crop); scd->dumprect = ibuf->rect; } @@ -157,9 +159,20 @@ static int screenshot_exec(bContext *C, wmOperator *op) return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } -static int screenshot_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int screenshot_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if (screenshot_data_create(C, op)) { + const bool use_crop = STREQ(op->idname, "SCREEN_OT_screenshot_area"); + ScrArea *area = NULL; + if (use_crop) { + area = CTX_wm_area(C); + bScreen *screen = CTX_wm_screen(C); + ScrArea *area_test = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + if (area_test != NULL) { + area = area_test; + } + } + + if (screenshot_data_create(C, op, area)) { if (RNA_struct_property_is_set(op->ptr, "filepath")) { return screenshot_exec(C, op); } @@ -226,12 +239,8 @@ static bool screenshot_poll(bContext *C) return WM_operator_winactive(C); } -void SCREEN_OT_screenshot(wmOperatorType *ot) +static void screen_screenshot_impl(wmOperatorType *ot) { - ot->name = "Save Screenshot"; - ot->idname = "SCREEN_OT_screenshot"; - ot->description = "Capture a picture of the active area or whole Blender window"; - ot->invoke = screenshot_invoke; ot->check = screenshot_check; ot->exec = screenshot_exec; @@ -239,8 +248,6 @@ void SCREEN_OT_screenshot(wmOperatorType *ot) ot->ui = screenshot_draw; ot->poll = screenshot_poll; - ot->flag = 0; - WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, FILE_SPECIAL, @@ -248,9 +255,26 @@ void SCREEN_OT_screenshot(wmOperatorType *ot) WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - RNA_def_boolean(ot->srna, - "full", - 1, - "Full Screen", - "Capture the whole window (otherwise only capture the active area)"); +} + +void SCREEN_OT_screenshot(wmOperatorType *ot) +{ + ot->name = "Save Screenshot"; + ot->idname = "SCREEN_OT_screenshot"; + ot->description = "Capture a picture of the whole Blender window"; + + screen_screenshot_impl(ot); + + ot->flag = 0; +} + +void SCREEN_OT_screenshot_area(wmOperatorType *ot) +{ + ot->name = "Save Screenshot (Area)"; + ot->idname = "SCREEN_OT_screenshot_area"; + ot->description = "Capture a picture of the active area"; + + screen_screenshot_impl(ot); + + ot->flag = OPTYPE_DEPENDS_ON_CURSOR; } diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c index 31ca11bb847..0ec32da0404 100644 --- a/source/blender/editors/screen/workspace_layout_edit.c +++ b/source/blender/editors/screen/workspace_layout_edit.c @@ -140,7 +140,7 @@ bool ED_workspace_layout_delete(WorkSpace *workspace, WorkSpaceLayout *layout_ol BLI_assert(BLI_findindex(&workspace->layouts, layout_old) != -1); - /* don't allow deleting temp fullscreens for now */ + /* Don't allow deleting temp full-screens for now. */ if (BKE_screen_is_fullscreen_area(screen_old)) { return false; } diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 39d776e0054..b5113681955 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -1153,7 +1153,7 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob SpaceImage *sima = (SpaceImage *)sl; if (!sima->pin) { - ED_space_image_set(bmain, sima, NULL, ima, true); + ED_space_image_set(bmain, sima, ima, true); } } } diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index b8e0e2eac61..a0f4bac2e7c 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1342,7 +1342,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) }), sculpt_mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); BKE_mesh_nomain_to_mesh( result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true); } @@ -1896,7 +1896,7 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) ot->poll = SCULPT_mode_poll_view3d; - ot->flag = OPTYPE_REGISTER; + ot->flag = OPTYPE_REGISTER | OPTYPE_DEPENDS_ON_CURSOR; /* Properties. */ WM_operator_properties_gesture_lasso(ot); @@ -1959,6 +1959,8 @@ void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot) ot->poll = SCULPT_mode_poll_view3d; + ot->flag = OPTYPE_DEPENDS_ON_CURSOR; + /* Properties. */ WM_operator_properties_gesture_lasso(ot); sculpt_gesture_operator_properties(ot); @@ -1995,7 +1997,7 @@ void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot) ot->poll = SCULPT_mode_poll_view3d; - ot->flag = OPTYPE_REGISTER; + ot->flag = OPTYPE_REGISTER | OPTYPE_DEPENDS_ON_CURSOR; /* Properties. */ WM_operator_properties_gesture_lasso(ot); diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 6f4e295cbb2..3e38be243c9 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -192,7 +192,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const bGPDlayer *gpl = ale->data; bGPDframe *gpf; - /* find gp-frame which is less than or equal to cframe */ + /* Find gp-frame which is less than or equal to current-frame. */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { const float framenum = (float)gpf->framenum; *min = min_ff(*min, framenum); @@ -204,7 +204,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const MaskLayer *masklay = ale->data; MaskLayerShape *masklay_shape; - /* find mask layer which is less than or equal to cframe */ + /* Find mask layer which is less than or equal to current-frame. */ for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { const float framenum = (float)masklay_shape->frame; diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 9dcfc626a50..26d71178b68 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -162,6 +162,7 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, struct AnimKeylist *keylist = ED_keylist_create(); actkeys_list_element_to_keylist(ac, keylist, ale); + ED_keylist_prepare_for_direct_access(keylist); AnimData *adt = ANIM_nla_mapping_get(ac, ale); @@ -833,7 +834,7 @@ void ACTION_OT_select_lasso(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* flags */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ WM_operator_properties_gesture_lasso(ot); diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 5e5143723a6..f59429ba981 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -536,9 +536,9 @@ static void action_listener(const wmSpaceTypeListenerParams *params) saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC; ED_area_tag_refresh(area); } - /* autocolor only really needs to change when channels are added/removed, + /* Auto-color only really needs to change when channels are added/removed, * or previously hidden stuff appears - * (assume for now that if just adding these works, that will be fine) + * (assume for now that if just adding these works, that will be fine). */ else if (((wmn->data == ND_KEYFRAME) && ELEM(wmn->action, NA_ADDED, NA_REMOVED)) || ((wmn->data == ND_ANIMCHAN) && (wmn->action != NA_SELECTED))) { diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index bf855db07e9..91b0677ebaa 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -72,7 +72,7 @@ static int set_pointer_type(ButsContextPath *path, bContextDataResult *result, S PointerRNA *ptr = &path->ptr[i]; if (RNA_struct_is_a(ptr->type, type)) { - CTX_data_pointer_set(result, ptr->owner_id, ptr->type, ptr->data); + CTX_data_pointer_set_ptr(result, ptr); return CTX_RESULT_OK; } } @@ -1003,7 +1003,7 @@ int /*eContextResult*/ buttons_context(const bContext *C, if (ct->user && ct->user->ptr.data) { ButsTextureUser *user = ct->user; - CTX_data_pointer_set(result, user->ptr.owner_id, user->ptr.type, user->ptr.data); + CTX_data_pointer_set_ptr(result, &user->ptr); } return CTX_RESULT_OK; @@ -1092,7 +1092,7 @@ int /*eContextResult*/ buttons_context(const bContext *C, PointerRNA *ptr = get_pointer_type(path, &RNA_ParticleSettings); if (ptr && ptr->data) { - CTX_data_pointer_set(result, ptr->owner_id, &RNA_ParticleSettings, ptr->data); + CTX_data_pointer_set_ptr(result, ptr); return CTX_RESULT_OK; } diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index c7f0f4c228f..73a73eb7911 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -721,7 +721,7 @@ void CLIP_OT_select_lasso(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* flags */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ WM_operator_properties_gesture_lasso(ot); diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 17749d36418..905c0aeb8e0 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -92,6 +92,9 @@ void file_sfile_to_operator(struct Main *bmain, struct wmOperator *op, struct Sp void file_operator_to_sfile(struct Main *bmain, struct SpaceFile *sfile, struct wmOperator *op); +/* space_file.c */ +extern const char *file_context_dir[]; /* doc access */ + /* filesel.c */ void fileselect_refresh_params(struct SpaceFile *sfile); void fileselect_file_set(SpaceFile *sfile, const int index); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index c7d23943b6c..511b5b255e9 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1442,7 +1442,9 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat if (preview->in_memory_preview) { if (BKE_previewimg_is_finished(preview->in_memory_preview, ICON_SIZE_PREVIEW)) { ImBuf *imbuf = BKE_previewimg_to_imbuf(preview->in_memory_preview, ICON_SIZE_PREVIEW); - preview->icon_id = BKE_icon_imbuf_create(imbuf); + if (imbuf) { + preview->icon_id = BKE_icon_imbuf_create(imbuf); + } done = true; } } @@ -1953,7 +1955,9 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in if (entry->local_data.preview_image && BKE_previewimg_is_finished(entry->local_data.preview_image, ICON_SIZE_PREVIEW)) { ImBuf *ibuf = BKE_previewimg_to_imbuf(entry->local_data.preview_image, ICON_SIZE_PREVIEW); - ret->preview_icon_id = BKE_icon_imbuf_create(ibuf); + if (ibuf) { + ret->preview_icon_id = BKE_icon_imbuf_create(ibuf); + } } BLI_addtail(&cache->cached_entries, ret); return ret; diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 4ab7014cf82..11b06d2b414 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -135,7 +135,8 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR; base_params->display = FILE_IMGDISPLAY; base_params->sort = FILE_SORT_ALPHA; - base_params->recursion_level = 1; + /* Asset libraries include all sub-directories, so enable maximal recursion. */ + base_params->recursion_level = FILE_SELECT_MAX_RECURSIONS; /* 'SMALL' size by default. More reasonable since this is typically used as regular editor, * space is more of an issue here. */ base_params->thumbnail_size = 96; diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 7deaa2fec60..a4f36c2a6ee 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -871,8 +871,9 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C), RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items); } -static const char *file_context_dir[] = { +const char *file_context_dir[] = { "active_file", + "selected_files", "asset_library_ref", "id", NULL, @@ -907,6 +908,20 @@ static int /*eContextResult*/ file_context(const bContext *C, CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file); return CTX_RESULT_OK; } + if (CTX_data_equals(member, "selected_files")) { + const int num_files_filtered = filelist_files_ensure(sfile->files); + + for (int file_index = 0; file_index < num_files_filtered; file_index++) { + if (filelist_entry_is_selected(sfile->files, file_index)) { + FileDirEntry *entry = filelist_file(sfile->files, file_index); + CTX_data_list_add(result, &screen->id, &RNA_FileSelectEntry, entry); + } + } + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; + } + if (CTX_data_equals(member, "asset_library_ref")) { FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); if (!asset_params) { diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index a853efb1ace..ffe74e20bdf 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -1006,7 +1006,7 @@ void GRAPH_OT_select_lasso(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* Flags. */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* Properties. */ WM_operator_properties_gesture_lasso(ot); diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 10629caa8b0..f04336cab84 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -41,6 +41,7 @@ #include "ED_keyframes_edit.h" #include "ED_numinput.h" #include "ED_screen.h" +#include "ED_util.h" #include "WM_api.h" #include "WM_types.h" @@ -94,6 +95,8 @@ typedef struct tDecimateGraphOp { /** The original bezt curve data (used for restoring fcurves). */ ListBase bezt_arr_list; + struct tSlider *slider; + NumInput num; } tDecimateGraphOp; @@ -161,6 +164,8 @@ static void decimate_exit(bContext *C, wmOperator *op) ScrArea *area = dgo->area; LinkData *link; + ED_slider_destroy(C, dgo->slider); + for (link = dgo->bezt_arr_list.first; link != NULL; link = link->next) { tBeztCopyData *copy = link->data; MEM_freeN(copy->bezt); @@ -178,11 +183,14 @@ static void decimate_exit(bContext *C, wmOperator *op) op->customdata = NULL; } -/* Draw a percentage indicator in header. */ -static void decimate_draw_status_header(wmOperator *op, tDecimateGraphOp *dgo) +/* Draw a percentage indicator in workspace footer. */ +static void decimate_draw_status(bContext *C, tDecimateGraphOp *dgo) { char status_str[UI_MAX_DRAW_STR]; char mode_str[32]; + char slider_string[UI_MAX_DRAW_STR]; + + ED_slider_status_string_get(dgo->slider, slider_string, UI_MAX_DRAW_STR); strcpy(mode_str, TIP_("Decimate Keyframes")); @@ -194,23 +202,10 @@ static void decimate_draw_status_header(wmOperator *op, tDecimateGraphOp *dgo) BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs); } else { - float percentage = RNA_property_float_get(op->ptr, dgo->percentage_prop); - BLI_snprintf( - status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(percentage * 100.0f)); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string); } - ED_area_status_text(dgo->area, status_str); -} - -/* 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. - */ -static void decimate_mouse_update_percentage(tDecimateGraphOp *dgo, - wmOperator *op, - const wmEvent *event) -{ - float percentage = (event->x - dgo->region->winrct.xmin) / ((float)dgo->region->winx); - RNA_property_float_set(op->ptr, dgo->percentage_prop, percentage); + ED_workspace_status_text(C, status_str); } static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -234,10 +229,11 @@ static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent dgo->area = CTX_wm_area(C); dgo->region = CTX_wm_region(C); - /* Initialize percentage so that it will have the correct value before the first mouse move. */ - decimate_mouse_update_percentage(dgo, op, event); + dgo->slider = ED_slider_create(C); + ED_slider_init(dgo->slider, event); + ED_slider_allow_overshoot_set(dgo->slider, false); - decimate_draw_status_header(op, dgo); + decimate_draw_status(C, dgo); /* Construct a list with the original bezt arrays so we can restore them during modal operation. */ @@ -300,13 +296,14 @@ static void graphkeys_decimate_modal_update(bContext *C, wmOperator *op) * (e.g. pressing a key or moving the mouse). */ tDecimateGraphOp *dgo = op->customdata; - decimate_draw_status_header(op, dgo); + decimate_draw_status(C, dgo); /* Reset keyframe data (so we get back to the original state). */ decimate_reset_bezts(dgo); /* Apply... */ - float remove_ratio = RNA_property_float_get(op->ptr, dgo->percentage_prop); + float remove_ratio = ED_slider_factor_get(dgo->slider); + RNA_property_float_set(op->ptr, dgo->percentage_prop, remove_ratio); /* We don't want to limit the decimation to a certain error margin. */ const float error_sq_max = FLT_MAX; decimate_graph_keys(&dgo->ac, remove_ratio, error_sq_max); @@ -323,6 +320,8 @@ static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent * const bool has_numinput = hasNumInput(&dgo->num); + ED_slider_modal(dgo->slider, event); + switch (event->type) { case LEFTMOUSE: /* Confirm */ case EVT_RETKEY: @@ -353,9 +352,6 @@ static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent * case MOUSEMOVE: /* Calculate new position. */ { if (has_numinput == false) { - /* Update percentage based on position of mouse. */ - decimate_mouse_update_percentage(dgo, op, event); - /* Update pose to reflect the new values. */ graphkeys_decimate_modal_update(C, op); } diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 49966e880d3..720d69eaf4f 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -556,8 +556,8 @@ static void graph_listener(const wmSpaceTypeListenerParams *params) /* context changes */ switch (wmn->category) { case NC_ANIMATION: - /* for selection changes of animation data, we can just redraw... - * otherwise autocolor might need to be done again */ + /* For selection changes of animation data, we can just redraw... + * otherwise auto-color might need to be done again. */ if (ELEM(wmn->data, ND_KEYFRAME, ND_ANIMCHAN) && (wmn->action == NA_SELECTED)) { ED_area_tag_redraw(area); } diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index a95189a303f..2174a4b9dc1 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -57,7 +57,7 @@ Image *ED_space_image(SpaceImage *sima) return sima->image; } -void ED_space_image_set(Main *bmain, SpaceImage *sima, Object *obedit, Image *ima, bool automatic) +void ED_space_image_set(Main *bmain, SpaceImage *sima, Image *ima, bool automatic) { /* Automatically pin image when manually assigned, otherwise it follows object. */ if (!automatic && sima->image != ima && sima->mode == SI_MODE_UV) { @@ -78,10 +78,6 @@ void ED_space_image_set(Main *bmain, SpaceImage *sima, Object *obedit, Image *im id_us_ensure_real((ID *)sima->image); - if (obedit) { - WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data); - } - WM_main_add_notifier(NC_SPACE | ND_SPACE_IMAGE, NULL); } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 11efd3e33ef..94d44e047a4 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1311,7 +1311,6 @@ static int image_open_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); ScrArea *area = CTX_wm_area(C); Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); ImageUser *iuser = NULL; Image *ima = NULL; int frame_seq_len = 0; @@ -1364,7 +1363,7 @@ static int image_open_exec(bContext *C, wmOperator *op) } else if (area && area->spacetype == SPACE_IMAGE) { SpaceImage *sima = area->spacedata.first; - ED_space_image_set(bmain, sima, obedit, ima, false); + ED_space_image_set(bmain, sima, ima, false); iuser = &sima->iuser; } else { @@ -2519,7 +2518,6 @@ static void image_new_free(wmOperator *op) static int image_new_exec(bContext *C, wmOperator *op) { SpaceImage *sima; - Object *obedit; Image *ima; Main *bmain; PropertyRNA *prop; @@ -2531,7 +2529,6 @@ static int image_new_exec(bContext *C, wmOperator *op) /* retrieve state */ sima = CTX_wm_space_image(C); - obedit = CTX_data_edit_object(C); bmain = CTX_data_main(C); prop = RNA_struct_find_property(op->ptr, "name"); @@ -2587,7 +2584,7 @@ static int image_new_exec(bContext *C, wmOperator *op) RNA_property_update(C, &data->pprop.ptr, data->pprop.prop); } else if (sima) { - ED_space_image_set(bmain, sima, obedit, ima, false); + ED_space_image_set(bmain, sima, ima, false); } BKE_image_signal(bmain, ima, (sima) ? &sima->iuser : NULL, IMA_SIGNAL_USER_NEW_IMAGE); @@ -3705,7 +3702,7 @@ static int image_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"); if (sima->image == NULL) { - ED_space_image_set(bmain, sima, NULL, ima, false); + ED_space_image_set(bmain, sima, ima, false); } RE_ReadRenderResult(scene, scene); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 4107fd619aa..de8e4684d45 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -461,7 +461,7 @@ static void IMAGE_GGT_gizmo2d(wmGizmoGroupType *gzgt) gzgt->name = "UV Transform Gizmo"; gzgt->idname = "IMAGE_GGT_gizmo2d"; - gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + gzgt->flag |= (WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_IMAGE; @@ -475,7 +475,7 @@ static void IMAGE_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt) gzgt->name = "UV Translate Gizmo"; gzgt->idname = "IMAGE_GGT_gizmo2d_translate"; - gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + gzgt->flag |= (WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_IMAGE; @@ -489,7 +489,7 @@ static void IMAGE_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt) gzgt->name = "UV Transform Gizmo Resize"; gzgt->idname = "IMAGE_GGT_gizmo2d_resize"; - gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + gzgt->flag |= (WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_IMAGE; @@ -503,7 +503,7 @@ static void IMAGE_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt) gzgt->name = "UV Transform Gizmo Resize"; gzgt->idname = "IMAGE_GGT_gizmo2d_rotate"; - gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + gzgt->flag |= (WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_IMAGE; diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index d019573bf93..215e865d194 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -185,10 +185,31 @@ bool nla_panel_context(const bContext *C, return (found != 0); } +bool ANIM_nla_context_track_ptr(const bContext *C, PointerRNA *r_ptr) +{ + return nla_panel_context(C, NULL, r_ptr, NULL); +} + +bool ANIM_nla_context_strip_ptr(const bContext *C, PointerRNA *r_ptr) +{ + return nla_panel_context(C, NULL, NULL, r_ptr); +} + +NlaTrack *ANIM_nla_context_track(const bContext *C) +{ + PointerRNA track_ptr; + if (!ANIM_nla_context_track_ptr(C, &track_ptr)) { + return NULL; + } + NlaTrack *track = track_ptr.data; + + return track; +} + NlaStrip *ANIM_nla_context_strip(const bContext *C) { PointerRNA strip_ptr; - if (!nla_panel_context(C, NULL, NULL, &strip_ptr)) { + if (!ANIM_nla_context_strip_ptr(C, &strip_ptr)) { return NULL; } NlaStrip *strip = strip_ptr.data; diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 0f7a911e3ce..4b859de0ac9 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -362,8 +362,12 @@ static void node_draw_frame_label(bNodeTree *ntree, bNode *node, const float asp float x = BLI_rctf_cent_x(rct) - (0.5f * width); float y = rct->ymax - label_height; - BLF_position(fontid, x, y, 0); - BLF_draw(fontid, label, BLF_DRAW_STR_DUMMY_MAX); + /* label */ + const bool has_label = node->label[0] != '\0'; + if (has_label) { + BLF_position(fontid, x, y, 0); + BLF_draw(fontid, label, BLF_DRAW_STR_DUMMY_MAX); + } /* draw text body */ if (node->id) { @@ -374,7 +378,8 @@ static void node_draw_frame_label(bNodeTree *ntree, bNode *node, const float asp /* 'x' doesn't need aspect correction */ x = rct->xmin + margin; - y = rct->ymax - (label_height + line_spacing); + y = rct->ymax - label_height - (has_label ? line_spacing : 0); + /* early exit */ int y_min = y + ((margin * 2) - (y - rct->ymin)); @@ -455,10 +460,8 @@ static void node_draw_frame(const bContext *C, UI_draw_roundbox_aa(rct, false, BASIS_RAD, color); } - /* label */ - if (node->label[0] != '\0') { - node_draw_frame_label(ntree, node, snode->runtime->aspect); - } + /* label and text */ + node_draw_frame_label(ntree, node, snode->runtime->aspect); UI_block_end(C, node->block); UI_block_draw(C, node->block); diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 9264c9d3572..7b6ca5e6e61 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -312,7 +312,7 @@ void NODE_OT_add_reroute(wmOperatorType *ot) ot->poll = ED_operator_node_editable; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ PropertyRNA *prop; @@ -575,7 +575,7 @@ static int node_add_texture_exec(bContext *C, wmOperator *op) bNode *texture_node = node_add_node(C, nullptr, - GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, + GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, snode->runtime->cursor[0], snode->runtime->cursor[1]); if (!texture_node) { diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 5b4e3b3b6f5..aa241071425 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -41,10 +41,12 @@ #include "BLI_span.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" +#include "BLI_vector_set.hh" #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_geometry_set.hh" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -78,6 +80,8 @@ #include "NOD_geometry_nodes_eval_log.hh" +#include "FN_field_cpp_type.hh" + #include "node_intern.h" /* own include */ #ifdef WITH_COMPOSITOR @@ -88,7 +92,11 @@ using blender::Map; using blender::Set; using blender::Span; using blender::Vector; +using blender::VectorSet; using blender::fn::CPPType; +using blender::fn::FieldCPPType; +using blender::fn::FieldInput; +using blender::fn::GField; using blender::fn::GPointer; namespace geo_log = blender::nodes::geometry_nodes_eval_log; @@ -850,31 +858,70 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal }; const GPointer value = value_log.value(); - if (value.is_type<int>()) { - ss << *value.get<int>() << TIP_(" (Integer)"); - } - else if (value.is_type<float>()) { - ss << *value.get<float>() << TIP_(" (Float)"); - } - else if (value.is_type<blender::float3>()) { - ss << *value.get<blender::float3>() << TIP_(" (Vector)"); - } - else if (value.is_type<bool>()) { - ss << (*value.get<bool>() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); - } - else if (value.is_type<std::string>()) { - ss << *value.get<std::string>() << TIP_(" (String)"); + const CPPType &type = *value.type(); + if (const FieldCPPType *field_type = dynamic_cast<const FieldCPPType *>(&type)) { + const CPPType &base_type = field_type->field_type(); + BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer); + const GField &field = field_type->get_gfield(value.get()); + if (field.node().depends_on_input()) { + if (base_type.is<int>()) { + ss << TIP_("Integer Field"); + } + else if (base_type.is<float>()) { + ss << TIP_("Float Field"); + } + else if (base_type.is<blender::float3>()) { + ss << TIP_("Vector Field"); + } + else if (base_type.is<bool>()) { + ss << TIP_("Boolean Field"); + } + else if (base_type.is<std::string>()) { + ss << TIP_("String Field"); + } + ss << TIP_(" based on:\n"); + + /* Use vector set to deduplicate inputs. */ + VectorSet<std::reference_wrapper<const FieldInput>> field_inputs; + field.node().foreach_field_input( + [&](const FieldInput &field_input) { field_inputs.add(field_input); }); + for (const FieldInput &field_input : field_inputs) { + ss << "\u2022 " << field_input.socket_inspection_name(); + if (field_input != field_inputs.as_span().last().get()) { + ss << ".\n"; + } + } + } + else { + blender::fn::evaluate_constant_field(field, buffer); + if (base_type.is<int>()) { + ss << *(int *)buffer << TIP_(" (Integer)"); + } + else if (base_type.is<float>()) { + ss << *(float *)buffer << TIP_(" (Float)"); + } + else if (base_type.is<blender::float3>()) { + ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); + } + else if (base_type.is<bool>()) { + ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); + } + else if (base_type.is<std::string>()) { + ss << *(std::string *)buffer << TIP_(" (String)"); + } + base_type.destruct(buffer); + } } - else if (value.is_type<Object *>()) { + else if (type.is<Object *>()) { id_to_inspection_string((ID *)*value.get<Object *>(), ID_OB); } - else if (value.is_type<Material *>()) { + else if (type.is<Material *>()) { id_to_inspection_string((ID *)*value.get<Material *>(), ID_MA); } - else if (value.is_type<Tex *>()) { + else if (type.is<Tex *>()) { id_to_inspection_string((ID *)*value.get<Tex *>(), ID_TE); } - else if (value.is_type<Collection *>()) { + else if (type.is<Collection *>()) { id_to_inspection_string((ID *)*value.get<Collection *>(), ID_GR); } } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 5b1b737751c..030d1672a08 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -52,6 +52,7 @@ #include "RE_engine.h" #include "RE_pipeline.h" +#include "ED_image.h" #include "ED_node.h" /* own include */ #include "ED_render.h" #include "ED_screen.h" @@ -719,11 +720,22 @@ void ED_node_set_active( ED_node_tag_update_nodetree(bmain, ntree, node); } - /* if active texture changed, free glsl materials */ if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) { + /* If active texture changed, free glsl materials. */ LISTBASE_FOREACH (Material *, ma, &bmain->materials) { if (ma->nodetree && ma->use_nodes && ntreeHasTree(ma->nodetree, ntree)) { GPU_material_free(&ma->gpumaterial); + + /* Sync to active texpaint slot, otherwise we can end up painting on a different slot + * than we are looking at. */ + if (ma->texpaintslot) { + Image *image = (Image *)node->id; + for (int i = 0; i < ma->tot_slots; i++) { + if (ma->texpaintslot[i].ima == image) { + ma->paint_active_slot = i; + } + } + } } } @@ -733,6 +745,23 @@ void ED_node_set_active( } } + /* Sync to Image Editor. */ + Image *image = (Image *)node->id; + wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + const bScreen *screen = WM_window_get_active_screen(win); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + if (!sima->pin) { + ED_space_image_set(bmain, sima, image, true); + } + } + } + } + } + if (r_active_texture_changed) { *r_active_texture_changed = true; } diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index e908a61eed9..7d95659e403 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -1427,7 +1427,7 @@ void NODE_OT_links_cut(wmOperatorType *ot) ot->poll = ED_operator_node_editable; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ PropertyRNA *prop; @@ -1533,7 +1533,7 @@ void NODE_OT_links_mute(wmOperatorType *ot) ot->poll = ED_operator_node_editable; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ PropertyRNA *prop; diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index adff85a2b8c..29b8372d043 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -923,7 +923,7 @@ void NODE_OT_select_lasso(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* flags */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ RNA_def_boolean(ot->srna, diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index db37c8c1c8c..c06a1010168 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -32,6 +32,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_text_types.h" #include "BLI_blenlib.h" #include "BLI_math.h" @@ -59,6 +60,7 @@ #include "DEG_depsgraph_build.h" #include "ED_armature.h" +#include "ED_fileselect.h" #include "ED_outliner.h" #include "ED_screen.h" @@ -2625,9 +2627,17 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case ID_NLA: data.icon = ICON_NLA; break; - case ID_TXT: - data.icon = ICON_SCRIPT; + case ID_TXT: { + Text *text = (Text *)tselem->id; + if (text->filepath == NULL || (text->flags & TXT_ISMEM)) { + data.icon = ICON_FILE_TEXT; + } + else { + /* Helps distinguish text-based formats like the file-browser does. */ + data.icon = ED_file_extension_icon(text->filepath); + } break; + } case ID_GR: data.icon = ICON_OUTLINER_COLLECTION; break; diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 1feecc04ead..581892ebb3a 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -673,7 +673,7 @@ static void tree_element_sequence_activate(bContext *C, const eOLSetState set) { Sequence *seq = (Sequence *)te->directdata; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (BLI_findindex(ed->seqbasep, seq) != -1) { if (set == OL_SETSEL_EXTEND) { @@ -695,7 +695,7 @@ static void tree_element_sequence_activate(bContext *C, static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED(te)) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); #if 0 select_single_seq(seq, 1); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index d88ae82cc9a..9e314701719 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -737,13 +737,8 @@ static void id_local_fn(bContext *C, { if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) { Main *bmain = CTX_data_main(C); - /* if the ID type has no special local function, - * just clear the lib */ - if (BKE_lib_id_make_local(bmain, tselem->id, false, 0) == false) { - BKE_lib_id_clear_library_data(bmain, tselem->id); - } - else { - BKE_main_id_newptr_and_tag_clear(bmain); + if (BKE_lib_id_make_local(bmain, tselem->id, 0)) { + BKE_id_newptr_and_tag_clear(tselem->id); } } else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { @@ -1286,7 +1281,7 @@ static void sequence_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem { Sequence *seq = (Sequence *)te->directdata; Scene *scene = (Scene *)scene_ptr; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (BLI_findindex(ed->seqbasep, seq) != -1) { if (event == OL_DOP_SELECT) { ED_sequencer_select_sequence_single(scene, seq, true); diff --git a/source/blender/editors/space_outliner/tree/tree_display_sequencer.cc b/source/blender/editors/space_outliner/tree/tree_display_sequencer.cc index 40f329d72c3..02af6a13cb3 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_sequencer.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_sequencer.cc @@ -43,7 +43,7 @@ ListBase TreeDisplaySequencer::buildTree(const TreeSourceData &source_data) { ListBase tree = {nullptr}; - Editing *ed = SEQ_editing_get(source_data.scene, false); + Editing *ed = SEQ_editing_get(source_data.scene); if (ed == nullptr) { return tree; } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 16b690dd6e4..081f0241e94 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -185,7 +185,7 @@ static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type) Sequence *tgt = NULL; Sequence *seq; Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); + Editing *ed = SEQ_editing_ensure(scene); int timeline_frame = (int)CFRA; int proximity = INT_MAX; @@ -314,7 +314,7 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm static void seq_load_apply_generic_options(bContext *C, wmOperator *op, Sequence *seq) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (seq == NULL) { return; @@ -356,7 +356,7 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - const Editing *ed = SEQ_editing_get(scene, true); + const Editing *ed = SEQ_editing_ensure(scene); Scene *sce_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene")); if (sce_seq == NULL) { @@ -384,7 +384,7 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) static void sequencer_disable_one_time_properties(bContext *C, wmOperator *op) { - Editing *ed = SEQ_editing_get(CTX_data_scene(C), false); + Editing *ed = SEQ_editing_get(CTX_data_scene(C)); /* Disable following properties if there are any existing strips, unless overridden by user. */ if (ed && ed->seqbasep && ed->seqbasep->first) { if (RNA_struct_find_property(op->ptr, "use_framerate")) { @@ -435,7 +435,7 @@ static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - const Editing *ed = SEQ_editing_get(scene, true); + const Editing *ed = SEQ_editing_ensure(scene); MovieClip *clip = BLI_findlink(&bmain->movieclips, RNA_enum_get(op->ptr, "clip")); if (clip == NULL) { @@ -499,7 +499,7 @@ static int sequencer_add_mask_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - const Editing *ed = SEQ_editing_get(scene, true); + const Editing *ed = SEQ_editing_ensure(scene); Mask *mask = BLI_findlink(&bmain->masks, RNA_enum_get(op->ptr, "mask")); if (mask == NULL) { @@ -632,7 +632,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C, { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - const Editing *ed = SEQ_editing_get(scene, true); + const Editing *ed = SEQ_editing_ensure(scene); RNA_BEGIN (op->ptr, itemptr, "files") { char dir_only[FILE_MAX]; @@ -668,7 +668,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - const Editing *ed = SEQ_editing_get(scene, true); + const Editing *ed = SEQ_editing_ensure(scene); Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; @@ -817,7 +817,7 @@ static void sequencer_add_sound_multiple_strips(bContext *C, { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); + Editing *ed = SEQ_editing_ensure(scene); RNA_BEGIN (op->ptr, itemptr, "files") { char dir_only[FILE_MAX]; @@ -842,7 +842,7 @@ static bool sequencer_add_sound_single_strip(bContext *C, wmOperator *op, SeqLoa { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); + Editing *ed = SEQ_editing_ensure(scene); Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f); if (seq == NULL) { @@ -1035,7 +1035,7 @@ static void sequencer_add_image_strip_load_files( static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); + Editing *ed = SEQ_editing_ensure(scene); SeqLoadData load_data; load_data_init_from_operator(&load_data, C, op); @@ -1141,7 +1141,7 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); + Editing *ed = SEQ_editing_ensure(scene); const char *error_msg; SeqLoadData load_data; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index fe3ff469e50..b56ad48cec2 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2275,9 +2275,9 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region) } } -static void seq_draw_sfra_efra(Scene *scene, View2D *v2d) +static void seq_draw_sfra_efra(const Scene *scene, View2D *v2d) { - const Editing *ed = SEQ_editing_get(scene, false); + const Editing *ed = SEQ_editing_get(scene); const int frame_sta = scene->r.sfra; const int frame_end = scene->r.efra + 1; @@ -2312,7 +2312,7 @@ static void seq_draw_sfra_efra(Scene *scene, View2D *v2d) /* While in meta strip, draw a checkerboard overlay outside of frame range. */ if (ed && !BLI_listbase_is_empty(&ed->metastack)) { - MetaStack *ms = ed->metastack.last; + const MetaStack *ms = ed->metastack.last; immUnbindProgram(); immBindBuiltinProgram(GPU_SHADER_2D_CHECKER); @@ -2586,7 +2586,7 @@ static void draw_overlap_frame_indicator(const struct Scene *scene, const View2D void draw_timeline_seq(const bContext *C, ARegion *region) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); SpaceSeq *sseq = CTX_wm_space_seq(C); View2D *v2d = ®ion->v2d; short cfra_flag = 0; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 935bc97d0b2..b95b7fa0620 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -190,14 +190,14 @@ bool ED_space_sequencer_has_playback_animation(const struct SpaceSeq *sseq, /* Operator functions. */ bool sequencer_edit_poll(bContext *C) { - return (SEQ_editing_get(CTX_data_scene(C), false) != NULL); + return (SEQ_editing_get(CTX_data_scene(C)) != NULL); } #if 0 /* UNUSED */ bool sequencer_strip_poll(bContext *C) { Editing *ed; - return (((ed = SEQ_editing_get(CTX_data_scene(C), false)) != NULL) && + return (((ed = SEQ_editing_get(CTX_data_scene(C))) != NULL) && (ed->act_seq != NULL)); } #endif @@ -206,14 +206,14 @@ bool sequencer_strip_has_path_poll(bContext *C) { Editing *ed; Sequence *seq; - return (((ed = SEQ_editing_get(CTX_data_scene(C), false)) != NULL) && - ((seq = ed->act_seq) != NULL) && (SEQ_HAS_PATH(seq))); + return (((ed = SEQ_editing_get(CTX_data_scene(C))) != NULL) && ((seq = ed->act_seq) != NULL) && + (SEQ_HAS_PATH(seq))); } bool sequencer_view_preview_poll(bContext *C) { SpaceSeq *sseq = CTX_wm_space_seq(C); - Editing *ed = SEQ_editing_get(CTX_data_scene(C), false); + Editing *ed = SEQ_editing_get(CTX_data_scene(C)); if (ed && sseq && (sseq->mainb == SEQ_DRAW_IMG_IMBUF)) { return 1; } @@ -241,7 +241,7 @@ static int sequencer_gap_remove_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); const bool do_all = RNA_boolean_get(op->ptr, "all"); - const Editing *ed = SEQ_editing_get(scene, false); + const Editing *ed = SEQ_editing_get(scene); SEQ_edit_remove_gaps(scene, ed->seqbasep, CFRA, do_all); @@ -281,7 +281,7 @@ static int sequencer_gap_insert_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); const int frames = RNA_int_get(op->ptr, "frames"); - const Editing *ed = SEQ_editing_get(scene, false); + const Editing *ed = SEQ_editing_get(scene); SEQ_transform_offset_after_frame(scene, ed->seqbasep, frames, CFRA); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -327,7 +327,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; int snap_frame; @@ -531,7 +531,7 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve { SlipData *data; Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); float mouseloc[2]; int num_seq; View2D *v2d = UI_view2d_fromcontext(C); @@ -579,7 +579,7 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) { /* Only data types supported for now. */ - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); bool changed = false; /* Iterate in reverse so meta-strips are iterated after their children. */ @@ -663,7 +663,7 @@ static void sequencer_slip_apply_limits(SlipData *data, int *offset) static int sequencer_slip_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); int offset = RNA_int_get(op->ptr, "offset"); bool success = false; @@ -798,7 +798,7 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even case EVT_ESCKEY: case RIGHTMOUSE: { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); for (int i = 0; i < data->num_seq; i++) { transseq_restore(data->ts + i, data->seq_array[i]); @@ -900,7 +900,7 @@ void SEQUENCER_OT_slip(struct wmOperatorType *ot) static int sequencer_mute_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; bool selected; @@ -956,7 +956,7 @@ void SEQUENCER_OT_mute(struct wmOperatorType *ot) static int sequencer_unmute_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; bool selected; @@ -1012,7 +1012,7 @@ void SEQUENCER_OT_unmute(struct wmOperatorType *ot) static int sequencer_lock_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; for (seq = ed->seqbasep->first; seq; seq = seq->next) { @@ -1050,7 +1050,7 @@ void SEQUENCER_OT_lock(struct wmOperatorType *ot) static int sequencer_unlock_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; for (seq = ed->seqbasep->first; seq; seq = seq->next) { @@ -1089,7 +1089,7 @@ static int sequencer_reload_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; const bool adjust_length = RNA_boolean_get(op->ptr, "adjust_length"); @@ -1152,7 +1152,7 @@ static bool sequencer_refresh_all_poll(bContext *C) static int sequencer_refresh_all_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); SEQ_relations_free_imbuf(scene, &ed->seqbase, false); @@ -1187,7 +1187,7 @@ int seq_effect_find_selected(Scene *scene, Sequence **r_selseq3, const char **r_error_str) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq1 = NULL, *seq2 = NULL, *seq3 = NULL, *seq; *r_error_str = NULL; @@ -1316,7 +1316,7 @@ static int sequencer_reassign_inputs_exec(bContext *C, wmOperator *op) static bool sequencer_effect_poll(bContext *C) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { Sequence *last_seq = SEQ_select_active_get(scene); @@ -1423,7 +1423,7 @@ static int sequencer_split_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); bool changed = false; bool seq_selected = false; @@ -1628,7 +1628,7 @@ void SEQUENCER_OT_split(struct wmOperatorType *ot) static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return OPERATOR_CANCELLED; @@ -1688,7 +1688,7 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); SEQ_prefetch_stop(scene); @@ -1747,7 +1747,7 @@ void SEQUENCER_OT_delete(wmOperatorType *ot) static int sequencer_offset_clear_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; /* For effects, try to find a replacement input. */ @@ -1802,7 +1802,7 @@ void SEQUENCER_OT_offset_clear(wmOperatorType *ot) static int sequencer_separate_images_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq, *seq_new; Strip *strip_new; @@ -1908,7 +1908,7 @@ void SEQUENCER_OT_images_separate(wmOperatorType *ot) static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *active_seq = SEQ_select_active_get(scene); if (active_seq && active_seq->type == SEQ_TYPE_META && active_seq->flag & SELECT) { @@ -1959,7 +1959,7 @@ void SEQUENCER_OT_meta_toggle(wmOperatorType *ot) static int sequencer_meta_make_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *active_seq = SEQ_select_active_get(scene); ListBase *active_seqbase = SEQ_active_seqbase_get(ed); @@ -2027,7 +2027,7 @@ void SEQUENCER_OT_meta_make(wmOperatorType *ot) static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *active_seq = SEQ_select_active_get(scene); if (active_seq == NULL || active_seq->type != SEQ_TYPE_META) { @@ -2181,7 +2181,7 @@ static Sequence *find_next_prev_sequence(Scene *scene, Sequence *test, int lr, i { /* sel: 0==unselected, 1==selected, -1==don't care. */ Sequence *seq, *best_seq = NULL; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); int dist, best_dist; best_dist = MAXFRAME * 2; @@ -2231,7 +2231,7 @@ static bool seq_is_parent(Sequence *par, Sequence *seq) static int sequencer_swap_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *active_seq = SEQ_select_active_get(scene); Sequence *seq, *iseq; int side = RNA_enum_get(op->ptr, "side"); @@ -2399,7 +2399,7 @@ static int sequencer_copy_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); SEQ_clipboard_free(); @@ -2456,7 +2456,7 @@ void SEQUENCER_OT_copy(wmOperatorType *ot) void ED_sequencer_deselect_all(Scene *scene) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return; @@ -2471,7 +2471,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); /* Create if needed. */ + Editing *ed = SEQ_editing_ensure(scene); /* Create if needed. */ ListBase nseqbase = {NULL, NULL}; int ofs; Sequence *iseq, *iseq_first; @@ -2640,7 +2640,7 @@ static const EnumPropertyItem prop_change_effect_input_types[] = { static int sequencer_change_effect_input_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = SEQ_select_active_get(scene); Sequence **seq_1, **seq_2; @@ -2724,7 +2724,7 @@ EnumPropertyItem sequencer_prop_effect_types[] = { static int sequencer_change_effect_type_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = SEQ_select_active_get(scene); const int new_type = RNA_enum_get(op->ptr, "type"); @@ -2790,7 +2790,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = SEQ_select_active_get(scene); const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders"); @@ -3000,7 +3000,7 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Sequence *seq, *seq_next; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); ListBase text_seq = {0}; int iter = 0; FILE *file; @@ -3077,8 +3077,8 @@ static bool sequencer_strip_is_text_poll(bContext *C) { Editing *ed; Sequence *seq; - return (((ed = SEQ_editing_get(CTX_data_scene(C), false)) != NULL) && - ((seq = ed->act_seq) != NULL) && (seq->type == SEQ_TYPE_TEXT)); + return (((ed = SEQ_editing_get(CTX_data_scene(C))) != NULL) && ((seq = ed->act_seq) != NULL) && + (seq->type == SEQ_TYPE_TEXT)); } void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot) @@ -3114,7 +3114,7 @@ void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot) static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; int sfra = MAXFRAME; @@ -3199,7 +3199,7 @@ static const EnumPropertyItem transform_reset_properties[] = { static int sequencer_strip_transform_clear_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - const Editing *ed = SEQ_editing_get(scene, false); + const Editing *ed = SEQ_editing_get(scene); Sequence *seq; const int property = RNA_enum_get(op->ptr, "property"); @@ -3272,7 +3272,7 @@ static const EnumPropertyItem scale_fit_methods[] = { static int sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - const Editing *ed = SEQ_editing_get(scene, false); + const Editing *ed = SEQ_editing_get(scene); Sequence *seq; const eSeqImageFitMethod fit_method = RNA_enum_get(op->ptr, "fit_method"); diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c index 9b3ecacceb9..dd8595622f8 100644 --- a/source/blender/editors/space_sequencer/sequencer_modifier.c +++ b/source/blender/editors/space_sequencer/sequencer_modifier.c @@ -48,7 +48,7 @@ static bool strip_modifier_active_poll(bContext *C) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { Sequence *seq = SEQ_select_active_get(scene); diff --git a/source/blender/editors/space_sequencer/sequencer_proxy.c b/source/blender/editors/space_sequencer/sequencer_proxy.c index 16d14b5fa72..0a8eb7cb88f 100644 --- a/source/blender/editors/space_sequencer/sequencer_proxy.c +++ b/source/blender/editors/space_sequencer/sequencer_proxy.c @@ -56,7 +56,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); ScrArea *area = CTX_wm_area(C); if (ed == NULL) { @@ -121,7 +121,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) Main *bmain = CTX_data_main(C); struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); GSet *file_list; if (ed == NULL) { @@ -184,7 +184,7 @@ static int sequencer_enable_proxies_invoke(bContext *C, static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); bool proxy_25 = RNA_boolean_get(op->ptr, "proxy_25"); bool proxy_50 = RNA_boolean_get(op->ptr, "proxy_50"); bool proxy_75 = RNA_boolean_get(op->ptr, "proxy_75"); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 333edd0ed5f..80d3e2cbdaa 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -202,7 +202,7 @@ void select_surround_from_last(Scene *scene) void ED_sequencer_select_sequence_single(Scene *scene, Sequence *seq, bool deselect_all) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (deselect_all) { ED_sequencer_deselect_all(scene); @@ -236,7 +236,7 @@ Sequence *find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int se { /* sel: 0==unselected, 1==selected, -1==don't care. */ Sequence *seq; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return NULL; @@ -270,7 +270,7 @@ Sequence *find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int se Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[2]) { Sequence *seq; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); float x, y; float pixelx; float handsize; @@ -396,7 +396,7 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) int action = RNA_enum_get(op->ptr, "action"); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; if (action == SEL_TOGGLE) { @@ -463,7 +463,7 @@ void SEQUENCER_OT_select_all(struct wmOperatorType *ot) static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; for (seq = ed->seqbasep->first; seq; seq = seq->next) { @@ -504,238 +504,245 @@ void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot) /** \name Select Operator * \{ */ -static int sequencer_select_exec(bContext *C, wmOperator *op) +static void sequencer_select_set_active(Scene *scene, Sequence *seq) { - View2D *v2d = UI_view2d_fromcontext(C); - Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - const bool linked_handle = RNA_boolean_get(op->ptr, "linked_handle"); - const bool linked_time = RNA_boolean_get(op->ptr, "linked_time"); - bool side_of_frame = RNA_boolean_get(op->ptr, "side_of_frame"); - bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - int mval[2]; - int ret_value = OPERATOR_CANCELLED; + Editing *ed = SEQ_editing_get(scene); - mval[0] = RNA_int_get(op->ptr, "mouse_x"); - mval[1] = RNA_int_get(op->ptr, "mouse_y"); - - Sequence *seq, *neighbor, *act_orig; - int hand, sel_side; + SEQ_select_active_set(scene, seq); - if (ed == NULL) { - return OPERATOR_CANCELLED; + if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) { + if (seq->strip) { + BLI_strncpy(ed->act_imagedir, seq->strip->dir, FILE_MAXDIR); + } } - - if (extend) { - wait_to_deselect_others = false; + else if (seq->type == SEQ_TYPE_SOUND_RAM) { + if (seq->strip) { + BLI_strncpy(ed->act_sounddir, seq->strip->dir, FILE_MAXDIR); + } } + recurs_sel_seq(seq); +} - seq = find_nearest_seq(scene, v2d, &hand, mval); - - /* XXX: not nice, Ctrl+RMB needs to do side_of_frame only when not over a strip. */ - if (seq && linked_time) { - side_of_frame = false; - } +static void sequencer_select_do_updates(bContext *C, Scene *scene) +{ + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); +} - /* Select left, right or overlapping the current frame. */ - if (side_of_frame) { - /* Use different logic for this. */ - if (extend == false) { - ED_sequencer_deselect_all(scene); +static void sequencer_select_side_of_frame(const bContext *C, + const View2D *v2d, + const int mval[2], + Scene *scene) +{ + Editing *ed = SEQ_editing_get(scene); + + const float x = UI_view2d_region_to_view_x(v2d, mval[0]); + LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) { + if (((x < CFRA) && (seq_iter->enddisp <= CFRA)) || + ((x >= CFRA) && (seq_iter->startdisp >= CFRA))) { + /* Select left or right. */ + seq_iter->flag |= SELECT; + recurs_sel_seq(seq_iter); } + } - const float x = UI_view2d_region_to_view_x(v2d, mval[0]); + { + SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq && sseq->flag & SEQ_MARKER_TRANS) { + TimeMarker *tmarker; - LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) { - if (((x < CFRA) && (seq_iter->enddisp <= CFRA)) || - ((x >= CFRA) && (seq_iter->startdisp >= CFRA))) { - /* Select left or right. */ - seq_iter->flag |= SELECT; - recurs_sel_seq(seq_iter); + for (tmarker = scene->markers.first; tmarker; tmarker = tmarker->next) { + if (((x < CFRA) && (tmarker->frame <= CFRA)) || + ((x >= CFRA) && (tmarker->frame >= CFRA))) { + tmarker->flag |= SELECT; + } + else { + tmarker->flag &= ~SELECT; + } } } + } +} - { - SpaceSeq *sseq = CTX_wm_space_seq(C); - if (sseq && sseq->flag & SEQ_MARKER_TRANS) { - TimeMarker *tmarker; +static void sequencer_select_linked_handle(const bContext *C, + Sequence *seq, + const int handle_clicked) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + if (!ELEM(handle_clicked, SEQ_SIDE_LEFT, SEQ_SIDE_RIGHT)) { + /* First click selects the strip and its adjacent handles (if valid). + * Second click selects the strip, + * both of its handles and its adjacent handles (if valid). */ + const bool is_striponly_selected = ((seq->flag & SEQ_ALLSEL) == SELECT); + seq->flag &= ~SEQ_ALLSEL; + seq->flag |= is_striponly_selected ? SEQ_ALLSEL : SELECT; + select_surrounding_handles(scene, seq); + } + else { + /* Always select the strip under the cursor. */ + seq->flag |= SELECT; - for (tmarker = scene->markers.first; tmarker; tmarker = tmarker->next) { - if (((x < CFRA) && (tmarker->frame <= CFRA)) || - ((x >= CFRA) && (tmarker->frame >= CFRA))) { - tmarker->flag |= SELECT; + /* First click selects adjacent handles on that side. + * Second click selects all strips in that direction. + * If there are no adjacent strips, it just selects all in that direction. + */ + int sel_side = handle_clicked; + Sequence *neighbor = find_neighboring_sequence(scene, seq, sel_side, -1); + if (neighbor) { + switch (sel_side) { + case SEQ_SIDE_LEFT: + if ((seq->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) { + seq->flag |= SELECT; + select_active_side(ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, seq->startdisp); } else { - tmarker->flag &= ~SELECT; + seq->flag |= SELECT; + neighbor->flag |= SELECT; + recurs_sel_seq(neighbor); + neighbor->flag |= SEQ_RIGHTSEL; + seq->flag |= SEQ_LEFTSEL; } - } + break; + case SEQ_SIDE_RIGHT: + if ((seq->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) { + seq->flag |= SELECT; + select_active_side(ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, seq->startdisp); + } + else { + seq->flag |= SELECT; + neighbor->flag |= SELECT; + recurs_sel_seq(neighbor); + neighbor->flag |= SEQ_LEFTSEL; + seq->flag |= SEQ_RIGHTSEL; + } + break; } } + else { - ret_value = OPERATOR_FINISHED; + select_active_side(ed->seqbasep, sel_side, seq->machine, seq->startdisp); + } } - else { - act_orig = ed->act_seq; - - if (seq) { - /* Are we trying to select a handle that's already selected? */ - const bool handle_selected = ((hand == SEQ_SIDE_LEFT) && (seq->flag & SEQ_LEFTSEL)) || - ((hand == SEQ_SIDE_RIGHT) && (seq->flag & SEQ_RIGHTSEL)); - - if (wait_to_deselect_others && (seq->flag & SELECT) && - (hand == SEQ_SIDE_NONE || handle_selected)) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else if (!extend && !linked_handle) { - ED_sequencer_deselect_all(scene); - ret_value = OPERATOR_FINISHED; - } - else { - ret_value = OPERATOR_FINISHED; - } - - SEQ_select_active_set(scene, seq); +} - if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) { - if (seq->strip) { - BLI_strncpy(ed->act_imagedir, seq->strip->dir, FILE_MAXDIR); - } - } - else if (seq->type == SEQ_TYPE_SOUND_RAM) { - if (seq->strip) { - BLI_strncpy(ed->act_sounddir, seq->strip->dir, FILE_MAXDIR); - } - } +static bool element_already_selected(const Sequence *seq, const int handle_clicked) +{ + const bool handle_already_selected = ((handle_clicked == SEQ_SIDE_LEFT) && + (seq->flag & SEQ_LEFTSEL)) || + ((handle_clicked == SEQ_SIDE_RIGHT) && + (seq->flag & SEQ_RIGHTSEL)); + return ((seq->flag & SELECT) && handle_clicked == SEQ_SIDE_NONE) || handle_already_selected; +} - /* On Alt selection, select the strip and bordering handles. */ - if (linked_handle) { - if (!ELEM(hand, SEQ_SIDE_LEFT, SEQ_SIDE_RIGHT)) { - /* First click selects the strip and its adjacent handles (if valid). - * Second click selects the strip, - * both of its handles and its adjacent handles (if valid). */ - const bool is_striponly_selected = ((seq->flag & SEQ_ALLSEL) == SELECT); +static void sequencer_select_strip_impl(const Editing *ed, + Sequence *seq, + const int handle_clicked, + const bool extend) +{ + /* Deselect strip. */ + if (extend && (seq->flag & SELECT) && ed->act_seq == seq) { + switch (handle_clicked) { + case SEQ_SIDE_NONE: + seq->flag &= ~SEQ_ALLSEL; + break; + case SEQ_SIDE_LEFT: + seq->flag ^= SEQ_LEFTSEL; + break; + case SEQ_SIDE_RIGHT: + seq->flag ^= SEQ_RIGHTSEL; + break; + } + } + else { /* Select strip. */ + seq->flag |= SELECT; + if (handle_clicked == SEQ_SIDE_LEFT) { + seq->flag |= SEQ_LEFTSEL; + } + if (handle_clicked == SEQ_SIDE_RIGHT) { + seq->flag |= SEQ_RIGHTSEL; + } + } +} - if (!extend) { - ED_sequencer_deselect_all(scene); - } - seq->flag &= ~SEQ_ALLSEL; - seq->flag |= is_striponly_selected ? SEQ_ALLSEL : SELECT; - select_surrounding_handles(scene, seq); - } - else { - /* Always select the strip under the cursor. */ - seq->flag |= SELECT; +static int sequencer_select_exec(bContext *C, wmOperator *op) +{ + View2D *v2d = UI_view2d_fromcontext(C); + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + const bool extend = RNA_boolean_get(op->ptr, "extend"); - /* First click selects adjacent handles on that side. - * Second click selects all strips in that direction. - * If there are no adjacent strips, it just selects all in that direction. - */ - sel_side = hand; - neighbor = find_neighboring_sequence(scene, seq, sel_side, -1); - if (neighbor) { - switch (sel_side) { - case SEQ_SIDE_LEFT: - if ((seq->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - seq->flag |= SELECT; - - select_active_side(ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, seq->startdisp); - } - else { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - seq->flag |= SELECT; - - neighbor->flag |= SELECT; - recurs_sel_seq(neighbor); - neighbor->flag |= SEQ_RIGHTSEL; - seq->flag |= SEQ_LEFTSEL; - } - break; - case SEQ_SIDE_RIGHT: - if ((seq->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - seq->flag |= SELECT; - - select_active_side(ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, seq->startdisp); - } - else { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - seq->flag |= SELECT; - - neighbor->flag |= SELECT; - recurs_sel_seq(neighbor); - neighbor->flag |= SEQ_LEFTSEL; - seq->flag |= SEQ_RIGHTSEL; - } - break; - } - } - else { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - select_active_side(ed->seqbasep, sel_side, seq->machine, seq->startdisp); - } - } + if (ed == NULL) { + return OPERATOR_CANCELLED; + } - ret_value = OPERATOR_FINISHED; - } - else { - if (extend && (seq->flag & SELECT) && ed->act_seq == act_orig) { - switch (hand) { - case SEQ_SIDE_NONE: - if (linked_handle == 0) { - seq->flag &= ~SEQ_ALLSEL; - } - break; - case SEQ_SIDE_LEFT: - seq->flag ^= SEQ_LEFTSEL; - break; - case SEQ_SIDE_RIGHT: - seq->flag ^= SEQ_RIGHTSEL; - break; - } - ret_value = OPERATOR_FINISHED; - } - else { - seq->flag |= SELECT; - if (hand == SEQ_SIDE_LEFT) { - seq->flag |= SEQ_LEFTSEL; - } - if (hand == SEQ_SIDE_RIGHT) { - seq->flag |= SEQ_RIGHTSEL; - } - } - } + int mval[2]; + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); - recurs_sel_seq(seq); + int handle_clicked; + Sequence *seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); - if (linked_time) { - select_linked_time(ed->seqbasep, seq); - } + /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap, + * therefore both properties can be true at the same time. */ + if (seq && RNA_boolean_get(op->ptr, "linked_time")) { + if (!extend) { + ED_sequencer_deselect_all(scene); + } + sequencer_select_strip_impl(ed, seq, handle_clicked, extend); + select_linked_time(ed->seqbasep, seq); + sequencer_select_do_updates(C, scene); + sequencer_select_set_active(scene, seq); + return OPERATOR_FINISHED; + } - BLI_assert((ret_value & OPERATOR_CANCELLED) == 0); + /* Select left, right or overlapping the current frame. */ + if (RNA_boolean_get(op->ptr, "side_of_frame")) { + if (!extend) { + ED_sequencer_deselect_all(scene); } - else if (deselect_all) { + sequencer_select_side_of_frame(C, v2d, mval, scene); + sequencer_select_do_updates(C, scene); + return OPERATOR_FINISHED; + } + + /* On Alt selection, select the strip and bordering handles. */ + if (seq && RNA_boolean_get(op->ptr, "linked_handle")) { + if (!extend) { ED_sequencer_deselect_all(scene); - ret_value = OPERATOR_FINISHED; } + sequencer_select_linked_handle(C, seq, handle_clicked); + sequencer_select_do_updates(C, scene); + sequencer_select_set_active(scene, seq); + return OPERATOR_FINISHED; } - ED_outliner_select_sync_from_sequence_tag(C); + const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + /* Clicking on already selected element falls on modal operation. + * All strips are deselected on mouse button release unless extend mode is used. */ + if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !extend) { + return OPERATOR_RUNNING_MODAL; + } + + int ret_value = OPERATOR_CANCELLED; + + if (!extend) { + ED_sequencer_deselect_all(scene); + ret_value = OPERATOR_FINISHED; + } + + /* Nothing to select, but strips could be deselected. */ + if (!seq) { + sequencer_select_do_updates(C, scene); + return ret_value; + } + /* Do actual selection. */ + sequencer_select_strip_impl(ed, seq, handle_clicked, extend); + ret_value = OPERATOR_FINISHED; + sequencer_select_do_updates(C, scene); + sequencer_select_set_active(scene, seq); return ret_value; } @@ -764,13 +771,6 @@ void SEQUENCER_OT_select(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, - "deselect_all", - false, - "Deselect On Nothing", - "Deselect all when nothing under the cursor"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - prop = RNA_def_boolean(ot->srna, "linked_handle", false, "Linked Handle", @@ -799,7 +799,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) /* Run recursively to select linked. */ static bool select_linked_internal(Scene *scene) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return false; @@ -832,7 +832,7 @@ static bool select_linked_internal(Scene *scene) /* Select only one linked strip on each side. */ static bool select_more_less_seq__internal(Scene *scene, bool select_more) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return false; @@ -1070,7 +1070,7 @@ static const EnumPropertyItem prop_select_handles_side_types[] = { static int sequencer_select_handles_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; int sel_side = RNA_enum_get(op->ptr, "side"); @@ -1169,7 +1169,7 @@ void SEQUENCER_OT_select_handles(wmOperatorType *ot) static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); const bool extend = RNA_boolean_get(op->ptr, "extend"); const int side = RNA_enum_get(op->ptr, "side"); @@ -1244,7 +1244,7 @@ void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot) static int sequencer_select_side_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); const int sel_side = RNA_enum_get(op->ptr, "side"); const int frame_init = sel_side == SEQ_SIDE_LEFT ? INT_MIN : INT_MAX; @@ -1315,7 +1315,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); View2D *v2d = UI_view2d_fromcontext(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return OPERATOR_CANCELLED; @@ -1679,7 +1679,7 @@ static bool select_grouped_effect_link(Editing *ed, static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *actseq = SEQ_select_active_get(scene); if (actseq == NULL) { diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index a0a9cdd96b1..981f793c896 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -90,7 +90,7 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op) const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); Scene *scene = CTX_data_scene(C); - const Editing *ed = SEQ_editing_get(scene, false); + const Editing *ed = SEQ_editing_get(scene); SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box); UI_view2d_smooth_view(C, region, &box, smooth_viewtx); @@ -274,7 +274,7 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); View2D *v2d = UI_view2d_fromcontext(C); ARegion *region = CTX_wm_region(C); - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; rctf cur_new = v2d->cur; diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index fcc92345bea..a82648aeee0 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -270,7 +270,7 @@ Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, return nullptr; } Object *object_orig = (Object *)used_id; - if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) { + if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE, OB_FONT)) { return nullptr; } @@ -370,7 +370,7 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) std::unique_ptr<ColumnValues> values_ptr = data_source->get_column_values(*column->id); /* Should have been removed before if it does not exist anymore. */ BLI_assert(values_ptr); - const ColumnValues *values = scope.add(std::move(values_ptr), __func__); + const ColumnValues *values = scope.add(std::move(values_ptr)); const int width = get_column_width_in_pixels(*values); spreadsheet_layout.columns.append({values, width}); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index 680da9b6794..97170693cb3 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -35,6 +35,10 @@ struct CollectionCellValue { const Collection *collection; }; +struct GeometrySetCellValue { + const GeometrySet *geometry_set; +}; + /** * This is a type that can hold the value of a cell in a spreadsheet. This type allows us to * decouple the drawing of individual cells from the code that generates the data to be displayed. @@ -53,6 +57,7 @@ class CellValue { std::optional<ColorGeometry4f> value_color; std::optional<ObjectCellValue> value_object; std::optional<CollectionCellValue> value_collection; + std::optional<GeometrySetCellValue> value_geometry_set; }; } // namespace blender::ed::spreadsheet 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 e38c70afd0f..78d9f61d8d5 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -45,15 +45,19 @@ namespace blender::ed::spreadsheet { void GeometryDataSource::foreach_default_column_ids( FunctionRef<void(const SpreadsheetColumnID &)> fn) const { - component_->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (meta_data.domain != domain_) { - return true; - } - SpreadsheetColumnID column_id; - column_id.name = (char *)name.c_str(); - fn(column_id); - return true; - }); + component_->attribute_foreach( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain != domain_) { + return true; + } + if (attribute_id.is_anonymous()) { + return true; + } + SpreadsheetColumnID column_id; + column_id.name = (char *)attribute_id.name().data(); + fn(column_id); + return true; + }); } std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( @@ -65,7 +69,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( if (!attribute) { return {}; } - const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__); + const fn::GVArray *varray = scope_.add(std::move(attribute.varray)); if (attribute.domain != domain_) { return {}; } @@ -332,6 +336,11 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( r_cell_value.value_collection = CollectionCellValue{&collection}; break; } + case InstanceReference::Type::GeometrySet: { + const GeometrySet &geometry_set = reference.geometry_set(); + r_cell_value.value_geometry_set = GeometrySetCellValue{&geometry_set}; + break; + } case InstanceReference::Type::None: { break; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 8079763a339..1a5eac53306 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -209,6 +209,23 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { 0, nullptr); } + else if (cell_value.value_geometry_set.has_value()) { + uiDefIconTextBut(params.block, + UI_BTYPE_LABEL, + 0, + ICON_MESH_DATA, + "Geometry", + params.xmin, + params.ymin, + params.width, + params.height, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + } } void draw_float_vector(const CellDrawParams ¶ms, const Span<float> values) const diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index ae336edfead..1e46fef8d71 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -328,7 +328,7 @@ Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet, geometry_data_source->apply_selection_filter(rows_included); } - Vector<int64_t> &indices = scope.construct<Vector<int64_t>>(__func__); + Vector<int64_t> &indices = scope.construct<Vector<int64_t>>(); index_vector_from_bools(rows_included, indices); return indices; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 466820353b9..8ed134c7fd1 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -3961,9 +3961,10 @@ static int view_axis_exec(bContext *C, wmOperator *op) Object *obact = CTX_data_active_object(C); if (obact != NULL) { float twmat[3][3]; + ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = CTX_data_edit_object(C); /* same as transform gizmo when normal is set */ - ED_getTransformOrientationMatrix(C, obact, obedit, V3D_AROUND_ACTIVE, twmat); + ED_getTransformOrientationMatrix(view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat); align_quat = align_quat_buf; mat3_to_quat(align_quat, twmat); invert_qt_normalized(align_quat); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index edc34d0d883..f8278edbcae 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -33,6 +33,7 @@ #include "BKE_material.h" #include "BKE_object.h" +#include "BKE_scene.h" #include "BKE_unit.h" #include "DNA_gpencil_types.h" @@ -44,6 +45,7 @@ #include "ED_gizmo_utils.h" #include "ED_gpencil.h" #include "ED_screen.h" +#include "ED_transform.h" #include "ED_transform_snap_object_context.h" #include "ED_view3d.h" @@ -69,6 +71,12 @@ #include "BLF_api.h" +/** + * Supporting transform features could be removed if the actual transform system is used. + * Keep the option open since each transform feature is duplicating logic. + */ +#define USE_AXIS_CONSTRAINTS + static const char *view3d_gzgt_ruler_id = "VIEW3D_GGT_ruler"; #define MVAL_MAX_PX_DIST 12.0f @@ -98,6 +106,24 @@ enum { RULER_STATE_DRAG, }; +#ifdef USE_AXIS_CONSTRAINTS +/* Constrain axes */ +enum { + CONSTRAIN_AXIS_NONE = -1, + CONSTRAIN_AXIS_X = 0, + CONSTRAIN_AXIS_Y = 1, + CONSTRAIN_AXIS_Z = 2, +}; + +/* Constraining modes. + Off / Scene orientation / Global (or Local if Scene orientation is Global) */ +enum { + CONSTRAIN_MODE_OFF = 0, + CONSTRAIN_MODE_1 = 1, + CONSTRAIN_MODE_2 = 2, +}; +#endif /* USE_AXIS_CONSTRAINTS */ + struct RulerItem; typedef struct RulerInfo { @@ -106,6 +132,10 @@ typedef struct RulerInfo { int snap_flag; int state; +#ifdef USE_AXIS_CONSTRAINTS + short constrain_axis, constrain_mode; +#endif + /* wm state */ wmWindowManager *wm; wmWindow *win; @@ -394,6 +424,44 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, if (ED_gizmotypes_snap_3d_is_enabled(snap_gizmo)) { ED_gizmotypes_snap_3d_data_get(snap_gizmo, co, NULL, NULL, NULL); } + +#ifdef USE_AXIS_CONSTRAINTS + if (!(ruler_item->flag & RULERITEM_USE_ANGLE) && + ruler_info->constrain_mode != CONSTRAIN_MODE_OFF) { + + Scene *scene = DEG_get_input_scene(depsgraph); + ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); + RegionView3D *rv3d = ruler_info->region->regiondata; + Object *ob = OBACT(view_layer); + Object *obedit = OBEDIT_FROM_OBACT(ob); + + short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + + if (ruler_info->constrain_mode == CONSTRAIN_MODE_2) { + orient_index = (orient_index == V3D_ORIENT_GLOBAL) ? V3D_ORIENT_LOCAL : + V3D_ORIENT_GLOBAL; + } + + const int pivot_point = scene->toolsettings->transform_pivot_point; + float mat[3][3]; + + ED_transform_calc_orientation_from_type_ex( + scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, mat); + + invert_m3(mat); + mul_m3_m3_pre(ruler_item->co, mat); + + /* Loop through the axes and constrain the dragged point to the current constrained axis. + */ + for (int i = 0; i <= 2; i++) { + if (ruler_info->constrain_axis != i) { + ruler_item->co[inter->co_index][i] = ruler_item->co[(inter->co_index == 0) ? 2 : 0][i]; + } + } + invert_m3(mat); + mul_m3_m3_pre(ruler_item->co, mat); + } +#endif } return true; } @@ -940,6 +1008,35 @@ static int gizmo_ruler_modal(bContext *C, ruler_info->region = region; +#ifdef USE_AXIS_CONSTRAINTS + if ((event->val == KM_PRESS) && ELEM(event->type, EVT_XKEY, EVT_YKEY, EVT_ZKEY)) { + /* Go to Mode 1 if a new axis is selected. */ + if (event->type == EVT_XKEY && ruler_info->constrain_axis != CONSTRAIN_AXIS_X) { + ruler_info->constrain_axis = CONSTRAIN_AXIS_X; + ruler_info->constrain_mode = CONSTRAIN_MODE_1; + } + else if (event->type == EVT_YKEY && ruler_info->constrain_axis != CONSTRAIN_AXIS_Y) { + ruler_info->constrain_axis = CONSTRAIN_AXIS_Y; + ruler_info->constrain_mode = CONSTRAIN_MODE_1; + } + else if (event->type == EVT_ZKEY && ruler_info->constrain_axis != CONSTRAIN_AXIS_Z) { + ruler_info->constrain_axis = CONSTRAIN_AXIS_Z; + ruler_info->constrain_mode = CONSTRAIN_MODE_1; + } + else { + /* Cycle to the next mode if the same key is pressed again. */ + if (ruler_info->constrain_mode != CONSTRAIN_MODE_2) { + ruler_info->constrain_mode++; + } + else { + ruler_info->constrain_mode = CONSTRAIN_MODE_OFF; + ruler_info->constrain_axis = CONSTRAIN_AXIS_NONE; + } + } + do_cursor_update = true; + } +#endif + #ifndef USE_SNAP_DETECT_FROM_KEYMAP_HACK const bool do_snap = !(tweak_flag & WM_GIZMO_TWEAK_SNAP); #endif @@ -986,6 +1083,11 @@ static int gizmo_ruler_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) const float mval_fl[2] = {UNPACK2(event->mval)}; +#ifdef USE_AXIS_CONSTRAINTS + ruler_info->constrain_axis = CONSTRAIN_AXIS_NONE; + ruler_info->constrain_mode = CONSTRAIN_MODE_OFF; +#endif + /* select and drag */ if (gz->highlight_part == PART_LINE) { if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) { diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index e3f97dd1c63..3f572bf9d5a 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1438,7 +1438,7 @@ void VIEW3D_OT_select_lasso(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* flags */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ WM_operator_properties_gesture_lasso(ot); @@ -1953,7 +1953,8 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc, const int mval[2], eV3DSelectObjectFilter select_filter, bool do_nearest, - bool do_nearest_xray_if_supported) + bool do_nearest_xray_if_supported, + const bool do_material_slot_selection) { rcti rect; int hits15, hits9 = 0, hits5 = 0; @@ -1972,7 +1973,8 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc, view3d_opengl_select_cache_begin(); BLI_rcti_init_pt_radius(&rect, mval, 14); - hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter); + hits15 = view3d_opengl_select_ex( + vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter, do_material_slot_selection); if (hits15 == 1) { hits = selectbuffer_ret_hits_15(buffer, hits15); goto finally; @@ -2071,7 +2073,8 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, do_nearest = do_nearest && !enumerate; - int hits = mixed_bones_object_selectbuffer(vc, buffer, mval, select_filter, do_nearest, true); + int hits = mixed_bones_object_selectbuffer( + vc, buffer, mval, select_filter, do_nearest, true, false); return hits; } @@ -2088,12 +2091,14 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, int hits, Base *startbase, bool has_bones, - bool do_nearest) + bool do_nearest, + int *r_sub_selection) { ViewLayer *view_layer = vc->view_layer; View3D *v3d = vc->v3d; Base *base, *basact = NULL; int a; + int sub_selection_id = 0; if (do_nearest) { uint min = 0xFFFFFFFF; @@ -2105,6 +2110,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, if (min > buffer[4 * a + 1] && (buffer[4 * a + 3] & 0xFFFF0000)) { min = buffer[4 * a + 1]; selcol = buffer[4 * a + 3] & 0xFFFF; + sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16; } } } @@ -2118,6 +2124,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) { min = buffer[4 * a + 1]; selcol = buffer[4 * a + 3] & 0xFFFF; + sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16; } } } @@ -2184,11 +2191,16 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, } } + if (basact && r_sub_selection) { + *r_sub_selection = sub_selection_id; + } + return basact; } -/* mval comes from event->mval, only use within region handlers */ -Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) +static Base *ed_view3d_give_base_under_cursor_ex(bContext *C, + const int mval[2], + int *r_material_slot) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; @@ -2202,18 +2214,30 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) ED_view3d_viewcontext_init(C, &vc, depsgraph); const bool do_nearest = !XRAY_ACTIVE(vc.v3d); + const bool do_material_slot_selection = r_material_slot != NULL; const int hits = mixed_bones_object_selectbuffer( - &vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false); + &vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false, do_material_slot_selection); if (hits > 0) { - const bool has_bones = selectbuffer_has_bones(buffer, hits); - basact = mouse_select_eval_buffer( - &vc, buffer, hits, vc.view_layer->object_bases.first, has_bones, do_nearest); + const bool has_bones = (r_material_slot == NULL) && selectbuffer_has_bones(buffer, hits); + basact = mouse_select_eval_buffer(&vc, + buffer, + hits, + vc.view_layer->object_bases.first, + has_bones, + do_nearest, + r_material_slot); } return basact; } +/* mval comes from event->mval, only use within region handlers */ +Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) +{ + return ed_view3d_give_base_under_cursor_ex(C, mval, NULL); +} + Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2]) { Base *base = ED_view3d_give_base_under_cursor(C, mval); @@ -2223,6 +2247,17 @@ Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2]) return NULL; } +struct Object *ED_view3d_give_material_slot_under_cursor(struct bContext *C, + const int mval[2], + int *r_material_slot) +{ + Base *base = ed_view3d_give_base_under_cursor_ex(C, mval, r_material_slot); + if (base) { + return base->object; + } + return NULL; +} + bool ED_view3d_is_object_under_cursor(bContext *C, const int mval[2]) { return ED_view3d_give_object_under_cursor(C, mval) != NULL; @@ -2374,7 +2409,8 @@ static bool ed_object_select_pick(bContext *C, } } else { - basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest); + basact = mouse_select_eval_buffer( + &vc, buffer, hits, startbase, has_bones, do_nearest, NULL); } if (has_bones && basact) { @@ -2436,7 +2472,7 @@ static bool ed_object_select_pick(bContext *C, if (!changed) { /* fallback to regular object selection if no new bundles were selected, * allows to select object parented to reconstruction object */ - basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest); + basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest, NULL); } } } @@ -2677,7 +2713,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op) uint buffer[MAXPICKBUF]; const int hits = mixed_bones_object_selectbuffer( - &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true); + &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true, false); retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle); } if (!retval) { diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 86a610f8dd9..f5da7c14a88 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -137,7 +137,6 @@ void ED_view3d_smooth_view_ex( { RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore sms = {{0}}; - bool ok = false; /* initialize sms */ view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d); @@ -200,92 +199,75 @@ void ED_view3d_smooth_view_ex( sms.to_camera = true; /* restore view3d values in end */ } - /* skip smooth viewing for external render engine draw */ + if ((sview->camera_old == sview->camera) && /* Camera. */ + (sms.dst.dist == rv3d->dist) && /* Distance. */ + (sms.dst.lens == v3d->lens) && /* Lens. */ + equals_v3v3(sms.dst.ofs, rv3d->ofs) && /* Offset. */ + equals_v4v4(sms.dst.quat, rv3d->viewquat) /* Rotation. */ + ) { + /* Early return if nothing changed. */ + return; + } + + /* Skip smooth viewing for external render engine draw. */ if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { - bool changed = false; /* zero means no difference */ - if (sview->camera_old != sview->camera) { - changed = true; - } - else if (sms.dst.dist != rv3d->dist) { - changed = true; - } - else if (sms.dst.lens != v3d->lens) { - changed = true; - } - else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs)) { - changed = true; + /* original values */ + if (sview->camera_old) { + Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old); + if (sview->ofs != NULL) { + sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f); + } + ED_view3d_from_object( + ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens); } - else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat)) { - changed = true; + /* grid draw as floor */ + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { + /* use existing if exists, means multiple calls to smooth view + * won't lose the original 'view' setting */ + rv3d->view = RV3D_VIEW_USER; } - /* The new view is different from the old one - * so animate the view */ - if (changed) { - /* original values */ - if (sview->camera_old) { - Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old); - if (sview->ofs != NULL) { - sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f); - } - ED_view3d_from_object( - ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens); - } - /* grid draw as floor */ - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { - /* use existing if exists, means multiple calls to smooth view - * won't lose the original 'view' setting */ - rv3d->view = RV3D_VIEW_USER; - } - - sms.time_allowed = (double)smooth_viewtx / 1000.0; - - /* if this is view rotation only - * we can decrease the time allowed by - * the angle between quats - * this means small rotations won't lag */ - if (sview->quat && !sview->ofs && !sview->dist) { - /* scale the time allowed by the rotation */ - /* 180deg == 1.0 */ - sms.time_allowed *= (double)fabsf( - angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) / - M_PI; - } + sms.time_allowed = (double)smooth_viewtx / 1000.0; - /* ensure it shows correct */ - if (sms.to_camera) { - /* use ortho if we move from an ortho view to an ortho camera */ - Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera); - rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) && - (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ? - RV3D_ORTHO : - RV3D_PERSP); - } + /* If this is view rotation only we can decrease the time allowed by the angle between quats + * this means small rotations won't lag. */ + if (sview->quat && !sview->ofs && !sview->dist) { + /* scale the time allowed by the rotation */ + /* 180deg == 1.0 */ + sms.time_allowed *= (double)fabsf(angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) / + M_PI; + } - rv3d->rflag |= RV3D_NAVIGATING; + /* ensure it shows correct */ + if (sms.to_camera) { + /* use ortho if we move from an ortho view to an ortho camera */ + Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera); + rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) && + (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ? + RV3D_ORTHO : + RV3D_PERSP); + } - /* not essential but in some cases the caller will tag the area for redraw, and in that - * case we can get a flicker of the 'org' user view but we want to see 'src' */ - view3d_smooth_view_state_restore(&sms.src, v3d, rv3d); + rv3d->rflag |= RV3D_NAVIGATING; - /* keep track of running timer! */ - if (rv3d->sms == NULL) { - rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d"); - } - *rv3d->sms = sms; - if (rv3d->smooth_timer) { - WM_event_remove_timer(wm, win, rv3d->smooth_timer); - } - /* #TIMER1 is hard-coded in key-map. */ - rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); + /* not essential but in some cases the caller will tag the area for redraw, and in that + * case we can get a flicker of the 'org' user view but we want to see 'src' */ + view3d_smooth_view_state_restore(&sms.src, v3d, rv3d); - ok = true; + /* keep track of running timer! */ + if (rv3d->sms == NULL) { + rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d"); + } + *rv3d->sms = sms; + if (rv3d->smooth_timer) { + WM_event_remove_timer(wm, win, rv3d->smooth_timer); } + /* #TIMER1 is hard-coded in key-map. */ + rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); } - - /* if we get here nothing happens */ - if (ok == false) { + else { + /* Animation is disabled, apply immediately. */ if (sms.to_camera == false) { copy_v3_v3(rv3d->ofs, sms.dst.ofs); copy_qt_qt(rv3d->viewquat, sms.dst.quat); @@ -300,6 +282,8 @@ void ED_view3d_smooth_view_ex( } ED_region_tag_redraw(region); + + WM_event_add_mousemove(win); } } @@ -320,6 +304,7 @@ void ED_view3d_smooth_view(bContext *C, /* only meant for timer usage */ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview) { + wmWindowManager *wm = CTX_wm_manager(C); RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore *sms = rv3d->sms; float step, step_inv; @@ -333,6 +318,7 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b /* end timer */ if (step >= 1.0f) { + wmWindow *win = CTX_wm_window(C); /* if we went to camera, store the original */ if (sms->to_camera) { @@ -355,9 +341,12 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b MEM_freeN(rv3d->sms); rv3d->sms = NULL; - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer); + WM_event_remove_timer(wm, win, rv3d->smooth_timer); rv3d->smooth_timer = NULL; rv3d->rflag &= ~RV3D_NAVIGATING; + + /* Event handling won't know if a UI item has been moved under the pointer. */ + WM_event_add_mousemove(win); } else { /* ease in/out */ @@ -380,12 +369,9 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - if (ED_screen_animation_playing(CTX_wm_manager(C))) { + if (ED_screen_animation_playing(wm)) { ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); } - - /* Event handling won't know if a UI item has been moved under the pointer. */ - WM_event_add_mousemove(CTX_wm_window(C)); } if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) { @@ -964,12 +950,13 @@ static bool drw_select_filter_object_mode_lock_for_weight_paint(Object *ob, void * * \note (vc->obedit == NULL) can be set to explicitly skip edit-object selection. */ -int view3d_opengl_select(ViewContext *vc, - uint *buffer, - uint bufsize, - const rcti *input, - eV3DSelectMode select_mode, - eV3DSelectObjectFilter select_filter) +int view3d_opengl_select_ex(ViewContext *vc, + uint *buffer, + uint bufsize, + const rcti *input, + eV3DSelectMode select_mode, + eV3DSelectObjectFilter select_filter, + const bool do_material_slot_selection) { struct bThemeState theme_state; const wmWindowManager *wm = CTX_wm_manager(vc->C); @@ -1119,6 +1106,7 @@ int view3d_opengl_select(ViewContext *vc, use_obedit_skip, draw_surface, use_nearest, + do_material_slot_selection, &rect, drw_select_loop_pass, &drw_select_loop_user_data, @@ -1149,6 +1137,7 @@ int view3d_opengl_select(ViewContext *vc, use_obedit_skip, draw_surface, use_nearest, + do_material_slot_selection, &rect, drw_select_loop_pass, &drw_select_loop_user_data, @@ -1178,6 +1167,16 @@ finally: return hits; } +int view3d_opengl_select(ViewContext *vc, + uint *buffer, + uint bufsize, + const rcti *input, + eV3DSelectMode select_mode, + eV3DSelectObjectFilter select_filter) +{ + return view3d_opengl_select_ex(vc, buffer, bufsize, input, select_mode, select_filter, false); +} + int view3d_opengl_select_with_id_filter(ViewContext *vc, uint *buffer, uint bufsize, diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 013c5faa54a..d1a1937cef1 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -618,9 +618,6 @@ typedef struct TransInfo { O_SET, } orient_curr; - /** backup from view3d, to restore on end. */ - short gizmo_flag; - short prop_mode; /** Value taken as input, either through mouse coordinates or entered as a parameter. */ diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 075db30fa61..a6658ae00a3 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -51,7 +51,10 @@ /* helper struct for gp-frame transforms */ typedef struct tGPFtransdata { - float val; /* where transdata writes transform */ + union { + float val; /* where transdata writes transform */ + float loc[3]; /* #td->val and #td->loc share the same pointer. */ + }; int *sdata; /* pointer to gpf->framenum */ } tGPFtransdata; @@ -245,8 +248,8 @@ static int GPLayerToTransData(TransData *td, tfd->val = (float)gpf->framenum; tfd->sdata = &gpf->framenum; - td->val = td->loc = &tfd->val; /* XXX: It's not a 3d array. */ - td->ival = td->iloc[0] = (float)gpf->framenum; + td->val = td->loc = &tfd->val; + td->ival = td->iloc[0] = tfd->val; td->center[0] = td->ival; td->center[1] = ypos; @@ -279,16 +282,15 @@ static int MaskLayerToTransData(TransData *td, masklay_shape = masklay_shape->next) { if (is_prop_edit || (masklay_shape->flag & MASK_SHAPE_SELECT)) { if (FrameOnMouseSide(side, (float)masklay_shape->frame, cfra)) { - /* memory is calloc'ed, so that should zero everything nicely for us */ - td->val = &tfd->val; - td->ival = (float)masklay_shape->frame; + tfd->val = (float)masklay_shape->frame; + tfd->sdata = &masklay_shape->frame; + + td->val = td->loc = &tfd->val; + td->ival = td->iloc[0] = tfd->val; td->center[0] = td->ival; td->center[1] = ypos; - tfd->val = (float)masklay_shape->frame; - tfd->sdata = &masklay_shape->frame; - /* advance td now */ td++; tfd++; diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 5627a910ab4..8f896512410 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -145,30 +145,26 @@ static void autokeyframe_pose( if (act) { for (fcu = act->curves.first; fcu; fcu = fcu->next) { /* only insert keyframes for this F-Curve if it affects the current bone */ - if (strstr(fcu->rna_path, "bones") == NULL) { + char pchan_name[sizeof(pchan->name)]; + if (!BLI_str_quoted_substr(fcu->rna_path, "bones[", pchan_name, sizeof(pchan_name))) { continue; } - char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones["); /* only if bone name matches too... * NOTE: this will do constraints too, but those are ok to do here too? */ - if (pchanName) { - if (STREQ(pchanName, pchan->name)) { - insert_keyframe(bmain, - reports, - id, - act, - ((fcu->grp) ? (fcu->grp->name) : (NULL)), - fcu->rna_path, - fcu->array_index, - &anim_eval_context, - ts->keyframe_type, - &nla_cache, - flag); - } - - MEM_freeN(pchanName); + if (STREQ(pchan_name, pchan->name)) { + insert_keyframe(bmain, + reports, + id, + act, + ((fcu->grp) ? (fcu->grp->name) : (NULL)), + fcu->rna_path, + fcu->array_index, + &anim_eval_context, + ts->keyframe_type, + &nla_cache, + flag); } } } diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 9548498f0b8..a2698b342d0 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -265,7 +265,7 @@ static void free_transform_custom_data(TransCustomData *custom_data) /* Canceled, need to update the strips display. */ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips) { - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); Sequence *seq; SEQ_ITERATOR_FOREACH (seq, transformed_strips) { @@ -346,7 +346,7 @@ static bool seq_transform_check_strip_effects(SeqCollection *transformed_strips) static ListBase *seqbase_active_get(const TransInfo *t) { - Editing *ed = SEQ_editing_get(t->scene, false); + Editing *ed = SEQ_editing_get(t->scene); return SEQ_active_seqbase_get(ed); } @@ -589,7 +589,7 @@ static SeqCollection *seq_transform_collection_from_transdata(TransDataContainer static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data) { - Editing *ed = SEQ_editing_get(t->scene, false); + Editing *ed = SEQ_editing_get(t->scene); if (ed == NULL) { free_transform_custom_data(custom_data); return; @@ -622,7 +622,7 @@ void createTransSeqData(TransInfo *t) #define XXX_DURIAN_ANIM_TX_HACK Scene *scene = t->scene; - Editing *ed = SEQ_editing_get(t->scene, false); + Editing *ed = SEQ_editing_get(t->scene); TransData *td = NULL; TransData2D *td2d = NULL; TransDataSeq *tdsq = NULL; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 9f5e74db501..c493b9bd102 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -249,12 +249,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->view = v3d; t->animtimer = (animscreen) ? animscreen->animtimer : NULL; - /* turn gizmo off during transform */ - if (t->flag & T_MODAL) { - t->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag = V3D_GIZMO_HIDE; - } - if (t->scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) { t->flag |= T_V3D_ALIGN; } @@ -742,13 +736,6 @@ void postTrans(bContext *C, TransInfo *t) } } } - else if (t->spacetype == SPACE_VIEW3D) { - View3D *v3d = t->area->spacedata.first; - /* restore gizmo */ - if (t->flag & T_MODAL) { - v3d->gizmo_flag = t->gizmo_flag; - } - } if (t->mouse.data) { MEM_freeN(t->mouse.data); @@ -791,7 +778,7 @@ static void restoreElement(TransData *td) { transdata_restore_basic((TransDataBasic *)td); - if (td->val) { + if (td->val && td->val != td->loc) { *td->val = td->ival; } diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 279dca9731d..0fa179c4f74 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -674,7 +674,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C, if (ob) { float mat[3][3]; ED_transform_calc_orientation_from_type_ex( - C, mat, scene, rv3d, ob, obedit, orient_index, pivot_point); + scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, mat); copy_m4_m3(rv3d->twmat, mat); } @@ -1974,8 +1974,8 @@ void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt) gzgt->name = "3D View: Transform Gizmo"; gzgt->idname = "VIEW3D_GGT_xform_gizmo"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | - WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; + gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -2216,8 +2216,8 @@ void VIEW3D_GGT_xform_cage(wmGizmoGroupType *gzgt) gzgt->name = "Transform Cage"; gzgt->idname = "VIEW3D_GGT_xform_cage"; - gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | - WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; + gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -2459,8 +2459,8 @@ void VIEW3D_GGT_xform_shear(wmGizmoGroupType *gzgt) gzgt->name = "Transform Shear"; gzgt->idname = "VIEW3D_GGT_xform_shear"; - gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | - WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; + gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 8df95222fa1..b9fb8a86752 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1232,14 +1232,24 @@ void transform_mode_default_modal_orientation_set(TransInfo *t, int type) return; } + View3D *v3d = NULL; RegionView3D *rv3d = NULL; if ((type == V3D_ORIENT_VIEW) && (t->spacetype == SPACE_VIEW3D) && t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) { + v3d = t->view; 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); + t->scene, + t->view_layer, + v3d, + rv3d, + NULL, + NULL, + type, + V3D_AROUND_CENTER_BOUNDS, + t->orient[O_DEFAULT].matrix); if (t->orient_curr == O_DEFAULT) { /* Update Orientation. */ diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 9638ec8750e..3a4a9342e18 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -911,7 +911,8 @@ static void TRANSFORM_OT_bend(struct wmOperatorType *ot) ot->name = "Bend"; ot->description = "Bend selected items between the 3D cursor and the mouse"; ot->idname = OP_BEND; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + /* Depend on cursor location because the cursor location is used to define the region to bend. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_DEPENDS_ON_CURSOR; /* api callbacks */ ot->invoke = transform_invoke; @@ -1091,7 +1092,7 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) ot->name = "Edge Slide"; ot->description = "Slide an edge loop along a mesh"; ot->idname = OP_EDGE_SLIDE; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_DEPENDS_ON_CURSOR; /* api callbacks */ ot->invoke = transform_invoke; @@ -1129,7 +1130,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot) ot->name = "Vertex Slide"; ot->description = "Slide a vertex along a mesh"; ot->idname = OP_VERT_SLIDE; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_DEPENDS_ON_CURSOR; /* api callbacks */ ot->invoke = transform_invoke; diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 33f4b06eb0e..1e3acdf1071 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -490,13 +490,14 @@ void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3 Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = CTX_data_edit_object(C); + View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = region->regiondata; Object *ob = OBACT(view_layer); const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); const int pivot_point = scene->toolsettings->transform_pivot_point; ED_transform_calc_orientation_from_type_ex( - C, r_mat, scene, rv3d, ob, obedit, orient_index, pivot_point); + scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, r_mat); } /** @@ -508,15 +509,15 @@ void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3 * - #V3D_ORIENT_LOCAL may contain shear from non-uniform scale in parent/child relationships. * - #V3D_ORIENT_CUSTOM may have been created from #V3D_ORIENT_LOCAL. */ -short ED_transform_calc_orientation_from_type_ex(const bContext *C, - float r_mat[3][3], - /* extra args (can be accessed from context) */ - Scene *scene, - RegionView3D *rv3d, +short ED_transform_calc_orientation_from_type_ex(const Scene *scene, + ViewLayer *view_layer, + const View3D *v3d, + const RegionView3D *rv3d, Object *ob, Object *obedit, const short orientation_index, - const int pivot_point) + const int pivot_point, + float r_mat[3][3]) { switch (orientation_index) { case V3D_ORIENT_GIMBAL: { @@ -528,7 +529,7 @@ short ED_transform_calc_orientation_from_type_ex(const bContext *C, } case V3D_ORIENT_NORMAL: { if (obedit || (ob && ob->mode & OB_MODE_POSE)) { - ED_getTransformOrientationMatrix(C, ob, obedit, pivot_point, r_mat); + ED_getTransformOrientationMatrix(view_layer, v3d, ob, obedit, pivot_point, r_mat); break; } /* No break we define 'normal' as 'local' in Object mode. */ @@ -541,7 +542,7 @@ short ED_transform_calc_orientation_from_type_ex(const bContext *C, * use the active pones axis for display T33575, this works as expected on a single * bone and users who select many bones will understand what's going on and what local * means when they start transforming. */ - ED_getTransformOrientationMatrix(C, ob, obedit, pivot_point, r_mat); + ED_getTransformOrientationMatrix(view_layer, v3d, ob, obedit, pivot_point, r_mat); } else { transform_orientations_create_from_axis(r_mat, UNPACK3(ob->obmat)); @@ -604,9 +605,11 @@ short transform_orientation_matrix_get(bContext *C, Object *ob = CTX_data_active_object(C); Object *obedit = CTX_data_edit_object(C); Scene *scene = t->scene; + View3D *v3d = NULL; RegionView3D *rv3d = NULL; if ((t->spacetype == SPACE_VIEW3D) && t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) { + v3d = t->view; rv3d = t->region->regiondata; if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) { @@ -619,15 +622,7 @@ short transform_orientation_matrix_get(bContext *C, } short r_orient_index = ED_transform_calc_orientation_from_type_ex( - C, - r_spacemtx, - /* extra args (can be accessed from context) */ - scene, - rv3d, - ob, - obedit, - orient_index, - t->around); + scene, t->view_layer, v3d, rv3d, ob, obedit, orient_index, t->around, r_spacemtx); if (rv3d && (t->options & CTX_PAINT_CURVE)) { /* Screen space in the 3d region. */ @@ -757,15 +752,14 @@ static uint bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const uint n) } #endif -int getTransformOrientation_ex(const bContext *C, +int getTransformOrientation_ex(ViewLayer *view_layer, + const View3D *v3d, struct Object *ob, struct Object *obedit, float normal[3], float plane[3], const short around) { - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); int result = ORIENTATION_NONE; const bool activeOnly = (around == V3D_AROUND_ACTIVE); @@ -1296,12 +1290,16 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3]) /* dummy value, not V3D_AROUND_ACTIVE and not V3D_AROUND_LOCAL_ORIGINS */ short around = V3D_AROUND_CENTER_BOUNDS; - return getTransformOrientation_ex(C, obact, obedit, normal, plane, around); + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + + return getTransformOrientation_ex(view_layer, v3d, obact, obedit, normal, plane, around); } -void ED_getTransformOrientationMatrix(const bContext *C, - struct Object *ob, - struct Object *obedit, +void ED_getTransformOrientationMatrix(ViewLayer *view_layer, + const View3D *v3d, + Object *ob, + Object *obedit, const short around, float r_orientation_mat[3][3]) { @@ -1310,7 +1308,7 @@ void ED_getTransformOrientationMatrix(const bContext *C, int type; - type = getTransformOrientation_ex(C, ob, obedit, normal, plane, around); + type = getTransformOrientation_ex(view_layer, v3d, ob, obedit, normal, plane, around); /* Fallback, when the plane can't be calculated. */ if (ORIENTATION_USE_PLANE(type) && is_zero_v3(plane)) { diff --git a/source/blender/editors/transform/transform_orientations.h b/source/blender/editors/transform/transform_orientations.h index de8c9b165c1..1da369c8307 100644 --- a/source/blender/editors/transform/transform_orientations.h +++ b/source/blender/editors/transform/transform_orientations.h @@ -58,7 +58,8 @@ enum { }; #define ORIENTATION_USE_PLANE(ty) ELEM(ty, ORIENTATION_NORMAL, ORIENTATION_EDGE, ORIENTATION_FACE) -int getTransformOrientation_ex(const struct bContext *C, +int getTransformOrientation_ex(ViewLayer *view_layer, + const View3D *v3d, struct Object *ob, struct Object *obedit, float normal[3], diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index bb04f557074..811f30c96e5 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -501,9 +501,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, } Object *obj_eval = DEG_get_evaluated_object(depsgraph, base->object); - if (obj_eval->transflag & OB_DUPLI || - (obj_eval->runtime.geometry_set_eval != NULL && - BKE_geometry_set_has_instances(obj_eval->runtime.geometry_set_eval))) { + if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { ListBase *lb = object_duplilist(depsgraph, sctx->scene, obj_eval); for (DupliObject *dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { BLI_assert(DEG_is_evaluated_object(dupli_ob->ob)); diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index 6e926f36fba..e82a00bcc77 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -137,7 +137,7 @@ static SeqCollection *seq_collection_extract_effects(SeqCollection *collection) static SeqCollection *query_snap_targets(const TransInfo *t, SeqCollection *snap_sources) { - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); const short snap_flag = SEQ_tool_settings_snap_flag_get(t->scene); SeqCollection *snap_targets = SEQ_collection_create(__func__); LISTBASE_FOREACH (Sequence *, seq, seqbase) { @@ -255,12 +255,12 @@ static int seq_snap_threshold_get_frame_distance(const TransInfo *t) TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) { TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__); - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); SeqCollection *snap_sources = SEQ_query_selected_strips(seqbase); SeqCollection *snap_targets = query_snap_targets(t, snap_sources); - if (SEQ_collection_len(snap_sources) == 0 || SEQ_collection_len(snap_targets) == 0) { + if (SEQ_collection_len(snap_sources) == 0) { SEQ_collection_free(snap_targets); SEQ_collection_free(snap_sources); MEM_freeN(snap_data); diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 3e0029156c1..22064e04e86 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -574,7 +574,12 @@ static bool ed_undo_is_init_poll(bContext *C) { wmWindowManager *wm = CTX_wm_manager(C); if (wm->undo_stack == NULL) { - CTX_wm_operator_poll_msg_set(C, "Undo disabled at startup"); + /* This message is intended for Python developers, + * it will be part of the exception when attempting to call undo in background mode. */ + CTX_wm_operator_poll_msg_set( + C, + "Undo disabled at startup in background-mode " + "(call `ed.undo_push()` to explicitly initialize the undo-system)"); return false; } return true; diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index 9e05efca3df..fcbc0807893 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -565,7 +565,7 @@ bool ED_imbuf_sample_poll(bContext *C) return false; } - return sseq && SEQ_editing_get(CTX_data_scene(C), false) != NULL; + return sseq && SEQ_editing_get(CTX_data_scene(C)) != NULL; } return false; diff --git a/source/blender/editors/uvedit/uvedit_rip.c b/source/blender/editors/uvedit/uvedit_rip.c index 631b831411f..7e4b18340c5 100644 --- a/source/blender/editors/uvedit/uvedit_rip.c +++ b/source/blender/editors/uvedit/uvedit_rip.c @@ -953,7 +953,7 @@ void UV_OT_rip(wmOperatorType *ot) ot->name = "UV Rip"; ot->description = "Rip selected vertices or a selected region"; ot->idname = "UV_OT_rip"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* api callbacks */ ot->exec = uv_rip_exec; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 5a82cd31112..c0ccf1b7095 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3450,7 +3450,7 @@ void UV_OT_select_lasso(wmOperatorType *ot) ot->cancel = WM_gesture_lasso_cancel; /* flags */ - ot->flag = OPTYPE_UNDO; + ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; /* properties */ WM_operator_properties_gesture_lasso(ot); diff --git a/source/blender/freestyle/intern/application/AppConfig.h b/source/blender/freestyle/intern/application/AppConfig.h index 61beff33876..adb6d906e68 100644 --- a/source/blender/freestyle/intern/application/AppConfig.h +++ b/source/blender/freestyle/intern/application/AppConfig.h @@ -115,10 +115,6 @@ static const string OPTIONS_FILE("options.xml"); static const string OPTIONS_CURRENT_DIRS_FILE("current_dirs.xml"); static const string OPTIONS_QGLVIEWER_FILE("qglviewer.xml"); -// Default options -static const real DEFAULT_SPHERE_RADIUS = 1.0; -static const real DEFAULT_DKR_EPSILON = 0.0; - } // namespace Config } /* namespace Freestyle */ diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index 7772a30c5f4..c74fd60fe35 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -65,9 +65,6 @@ using namespace Freestyle; extern "C" { -#define DEFAULT_SPHERE_RADIUS 1.0f -#define DEFAULT_DKR_EPSILON 0.0f - struct FreestyleGlobals g_freestyle; // Freestyle configuration @@ -433,14 +430,8 @@ static void prepare(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph) } // set parameters - if (config->flags & FREESTYLE_ADVANCED_OPTIONS_FLAG) { - controller->setSphereRadius(config->sphere_radius); - controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon); - } - else { - controller->setSphereRadius(DEFAULT_SPHERE_RADIUS); - controller->setSuggestiveContourKrDerivativeEpsilon(DEFAULT_DKR_EPSILON); - } + controller->setSphereRadius(config->sphere_radius); + controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon); controller->setFaceSmoothness((config->flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) ? true : false); controller->setCreaseAngle(RAD2DEGF(config->crease_angle)); controller->setVisibilityAlgo((config->flags & FREESTYLE_CULLING) ? diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index f8d2acc74a8..856668f01d7 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -28,14 +28,21 @@ set(INC_SYS set(SRC intern/cpp_types.cc + intern/field.cc intern/generic_vector_array.cc intern/generic_virtual_array.cc intern/generic_virtual_vector_array.cc intern/multi_function.cc intern/multi_function_builder.cc + intern/multi_function_parallel.cc + intern/multi_function_procedure.cc + intern/multi_function_procedure_builder.cc + intern/multi_function_procedure_executor.cc FN_cpp_type.hh FN_cpp_type_make.hh + FN_field.hh + FN_field_cpp_type.hh FN_generic_pointer.hh FN_generic_span.hh FN_generic_value_map.hh @@ -48,6 +55,10 @@ set(SRC FN_multi_function_data_type.hh FN_multi_function_param_type.hh FN_multi_function_params.hh + FN_multi_function_parallel.hh + FN_multi_function_procedure.hh + FN_multi_function_procedure_builder.hh + FN_multi_function_procedure_executor.hh FN_multi_function_signature.hh ) @@ -55,13 +66,31 @@ set(LIB bf_blenlib ) +if(WITH_TBB) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${TBB_LIBRARIES} + ) +endif() + blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/FN_cpp_type_test.cc + tests/FN_field_test.cc tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc + tests/FN_multi_function_procedure_test.cc tests/FN_multi_function_test.cc ) set(TEST_LIB diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh new file mode 100644 index 00000000000..d4375b625ce --- /dev/null +++ b/source/blender/functions/FN_field.hh @@ -0,0 +1,487 @@ +/* + * 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 fn + * + * A #Field represents a function that outputs a value based on an arbitrary number of inputs. The + * inputs for a specific field evaluation are provided by a #FieldContext. + * + * A typical example is a field that computes a displacement vector for every vertex on a mesh + * based on its position. + * + * Fields can be build, composed and evaluated at run-time. They are stored in a directed tree + * graph data structure, whereby each node is a #FieldNode and edges are dependencies. A #FieldNode + * has an arbitrary number of inputs and at least one output and a #Field references a specific + * output of a #FieldNode. The inputs of a #FieldNode are other fields. + * + * There are two different types of field nodes: + * - #FieldInput: Has no input and exactly one output. It represents an input to the entire field + * when it is evaluated. During evaluation, the value of this input is based on a #FieldContext. + * - #FieldOperation: Has an arbitrary number of field inputs and at least one output. Its main + * use is to compose multiple existing fields into new fields. + * + * When fields are evaluated, they are converted into a multi-function procedure which allows + * efficient computation. In the future, we might support different field evaluation mechanisms for + * e.g. the following scenarios: + * - Latency of a single evaluation is more important than throughput. + * - Evaluation should happen on other hardware like GPUs. + * + * Whenever possible, multiple fields should be evaluated together to avoid duplicate work when + * they share common sub-fields and a common context. + */ + +#include "BLI_function_ref.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "FN_generic_virtual_array.hh" +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_procedure.hh" +#include "FN_multi_function_procedure_builder.hh" +#include "FN_multi_function_procedure_executor.hh" + +namespace blender::fn { + +class FieldInput; + +/** + * A node in a field-tree. It has at least one output that can be referenced by fields. + */ +class FieldNode { + private: + bool is_input_; + /** + * True when this node is a #FieldInput or (potentially indirectly) depends on one. This could + * always be derived again later by traversing the field-tree, but keeping track of it while the + * field is built is cheaper. + * + * If this is false, the field is constant. Note that even when this is true, the field may be + * constant when all inputs are constant. + */ + bool depends_on_input_; + + public: + FieldNode(bool is_input, bool depends_on_input) + : is_input_(is_input), depends_on_input_(depends_on_input) + { + } + + virtual ~FieldNode() = default; + + virtual const CPPType &output_cpp_type(int output_index) const = 0; + + bool is_input() const + { + return is_input_; + } + + bool is_operation() const + { + return !is_input_; + } + + bool depends_on_input() const + { + return depends_on_input_; + } + + /** + * Invoke callback for every field input. It might be called multiple times for the same input. + * The caller is responsible for deduplication if required. + */ + virtual void foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const = 0; + + virtual uint64_t hash() const + { + return get_default_hash(this); + } + + friend bool operator==(const FieldNode &a, const FieldNode &b) + { + return a.is_equal_to(b); + } + + friend bool operator!=(const FieldNode &a, const FieldNode &b) + { + return !(a == b); + } + + virtual bool is_equal_to(const FieldNode &other) const + { + return this == &other; + } +}; + +/** + * Common base class for fields to avoid declaring the same methods for #GField and #GFieldRef. + */ +template<typename NodePtr> class GFieldBase { + protected: + NodePtr node_ = nullptr; + int node_output_index_ = 0; + + GFieldBase(NodePtr node, const int node_output_index) + : node_(std::move(node)), node_output_index_(node_output_index) + { + } + + public: + GFieldBase() = default; + + operator bool() const + { + return node_ != nullptr; + } + + friend bool operator==(const GFieldBase &a, const GFieldBase &b) + { + /* Two nodes can compare equal even when their pointer is not the same. For example, two + * "Position" nodes are the same. */ + return *a.node_ == *b.node_ && a.node_output_index_ == b.node_output_index_; + } + + uint64_t hash() const + { + return get_default_hash_2(*node_, node_output_index_); + } + + const fn::CPPType &cpp_type() const + { + return node_->output_cpp_type(node_output_index_); + } + + const FieldNode &node() const + { + return *node_; + } + + int node_output_index() const + { + return node_output_index_; + } +}; + +/** + * A field whose output type is only known at run-time. + */ +class GField : public GFieldBase<std::shared_ptr<FieldNode>> { + public: + GField() = default; + + GField(std::shared_ptr<FieldNode> node, const int node_output_index = 0) + : GFieldBase<std::shared_ptr<FieldNode>>(std::move(node), node_output_index) + { + } +}; + +/** + * Same as #GField but is cheaper to copy/move around, because it does not contain a + * #std::shared_ptr. + */ +class GFieldRef : public GFieldBase<const FieldNode *> { + public: + GFieldRef() = default; + + GFieldRef(const GField &field) + : GFieldBase<const FieldNode *>(&field.node(), field.node_output_index()) + { + } + + GFieldRef(const FieldNode &node, const int node_output_index = 0) + : GFieldBase<const FieldNode *>(&node, node_output_index) + { + } +}; + +/** + * A typed version of #GField. It has the same memory layout as #GField. + */ +template<typename T> class Field : public GField { + public: + Field() = default; + + Field(GField field) : GField(std::move(field)) + { + BLI_assert(this->cpp_type().template is<T>()); + } + + Field(std::shared_ptr<FieldNode> node, const int node_output_index = 0) + : Field(GField(std::move(node), node_output_index)) + { + } +}; + +/** + * A #FieldNode that allows composing existing fields into new fields. + */ +class FieldOperation : public FieldNode { + /** + * The multi-function used by this node. It is optionally owned. + * Multi-functions with mutable or vector parameters are not supported currently. + */ + std::unique_ptr<const MultiFunction> owned_function_; + const MultiFunction *function_; + + /** Inputs to the operation. */ + blender::Vector<GField> inputs_; + + public: + FieldOperation(std::unique_ptr<const MultiFunction> function, Vector<GField> inputs = {}); + FieldOperation(const MultiFunction &function, Vector<GField> inputs = {}); + + Span<GField> inputs() const + { + return inputs_; + } + + const MultiFunction &multi_function() const + { + return *function_; + } + + const CPPType &output_cpp_type(int output_index) const override + { + int output_counter = 0; + for (const int param_index : function_->param_indices()) { + MFParamType param_type = function_->param_type(param_index); + if (param_type.is_output()) { + if (output_counter == output_index) { + return param_type.data_type().single_type(); + } + output_counter++; + } + } + BLI_assert_unreachable(); + return CPPType::get<float>(); + } + + void foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const override; +}; + +class FieldContext; + +/** + * A #FieldNode that represents an input to the entire field-tree. + */ +class FieldInput : public FieldNode { + protected: + const CPPType *type_; + std::string debug_name_; + + public: + FieldInput(const CPPType &type, std::string debug_name = ""); + + /** + * Get the value of this specific input based on the given context. The returned virtual array, + * should live at least as long as the passed in #scope. May return null. + */ + virtual const GVArray *get_varray_for_context(const FieldContext &context, + IndexMask mask, + ResourceScope &scope) const = 0; + + virtual std::string socket_inspection_name() const + { + return debug_name_; + } + + blender::StringRef debug_name() const + { + return debug_name_; + } + + const CPPType &cpp_type() const + { + return *type_; + } + + const CPPType &output_cpp_type(int output_index) const override + { + BLI_assert(output_index == 0); + UNUSED_VARS_NDEBUG(output_index); + return *type_; + } + + void foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const override; +}; + +/** + * Provides inputs for a specific field evaluation. + */ +class FieldContext { + public: + ~FieldContext() = default; + + virtual const GVArray *get_varray_for_input(const FieldInput &field_input, + IndexMask mask, + ResourceScope &scope) const; +}; + +/** + * Utility class that makes it easier to evaluate fields. + */ +class FieldEvaluator : NonMovable, NonCopyable { + private: + struct OutputPointerInfo { + void *dst = nullptr; + /* When a destination virtual array is provided for an input, this is + * unnecessary, otherwise this is used to construct the required virtual array. */ + void (*set)(void *dst, const GVArray &varray, ResourceScope &scope) = nullptr; + }; + + ResourceScope scope_; + const FieldContext &context_; + const IndexMask mask_; + Vector<GField> fields_to_evaluate_; + Vector<GVMutableArray *> dst_varrays_; + Vector<const GVArray *> evaluated_varrays_; + Vector<OutputPointerInfo> output_pointer_infos_; + bool is_evaluated_ = false; + + public: + /** Takes #mask by pointer because the mask has to live longer than the evaluator. */ + FieldEvaluator(const FieldContext &context, const IndexMask *mask) + : context_(context), mask_(*mask) + { + } + + /** Construct a field evaluator for all indices less than #size. */ + FieldEvaluator(const FieldContext &context, const int64_t size) : context_(context), mask_(size) + { + } + + ~FieldEvaluator() + { + /* While this assert isn't strictly necessary, and could be replaced with a warning, + * it will catch cases where someone forgets to call #evaluate(). */ + BLI_assert(is_evaluated_); + } + + /** + * \param field: Field to add to the evaluator. + * \param dst: Mutable virtual array that the evaluated result for this field is be written into. + */ + int add_with_destination(GField field, GVMutableArray &dst); + + /** Same as #add_with_destination but typed. */ + template<typename T> int add_with_destination(Field<T> field, VMutableArray<T> &dst) + { + GVMutableArray &varray = scope_.construct<GVMutableArray_For_VMutableArray<T>>(dst); + return this->add_with_destination(GField(std::move(field)), varray); + } + + /** + * \param field: Field to add to the evaluator. + * \param dst: Mutable span that the evaluated result for this field is be written into. + * \note: When the output may only be used as a single value, the version of this function with + * a virtual array result array should be used. + */ + int add_with_destination(GField field, GMutableSpan dst); + + /** + * \param field: Field to add to the evaluator. + * \param dst: Mutable span that the evaluated result for this field is be written into. + * \note: When the output may only be used as a single value, the version of this function with + * a virtual array result array should be used. + */ + template<typename T> int add_with_destination(Field<T> field, MutableSpan<T> dst) + { + GVMutableArray &varray = scope_.construct<GVMutableArray_For_MutableSpan<T>>(dst); + return this->add_with_destination(std::move(field), varray); + } + + int add(GField field, const GVArray **varray_ptr); + + /** + * \param field: Field to add to the evaluator. + * \param varray_ptr: Once #evaluate is called, the resulting virtual array will be will be + * assigned to the given position. + * \return Index of the field in the evaluator which can be used in the #get_evaluated methods. + */ + template<typename T> int add(Field<T> field, const VArray<T> **varray_ptr) + { + const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); + dst_varrays_.append(nullptr); + output_pointer_infos_.append( + OutputPointerInfo{varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &scope) { + *(const VArray<T> **)dst = &*scope.construct<GVArray_Typed<T>>(varray); + }}); + return field_index; + } + + /** + * \return Index of the field in the evaluator which can be used in the #get_evaluated methods. + */ + int add(GField field); + + /** + * Evaluate all fields on the evaluator. This can only be called once. + */ + void evaluate(); + + const GVArray &get_evaluated(const int field_index) const + { + BLI_assert(is_evaluated_); + return *evaluated_varrays_[field_index]; + } + + template<typename T> const VArray<T> &get_evaluated(const int field_index) + { + const GVArray &varray = this->get_evaluated(field_index); + GVArray_Typed<T> &typed_varray = scope_.construct<GVArray_Typed<T>>(varray); + return *typed_varray; + } + + /** + * Retrieve the output of an evaluated boolean field and convert it to a mask, which can be used + * to avoid calculations for unnecessary elements later on. The evaluator will own the indices in + * some cases, so it must live at least as long as the returned mask. + */ + IndexMask get_evaluated_as_mask(const int field_index); +}; + +Vector<const GVArray *> evaluate_fields(ResourceScope &scope, + Span<GFieldRef> fields_to_evaluate, + IndexMask mask, + const FieldContext &context, + Span<GVMutableArray *> dst_varrays = {}); + +/* -------------------------------------------------------------------- + * Utility functions for simple field creation and evaluation. + */ + +void evaluate_constant_field(const GField &field, void *r_value); + +template<typename T> T evaluate_constant_field(const Field<T> &field) +{ + T value; + value.~T(); + evaluate_constant_field(field, &value); + return value; +} + +template<typename T> Field<T> make_constant_field(T value) +{ + auto constant_fn = std::make_unique<fn::CustomMF_Constant<T>>(std::forward<T>(value)); + auto operation = std::make_shared<FieldOperation>(std::move(constant_fn)); + return Field<T>{GField{std::move(operation), 0}}; +} + +GField make_field_constant_if_possible(GField field); + +} // namespace blender::fn diff --git a/source/blender/functions/FN_field_cpp_type.hh b/source/blender/functions/FN_field_cpp_type.hh new file mode 100644 index 00000000000..5e6f1b5a585 --- /dev/null +++ b/source/blender/functions/FN_field_cpp_type.hh @@ -0,0 +1,72 @@ +/* + * 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 fn + */ + +#include "FN_cpp_type_make.hh" +#include "FN_field.hh" + +namespace blender::fn { + +template<typename T> struct FieldCPPTypeParam { +}; + +class FieldCPPType : public CPPType { + private: + const CPPType &field_type_; + + public: + template<typename T> + FieldCPPType(FieldCPPTypeParam<Field<T>> /* unused */, StringRef debug_name) + : CPPType(CPPTypeParam<Field<T>, CPPTypeFlags::None>(), debug_name), + field_type_(CPPType::get<T>()) + { + } + + const CPPType &field_type() const + { + return field_type_; + } + + /* Ensure that #GField and #Field<T> have the same layout, to enable casting between the two. */ + static_assert(sizeof(Field<int>) == sizeof(GField)); + static_assert(sizeof(Field<int>) == sizeof(Field<std::string>)); + + const GField &get_gfield(const void *field) const + { + return *(const GField *)field; + } + + void construct_from_gfield(void *r_value, const GField &gfield) const + { + new (r_value) GField(gfield); + } +}; + +} // namespace blender::fn + +#define MAKE_FIELD_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \ + template<> \ + const blender::fn::CPPType &blender::fn::CPPType::get_impl<blender::fn::Field<FIELD_TYPE>>() \ + { \ + static blender::fn::FieldCPPType cpp_type{ \ + blender::fn::FieldCPPTypeParam<blender::fn::Field<FIELD_TYPE>>(), STRINGIFY(DEBUG_NAME)}; \ + return cpp_type; \ + } diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index f429243e2de..703118ba23e 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -911,4 +911,50 @@ template<typename T> class GVMutableArray_Typed { } }; +class GVArray_For_SlicedGVArray : public GVArray { + protected: + const GVArray &varray_; + int64_t offset_; + + public: + GVArray_For_SlicedGVArray(const GVArray &varray, const IndexRange slice) + : GVArray(varray.type(), slice.size()), varray_(varray), offset_(slice.start()) + { + BLI_assert(slice.one_after_last() <= varray.size()); + } + + /* TODO: Add #materialize method. */ + 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; +}; + +/** + * Utility class to create the "best" sliced virtual array. + */ +class GVArray_Slice { + private: + const GVArray *varray_; + /* Of these optional virtual arrays, at most one is constructed at any time. */ + std::optional<GVArray_For_GSpan> varray_span_; + std::optional<GVArray_For_SlicedGVArray> varray_any_; + + public: + GVArray_Slice(const GVArray &varray, const IndexRange slice); + + const GVArray &operator*() + { + return *varray_; + } + + const GVArray *operator->() + { + return varray_; + } + + operator const GVArray &() + { + return *varray_; + } +}; + } // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh index f6c4addfb52..98788025558 100644 --- a/source/blender/functions/FN_multi_function.hh +++ b/source/blender/functions/FN_multi_function.hh @@ -121,8 +121,13 @@ class MultiFunction { } }; -inline MFParamsBuilder::MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size) - : MFParamsBuilder(fn.signature(), min_array_size) +inline MFParamsBuilder::MFParamsBuilder(const MultiFunction &fn, int64_t mask_size) + : MFParamsBuilder(fn.signature(), IndexMask(mask_size)) +{ +} + +inline MFParamsBuilder::MFParamsBuilder(const MultiFunction &fn, const IndexMask *mask) + : MFParamsBuilder(fn.signature(), *mask) { } diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index 7a526bb640b..0ce05cbca30 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -326,18 +326,21 @@ template<typename From, typename To> class CustomMF_Convert : public MultiFuncti /** * A multi-function that outputs the same value every time. The value is not owned by an instance - * of this function. The caller is responsible for destructing and freeing the value. + * of this function. If #make_value_copy is false, the caller is responsible for destructing and + * freeing the value. */ class CustomMF_GenericConstant : public MultiFunction { private: const CPPType &type_; const void *value_; MFSignature signature_; + bool owns_value_; template<typename T> friend class CustomMF_Constant; public: - CustomMF_GenericConstant(const CPPType &type, const void *value); + CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy); + ~CustomMF_GenericConstant(); void call(IndexMask mask, MFParams params, MFContext context) const override; uint64_t hash() const override; bool equals(const MultiFunction &other) const override; @@ -417,4 +420,13 @@ class CustomMF_DefaultOutput : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext context) const override; }; +class CustomMF_GenericCopy : public MultiFunction { + private: + MFSignature signature_; + + public: + CustomMF_GenericCopy(StringRef name, MFDataType data_type); + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; + } // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_parallel.hh b/source/blender/functions/FN_multi_function_parallel.hh new file mode 100644 index 00000000000..84c57efd434 --- /dev/null +++ b/source/blender/functions/FN_multi_function_parallel.hh @@ -0,0 +1,39 @@ +/* + * 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 fn + */ + +#include "FN_multi_function.hh" + +namespace blender::fn { + +class ParallelMultiFunction : public MultiFunction { + private: + const MultiFunction &fn_; + const int64_t grain_size_; + bool threading_supported_; + + public: + ParallelMultiFunction(const MultiFunction &fn, const int64_t grain_size); + + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; + +} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index a480287d578..fe4d2b90d80 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -38,6 +38,7 @@ class MFParamsBuilder { private: ResourceScope scope_; const MFSignature *signature_; + IndexMask mask_; int64_t min_array_size_; Vector<const GVArray *> virtual_arrays_; Vector<GMutableSpan> mutable_spans_; @@ -46,35 +47,39 @@ class MFParamsBuilder { friend class MFParams; - public: - MFParamsBuilder(const MFSignature &signature, int64_t min_array_size) - : signature_(&signature), min_array_size_(min_array_size) + MFParamsBuilder(const MFSignature &signature, const IndexMask mask) + : signature_(&signature), mask_(mask), min_array_size_(mask.min_array_size()) { } - MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size); + public: + MFParamsBuilder(const class MultiFunction &fn, int64_t size); + /** + * The indices referenced by the #mask has to live longer than the params builder. This is + * because the it might have to destruct elements for all masked indices in the end. + */ + MFParamsBuilder(const class MultiFunction &fn, const IndexMask *mask); template<typename T> void add_readonly_single_input_value(T value, StringRef expected_name = "") { - T *value_ptr = &scope_.add_value<T>(std::move(value), __func__); + T *value_ptr = &scope_.add_value<T>(std::move(value)); this->add_readonly_single_input(value_ptr, expected_name); } template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>( - __func__, CPPType::get<T>(), min_array_size_, value), - expected_name); + this->add_readonly_single_input( + scope_.construct<GVArray_For_SingleValueRef>(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<GVArray_For_GSpan>(__func__, span), - expected_name); + this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(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); + this->add_readonly_single_input( + scope_.construct<GVArray_For_SingleValueRef>(*value.type(), min_array_size_, value.get()), + expected_name); } void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "") { @@ -85,13 +90,13 @@ class MFParamsBuilder { void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "") { - this->add_readonly_vector_input( - scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name); + this->add_readonly_vector_input(scope_.construct<GVVectorArray_For_GVectorArray>(vector_array), + expected_name); } void add_readonly_vector_input(const GSpan single_vector, StringRef expected_name = "") { this->add_readonly_vector_input( - scope_.construct<GVVectorArray_For_SingleGSpan>(__func__, single_vector, min_array_size_), + scope_.construct<GVVectorArray_For_SingleGSpan>(single_vector, min_array_size_), expected_name); } void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") @@ -112,6 +117,17 @@ class MFParamsBuilder { BLI_assert(ref.size() >= min_array_size_); mutable_spans_.append(ref); } + void add_ignored_single_output(StringRef expected_name = "") + { + this->assert_current_param_name(expected_name); + const int param_index = this->current_param_index(); + const MFParamType ¶m_type = signature_->param_types[param_index]; + BLI_assert(param_type.category() == MFParamType::SingleOutput); + const CPPType &type = param_type.data_type().single_type(); + /* An empty span indicates that this is ignored. */ + const GMutableSpan dummy_span{type}; + mutable_spans_.append(dummy_span); + } void add_vector_output(GVectorArray &vector_array, StringRef expected_name = "") { @@ -176,6 +192,19 @@ class MFParamsBuilder { #endif } + void assert_current_param_name(StringRef expected_name) + { + UNUSED_VARS_NDEBUG(expected_name); +#ifdef DEBUG + if (expected_name.is_empty()) { + return; + } + const int param_index = this->current_param_index(); + StringRef actual_name = signature_->param_names[param_index]; + BLI_assert(actual_name == expected_name); +#endif + } + int current_param_index() const { return virtual_arrays_.size() + mutable_spans_.size() + virtual_vector_arrays_.size() + @@ -195,7 +224,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<VArray_For_GVArray<T>>(__func__, array); + return builder_->scope_.construct<GVArray_Typed<T>>(array); } const GVArray &readonly_single_input(int param_index, StringRef name = "") { @@ -204,6 +233,19 @@ class MFParams { return *builder_->virtual_arrays_[data_index]; } + /** + * \return True when the caller provided a buffer for this output parameter. This allows the + * called multi-function to skip some computation. It is still valid to call + * #uninitialized_single_output when this returns false. In this case a new temporary buffer is + * allocated. + */ + bool single_output_is_required(int param_index, StringRef name = "") + { + this->assert_correct_param(param_index, name, MFParamType::SingleOutput); + int data_index = builder_->signature_->data_index(param_index); + return !builder_->mutable_spans_[data_index].is_empty(); + } + template<typename T> MutableSpan<T> uninitialized_single_output(int param_index, StringRef name = "") { @@ -213,14 +255,28 @@ class MFParams { { this->assert_correct_param(param_index, name, MFParamType::SingleOutput); int data_index = builder_->signature_->data_index(param_index); - return builder_->mutable_spans_[data_index]; + GMutableSpan span = builder_->mutable_spans_[data_index]; + if (span.is_empty()) { + /* The output is ignored by the caller, but the multi-function does not handle this case. So + * create a temporary buffer that the multi-function can write to. */ + const CPPType &type = span.type(); + void *buffer = builder_->scope_.linear_allocator().allocate( + builder_->min_array_size_ * type.size(), type.alignment()); + if (!type.is_trivially_destructible()) { + /* Make sure the temporary elements will be destructed in the end. */ + builder_->scope_.add_destruct_call( + [&type, buffer, mask = builder_->mask_]() { type.destruct_indices(buffer, mask); }); + } + span = GMutableSpan{type, buffer, builder_->min_array_size_}; + } + return span; } template<typename T> 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<VVectorArray_For_GVVectorArray<T>>(__func__, vector_array); + return builder_->scope_.construct<VVectorArray_For_GVVectorArray<T>>(vector_array); } const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "") { diff --git a/source/blender/functions/FN_multi_function_procedure.hh b/source/blender/functions/FN_multi_function_procedure.hh new file mode 100644 index 00000000000..4c06ce98ee3 --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure.hh @@ -0,0 +1,539 @@ +/* + * 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 fn + */ + +#include "FN_multi_function.hh" + +namespace blender::fn { + +class MFVariable; +class MFInstruction; +class MFCallInstruction; +class MFBranchInstruction; +class MFDestructInstruction; +class MFDummyInstruction; +class MFReturnInstruction; +class MFProcedure; + +/** Every instruction has exactly one of these types. */ +enum class MFInstructionType { + Call, + Branch, + Destruct, + Dummy, + Return, +}; + +/** + * An #MFInstructionCursor points to a position in a multi-function procedure, where an instruction + * can be inserted. + */ +class MFInstructionCursor { + public: + enum Type { + None, + Entry, + Call, + Destruct, + Branch, + Dummy, + }; + + private: + Type type_ = None; + MFInstruction *instruction_ = nullptr; + /* Only used when it is a branch instruction. */ + bool branch_output_ = false; + + public: + MFInstructionCursor() = default; + MFInstructionCursor(MFCallInstruction &instruction); + MFInstructionCursor(MFDestructInstruction &instruction); + MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output); + MFInstructionCursor(MFDummyInstruction &instruction); + + static MFInstructionCursor ForEntry(); + + MFInstruction *next(MFProcedure &procedure) const; + void set_next(MFProcedure &procedure, MFInstruction *new_instruction) const; + + MFInstruction *instruction() const; + + Type type() const; + + friend bool operator==(const MFInstructionCursor &a, const MFInstructionCursor &b) + { + return a.type_ == b.type_ && a.instruction_ == b.instruction_ && + a.branch_output_ == b.branch_output_; + } + + friend bool operator!=(const MFInstructionCursor &a, const MFInstructionCursor &b) + { + return !(a == b); + } +}; + +/** + * A variable is similar to a virtual register in other libraries. During evaluation, every is + * either uninitialized or contains a value for every index (remember, a multi-function procedure + * is always evaluated for many indices at the same time). + */ +class MFVariable : NonCopyable, NonMovable { + private: + MFDataType data_type_; + Vector<MFInstruction *> users_; + std::string name_; + int id_; + + friend MFProcedure; + friend MFCallInstruction; + friend MFBranchInstruction; + friend MFDestructInstruction; + + public: + MFDataType data_type() const; + Span<MFInstruction *> users(); + + StringRefNull name() const; + void set_name(std::string name); + + int id() const; +}; + +/** Base class for all instruction types. */ +class MFInstruction : NonCopyable, NonMovable { + protected: + MFInstructionType type_; + Vector<MFInstructionCursor> prev_; + + friend MFProcedure; + friend MFCallInstruction; + friend MFBranchInstruction; + friend MFDestructInstruction; + friend MFDummyInstruction; + friend MFReturnInstruction; + + public: + MFInstructionType type() const; + + /** + * Other instructions that come before this instruction. There can be multiple previous + * instructions when branching is used in the procedure. + */ + Span<MFInstructionCursor> prev() const; +}; + +/** + * References a multi-function that is evaluated when the instruction is executed. It also + * references the variables whose data will be passed into the multi-function. + */ +class MFCallInstruction : public MFInstruction { + private: + const MultiFunction *fn_ = nullptr; + MFInstruction *next_ = nullptr; + MutableSpan<MFVariable *> params_; + + friend MFProcedure; + + public: + const MultiFunction &fn() const; + + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); + + void set_param_variable(int param_index, MFVariable *variable); + void set_params(Span<MFVariable *> variables); + + Span<MFVariable *> params(); + Span<const MFVariable *> params() const; +}; + +/** + * What makes a branch instruction special is that it has two successor instructions. One that will + * be used when a condition variable was true, and one otherwise. + */ +class MFBranchInstruction : public MFInstruction { + private: + MFVariable *condition_ = nullptr; + MFInstruction *branch_true_ = nullptr; + MFInstruction *branch_false_ = nullptr; + + friend MFProcedure; + + public: + MFVariable *condition(); + const MFVariable *condition() const; + void set_condition(MFVariable *variable); + + MFInstruction *branch_true(); + const MFInstruction *branch_true() const; + void set_branch_true(MFInstruction *instruction); + + MFInstruction *branch_false(); + const MFInstruction *branch_false() const; + void set_branch_false(MFInstruction *instruction); +}; + +/** + * A destruct instruction destructs a single variable. So the variable value will be uninitialized + * after this instruction. All variables that are not output variables of the procedure, have to be + * destructed before the procedure ends. Destructing early is generally a good thing, because it + * might help with memory buffer reuse, which decreases memory-usage and increases performance. + */ +class MFDestructInstruction : public MFInstruction { + private: + MFVariable *variable_ = nullptr; + MFInstruction *next_ = nullptr; + + friend MFProcedure; + + public: + MFVariable *variable(); + const MFVariable *variable() const; + void set_variable(MFVariable *variable); + + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); +}; + +/** + * This instruction does nothing, it just exists to building a procedure simpler in some cases. + */ +class MFDummyInstruction : public MFInstruction { + private: + MFInstruction *next_ = nullptr; + + friend MFProcedure; + + public: + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); +}; + +/** + * This instruction ends the procedure. + */ +class MFReturnInstruction : public MFInstruction { +}; + +/** + * Inputs and outputs of the entire procedure network. + */ +struct MFParameter { + MFParamType::InterfaceType type; + MFVariable *variable; +}; + +struct ConstMFParameter { + MFParamType::InterfaceType type; + const MFVariable *variable; +}; + +/** + * A multi-function procedure allows composing multi-functions in arbitrary ways. It consists of + * variables and instructions that operate on those variables. Branching and looping within the + * procedure is supported as well. + * + * Typically, a #MFProcedure should be constructed using a #MFProcedureBuilder, which has many more + * utility methods for common use cases. + */ +class MFProcedure : NonCopyable, NonMovable { + private: + LinearAllocator<> allocator_; + Vector<MFCallInstruction *> call_instructions_; + Vector<MFBranchInstruction *> branch_instructions_; + Vector<MFDestructInstruction *> destruct_instructions_; + Vector<MFDummyInstruction *> dummy_instructions_; + Vector<MFReturnInstruction *> return_instructions_; + Vector<MFVariable *> variables_; + Vector<MFParameter> params_; + MFInstruction *entry_ = nullptr; + + friend class MFProcedureDotExport; + + public: + MFProcedure() = default; + ~MFProcedure(); + + MFVariable &new_variable(MFDataType data_type, std::string name = ""); + MFCallInstruction &new_call_instruction(const MultiFunction &fn); + MFBranchInstruction &new_branch_instruction(); + MFDestructInstruction &new_destruct_instruction(); + MFDummyInstruction &new_dummy_instruction(); + MFReturnInstruction &new_return_instruction(); + + void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable); + + Span<ConstMFParameter> params() const; + + MFInstruction *entry(); + const MFInstruction *entry() const; + void set_entry(MFInstruction &entry); + + Span<MFVariable *> variables(); + Span<const MFVariable *> variables() const; + + std::string to_dot() const; + + bool validate() const; + + private: + bool validate_all_instruction_pointers_set() const; + bool validate_all_params_provided() const; + bool validate_same_variables_in_one_call() const; + bool validate_parameters() const; + bool validate_initialization() const; + + struct InitState { + bool can_be_initialized = false; + bool can_be_uninitialized = false; + }; + + InitState find_initialization_state_before_instruction(const MFInstruction &target_instruction, + const MFVariable &variable) const; +}; + +namespace multi_function_procedure_types { +using MFVariable = fn::MFVariable; +using MFInstruction = fn::MFInstruction; +using MFCallInstruction = fn::MFCallInstruction; +using MFBranchInstruction = fn::MFBranchInstruction; +using MFDestructInstruction = fn::MFDestructInstruction; +using MFProcedure = fn::MFProcedure; +} // namespace multi_function_procedure_types + +/* -------------------------------------------------------------------- + * MFInstructionCursor inline methods. + */ + +inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction) + : type_(Call), instruction_(&instruction) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction) + : type_(Destruct), instruction_(&instruction) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction, + bool branch_output) + : type_(Branch), instruction_(&instruction), branch_output_(branch_output) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction) + : type_(Dummy), instruction_(&instruction) +{ +} + +inline MFInstructionCursor MFInstructionCursor::ForEntry() +{ + MFInstructionCursor cursor; + cursor.type_ = Type::Entry; + return cursor; +} + +inline MFInstruction *MFInstructionCursor::instruction() const +{ + /* This isn't really const correct unfortunately, because to make it correct we'll need a const + * version of #MFInstructionCursor. */ + return instruction_; +} + +inline MFInstructionCursor::Type MFInstructionCursor::type() const +{ + return type_; +} + +/* -------------------------------------------------------------------- + * MFVariable inline methods. + */ + +inline MFDataType MFVariable::data_type() const +{ + return data_type_; +} + +inline Span<MFInstruction *> MFVariable::users() +{ + return users_; +} + +inline StringRefNull MFVariable::name() const +{ + return name_; +} + +inline int MFVariable::id() const +{ + return id_; +} + +/* -------------------------------------------------------------------- + * MFInstruction inline methods. + */ + +inline MFInstructionType MFInstruction::type() const +{ + return type_; +} + +inline Span<MFInstructionCursor> MFInstruction::prev() const +{ + return prev_; +} + +/* -------------------------------------------------------------------- + * MFCallInstruction inline methods. + */ + +inline const MultiFunction &MFCallInstruction::fn() const +{ + return *fn_; +} + +inline MFInstruction *MFCallInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFCallInstruction::next() const +{ + return next_; +} + +inline Span<MFVariable *> MFCallInstruction::params() +{ + return params_; +} + +inline Span<const MFVariable *> MFCallInstruction::params() const +{ + return params_; +} + +/* -------------------------------------------------------------------- + * MFBranchInstruction inline methods. + */ + +inline MFVariable *MFBranchInstruction::condition() +{ + return condition_; +} + +inline const MFVariable *MFBranchInstruction::condition() const +{ + return condition_; +} + +inline MFInstruction *MFBranchInstruction::branch_true() +{ + return branch_true_; +} + +inline const MFInstruction *MFBranchInstruction::branch_true() const +{ + return branch_true_; +} + +inline MFInstruction *MFBranchInstruction::branch_false() +{ + return branch_false_; +} + +inline const MFInstruction *MFBranchInstruction::branch_false() const +{ + return branch_false_; +} + +/* -------------------------------------------------------------------- + * MFDestructInstruction inline methods. + */ + +inline MFVariable *MFDestructInstruction::variable() +{ + return variable_; +} + +inline const MFVariable *MFDestructInstruction::variable() const +{ + return variable_; +} + +inline MFInstruction *MFDestructInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFDestructInstruction::next() const +{ + return next_; +} + +/* -------------------------------------------------------------------- + * MFDummyInstruction inline methods. + */ + +inline MFInstruction *MFDummyInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFDummyInstruction::next() const +{ + return next_; +} + +/* -------------------------------------------------------------------- + * MFProcedure inline methods. + */ + +inline Span<ConstMFParameter> MFProcedure::params() const +{ + static_assert(sizeof(MFParameter) == sizeof(ConstMFParameter)); + return params_.as_span().cast<ConstMFParameter>(); +} + +inline MFInstruction *MFProcedure::entry() +{ + return entry_; +} + +inline const MFInstruction *MFProcedure::entry() const +{ + return entry_; +} + +inline Span<MFVariable *> MFProcedure::variables() +{ + return variables_; +} + +inline Span<const MFVariable *> MFProcedure::variables() const +{ + return variables_; +} + +} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_procedure_builder.hh b/source/blender/functions/FN_multi_function_procedure_builder.hh new file mode 100644 index 00000000000..e416f7e500d --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure_builder.hh @@ -0,0 +1,203 @@ +/* + * 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 fn + */ + +#include "FN_multi_function_procedure.hh" + +namespace blender::fn { + +/** + * Utility class to build a #MFProcedure. + */ +class MFProcedureBuilder { + private: + /** Procedure that is being build. */ + MFProcedure *procedure_ = nullptr; + /** Cursors where the next instruction should be inserted. */ + Vector<MFInstructionCursor> cursors_; + + public: + struct Branch; + struct Loop; + + MFProcedureBuilder(MFProcedure &procedure, + MFInstructionCursor initial_cursor = MFInstructionCursor::ForEntry()); + + MFProcedureBuilder(Span<MFProcedureBuilder *> builders); + + MFProcedureBuilder(Branch &branch); + + void set_cursor(const MFInstructionCursor &cursor); + void set_cursor(Span<MFInstructionCursor> cursors); + void set_cursor(Span<MFProcedureBuilder *> builders); + void set_cursor_after_branch(Branch &branch); + void set_cursor_after_loop(Loop &loop); + + void add_destruct(MFVariable &variable); + void add_destruct(Span<MFVariable *> variables); + + MFReturnInstruction &add_return(); + + Branch add_branch(MFVariable &condition); + + Loop add_loop(); + void add_loop_continue(Loop &loop); + void add_loop_break(Loop &loop); + + MFCallInstruction &add_call_with_no_variables(const MultiFunction &fn); + MFCallInstruction &add_call_with_all_variables(const MultiFunction &fn, + Span<MFVariable *> param_variables); + + Vector<MFVariable *> add_call(const MultiFunction &fn, + Span<MFVariable *> input_and_mutable_variables = {}); + + template<int OutputN> + std::array<MFVariable *, OutputN> add_call(const MultiFunction &fn, + Span<MFVariable *> input_and_mutable_variables = {}); + + void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable); + MFVariable &add_parameter(MFParamType param_type, std::string name = ""); + + MFVariable &add_input_parameter(MFDataType data_type, std::string name = ""); + template<typename T> MFVariable &add_single_input_parameter(std::string name = ""); + template<typename T> MFVariable &add_single_mutable_parameter(std::string name = ""); + + void add_output_parameter(MFVariable &variable); + + private: + void link_to_cursors(MFInstruction *instruction); +}; + +struct MFProcedureBuilder::Branch { + MFProcedureBuilder branch_true; + MFProcedureBuilder branch_false; +}; + +struct MFProcedureBuilder::Loop { + MFInstruction *begin = nullptr; + MFDummyInstruction *end = nullptr; +}; + +/* -------------------------------------------------------------------- + * MFProcedureBuilder inline methods. + */ + +inline MFProcedureBuilder::MFProcedureBuilder(Branch &branch) + : MFProcedureBuilder(*branch.branch_true.procedure_) +{ + this->set_cursor_after_branch(branch); +} + +inline MFProcedureBuilder::MFProcedureBuilder(MFProcedure &procedure, + MFInstructionCursor initial_cursor) + : procedure_(&procedure), cursors_({initial_cursor}) +{ +} + +inline MFProcedureBuilder::MFProcedureBuilder(Span<MFProcedureBuilder *> builders) + : MFProcedureBuilder(*builders[0]->procedure_) +{ + this->set_cursor(builders); +} + +inline void MFProcedureBuilder::set_cursor(const MFInstructionCursor &cursor) +{ + cursors_ = {cursor}; +} + +inline void MFProcedureBuilder::set_cursor(Span<MFInstructionCursor> cursors) +{ + cursors_ = cursors; +} + +inline void MFProcedureBuilder::set_cursor_after_branch(Branch &branch) +{ + this->set_cursor({&branch.branch_false, &branch.branch_true}); +} + +inline void MFProcedureBuilder::set_cursor_after_loop(Loop &loop) +{ + this->set_cursor(MFInstructionCursor{*loop.end}); +} + +inline void MFProcedureBuilder::set_cursor(Span<MFProcedureBuilder *> builders) +{ + cursors_.clear(); + for (MFProcedureBuilder *builder : builders) { + cursors_.extend(builder->cursors_); + } +} + +template<int OutputN> +inline std::array<MFVariable *, OutputN> MFProcedureBuilder::add_call( + const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables) +{ + Vector<MFVariable *> output_variables = this->add_call(fn, input_and_mutable_variables); + BLI_assert(output_variables.size() == OutputN); + + std::array<MFVariable *, OutputN> output_array; + initialized_copy_n(output_variables.data(), OutputN, output_array.data()); + return output_array; +} + +inline void MFProcedureBuilder::add_parameter(MFParamType::InterfaceType interface_type, + MFVariable &variable) +{ + procedure_->add_parameter(interface_type, variable); +} + +inline MFVariable &MFProcedureBuilder::add_parameter(MFParamType param_type, std::string name) +{ + MFVariable &variable = procedure_->new_variable(param_type.data_type(), std::move(name)); + this->add_parameter(param_type.interface_type(), variable); + return variable; +} + +inline MFVariable &MFProcedureBuilder::add_input_parameter(MFDataType data_type, std::string name) +{ + return this->add_parameter(MFParamType(MFParamType::Input, data_type), std::move(name)); +} + +template<typename T> +inline MFVariable &MFProcedureBuilder::add_single_input_parameter(std::string name) +{ + return this->add_parameter(MFParamType::ForSingleInput(CPPType::get<T>()), std::move(name)); +} + +template<typename T> +inline MFVariable &MFProcedureBuilder::add_single_mutable_parameter(std::string name) +{ + return this->add_parameter(MFParamType::ForMutableSingle(CPPType::get<T>()), std::move(name)); +} + +inline void MFProcedureBuilder::add_output_parameter(MFVariable &variable) +{ + this->add_parameter(MFParamType::Output, variable); +} + +inline void MFProcedureBuilder::link_to_cursors(MFInstruction *instruction) +{ + for (MFInstructionCursor &cursor : cursors_) { + cursor.set_next(*procedure_, instruction); + } +} + +} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_procedure_executor.hh b/source/blender/functions/FN_multi_function_procedure_executor.hh new file mode 100644 index 00000000000..9c8b59739b8 --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure_executor.hh @@ -0,0 +1,39 @@ +/* + * 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 fn + */ + +#include "FN_multi_function_procedure.hh" + +namespace blender::fn { + +/** A multi-function that executes a procedure internally. */ +class MFProcedureExecutor : public MultiFunction { + private: + MFSignature signature_; + const MFProcedure &procedure_; + + public: + MFProcedureExecutor(std::string name, const MFProcedure &procedure); + + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; + +} // namespace blender::fn diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 7be34d2a1bf..058fb76af2b 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -15,6 +15,7 @@ */ #include "FN_cpp_type_make.hh" +#include "FN_field_cpp_type.hh" #include "BLI_color.hh" #include "BLI_float2.hh" @@ -39,4 +40,12 @@ MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b, CPPTypeFlags::BasicType MAKE_CPP_TYPE(string, std::string, CPPTypeFlags::BasicType) +MAKE_FIELD_CPP_TYPE(FloatField, float); +MAKE_FIELD_CPP_TYPE(Float2Field, float2); +MAKE_FIELD_CPP_TYPE(Float3Field, float3); +MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f); +MAKE_FIELD_CPP_TYPE(BoolField, bool); +MAKE_FIELD_CPP_TYPE(Int32Field, int32_t); +MAKE_FIELD_CPP_TYPE(StringField, std::string); + } // namespace blender::fn diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc new file mode 100644 index 00000000000..6a4518ad4a6 --- /dev/null +++ b/source/blender/functions/intern/field.cc @@ -0,0 +1,672 @@ +/* + * 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_map.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" +#include "BLI_vector_set.hh" + +#include "FN_field.hh" +#include "FN_multi_function_parallel.hh" + +namespace blender::fn { + +/* -------------------------------------------------------------------- + * Field Evaluation. + */ + +struct FieldTreeInfo { + /** + * When fields are built, they only have references to the fields that they depend on. This map + * allows traversal of fields in the opposite direction. So for every field it stores the other + * fields that depend on it directly. + */ + MultiValueMap<GFieldRef, GFieldRef> field_users; + /** + * The same field input may exist in the field tree as as separate nodes due to the way + * the tree is constructed. This set contains every different input only once. + */ + VectorSet<std::reference_wrapper<const FieldInput>> deduplicated_field_inputs; +}; + +/** + * Collects some information from the field tree that is required by later steps. + */ +static FieldTreeInfo preprocess_field_tree(Span<GFieldRef> entry_fields) +{ + FieldTreeInfo field_tree_info; + + Stack<GFieldRef> fields_to_check; + Set<GFieldRef> handled_fields; + + for (GFieldRef field : entry_fields) { + if (handled_fields.add(field)) { + fields_to_check.push(field); + } + } + + while (!fields_to_check.is_empty()) { + GFieldRef field = fields_to_check.pop(); + if (field.node().is_input()) { + const FieldInput &field_input = static_cast<const FieldInput &>(field.node()); + field_tree_info.deduplicated_field_inputs.add(field_input); + continue; + } + BLI_assert(field.node().is_operation()); + const FieldOperation &operation = static_cast<const FieldOperation &>(field.node()); + for (const GFieldRef operation_input : operation.inputs()) { + field_tree_info.field_users.add(operation_input, field); + if (handled_fields.add(operation_input)) { + fields_to_check.push(operation_input); + } + } + } + return field_tree_info; +} + +/** + * Retrieves the data from the context that is passed as input into the field. + */ +static Vector<const GVArray *> get_field_context_inputs( + ResourceScope &scope, + const IndexMask mask, + const FieldContext &context, + const Span<std::reference_wrapper<const FieldInput>> field_inputs) +{ + Vector<const GVArray *> field_context_inputs; + for (const FieldInput &field_input : field_inputs) { + const GVArray *varray = context.get_varray_for_input(field_input, mask, scope); + if (varray == nullptr) { + const CPPType &type = field_input.cpp_type(); + varray = &scope.construct<GVArray_For_SingleValueRef>( + type, mask.min_array_size(), type.default_value()); + } + field_context_inputs.append(varray); + } + return field_context_inputs; +} + +/** + * \return A set that contains all fields from the field tree that depend on an input that varies + * for different indices. + */ +static Set<GFieldRef> find_varying_fields(const FieldTreeInfo &field_tree_info, + Span<const GVArray *> field_context_inputs) +{ + Set<GFieldRef> found_fields; + Stack<GFieldRef> fields_to_check; + + /* The varying fields are the ones that depend on inputs that are not constant. Therefore we + * start the tree search at the non-constant input fields and traverse through all fields that + * depend on them. */ + for (const int i : field_context_inputs.index_range()) { + const GVArray *varray = field_context_inputs[i]; + if (varray->is_single()) { + continue; + } + const FieldInput &field_input = field_tree_info.deduplicated_field_inputs[i]; + const GFieldRef field_input_field{field_input, 0}; + const Span<GFieldRef> users = field_tree_info.field_users.lookup(field_input_field); + for (const GFieldRef &field : users) { + if (found_fields.add(field)) { + fields_to_check.push(field); + } + } + } + while (!fields_to_check.is_empty()) { + GFieldRef field = fields_to_check.pop(); + const Span<GFieldRef> users = field_tree_info.field_users.lookup(field); + for (GFieldRef field : users) { + if (found_fields.add(field)) { + fields_to_check.push(field); + } + } + } + return found_fields; +} + +/** + * Builds the #procedure so that it computes the the fields. + */ +static void build_multi_function_procedure_for_fields(MFProcedure &procedure, + ResourceScope &scope, + const FieldTreeInfo &field_tree_info, + Span<GFieldRef> output_fields) +{ + MFProcedureBuilder builder{procedure}; + /* Every input, intermediate and output field corresponds to a variable in the procedure. */ + Map<GFieldRef, MFVariable *> variable_by_field; + + /* Start by adding the field inputs as parameters to the procedure. */ + for (const FieldInput &field_input : field_tree_info.deduplicated_field_inputs) { + MFVariable &variable = builder.add_input_parameter( + MFDataType::ForSingle(field_input.cpp_type()), field_input.debug_name()); + variable_by_field.add_new({field_input, 0}, &variable); + } + + /* Utility struct that is used to do proper depth first search traversal of the tree below. */ + struct FieldWithIndex { + GFieldRef field; + int current_input_index = 0; + }; + + for (GFieldRef field : output_fields) { + /* We start a new stack for each output field to make sure that a field pushed later to the + * stack does never depend on a field that was pushed before. */ + Stack<FieldWithIndex> fields_to_check; + fields_to_check.push({field, 0}); + while (!fields_to_check.is_empty()) { + FieldWithIndex &field_with_index = fields_to_check.peek(); + const GFieldRef &field = field_with_index.field; + if (variable_by_field.contains(field)) { + /* The field has been handled already. */ + fields_to_check.pop(); + continue; + } + /* Field inputs should already be handled above. */ + BLI_assert(field.node().is_operation()); + + const FieldOperation &operation = static_cast<const FieldOperation &>(field.node()); + const Span<GField> operation_inputs = operation.inputs(); + + if (field_with_index.current_input_index < operation_inputs.size()) { + /* Not all inputs are handled yet. Push the next input field to the stack and increment the + * input index. */ + fields_to_check.push({operation_inputs[field_with_index.current_input_index]}); + field_with_index.current_input_index++; + } + else { + /* All inputs variables are ready, now gather all variables that are used by the function + * and call it. */ + const MultiFunction &multi_function = operation.multi_function(); + Vector<MFVariable *> variables(multi_function.param_amount()); + + int param_input_index = 0; + int param_output_index = 0; + for (const int param_index : multi_function.param_indices()) { + const MFParamType param_type = multi_function.param_type(param_index); + const MFParamType::InterfaceType interface_type = param_type.interface_type(); + if (interface_type == MFParamType::Input) { + const GField &input_field = operation_inputs[param_input_index]; + variables[param_index] = variable_by_field.lookup(input_field); + param_input_index++; + } + else if (interface_type == MFParamType::Output) { + const GFieldRef output_field{operation, param_output_index}; + const bool output_is_ignored = + field_tree_info.field_users.lookup(output_field).is_empty() && + !output_fields.contains(output_field); + if (output_is_ignored) { + /* Ignored outputs don't need a variable. */ + variables[param_index] = nullptr; + } + else { + /* Create a new variable for used outputs. */ + MFVariable &new_variable = procedure.new_variable(param_type.data_type()); + variables[param_index] = &new_variable; + variable_by_field.add_new(output_field, &new_variable); + } + param_output_index++; + } + else { + BLI_assert_unreachable(); + } + } + builder.add_call_with_all_variables(multi_function, variables); + } + } + } + + /* Add output parameters to the procedure. */ + Set<MFVariable *> already_output_variables; + for (const GFieldRef &field : output_fields) { + MFVariable *variable = variable_by_field.lookup(field); + if (!already_output_variables.add(variable)) { + /* One variable can be output at most once. To output the same value twice, we have to make + * a copy first. */ + const MultiFunction ©_fn = scope.construct<CustomMF_GenericCopy>("copy", + variable->data_type()); + variable = builder.add_call<1>(copy_fn, {variable})[0]; + } + builder.add_output_parameter(*variable); + } + + /* Remove the variables that should not be destructed from the map. */ + for (const GFieldRef &field : output_fields) { + variable_by_field.remove(field); + } + /* Add destructor calls for the remaining variables. */ + for (MFVariable *variable : variable_by_field.values()) { + builder.add_destruct(*variable); + } + + builder.add_return(); + + // std::cout << procedure.to_dot() << "\n"; + BLI_assert(procedure.validate()); +} + +/** + * Utility class that destructs elements from a partially initialized array. + */ +struct PartiallyInitializedArray : NonCopyable, NonMovable { + void *buffer; + IndexMask mask; + const CPPType *type; + + ~PartiallyInitializedArray() + { + this->type->destruct_indices(this->buffer, this->mask); + } +}; + +/** + * Evaluate fields in the given context. If possible, multiple fields should be evaluated together, + * because that can be more efficient when they share common sub-fields. + * + * \param scope: The resource scope that owns data that makes up the output virtual arrays. Make + * sure the scope is not destructed when the output virtual arrays are still used. + * \param fields_to_evaluate: The fields that should be evaluated together. + * \param mask: Determines which indices are computed. The mask may be referenced by the returned + * virtual arrays. So the underlying indices (if applicable) should live longer then #scope. + * \param context: The context that the field is evaluated in. Used to retrieve data from each + * #FieldInput in the field network. + * \param dst_varrays: If provided, the computed data will be written into those virtual arrays + * instead of into newly created ones. That allows making the computed data live longer than + * #scope and is more efficient when the data will be written into those virtual arrays + * later anyway. + * \return The computed virtual arrays for each provided field. If #dst_varrays is passed, the + * provided virtual arrays are returned. + */ +Vector<const GVArray *> evaluate_fields(ResourceScope &scope, + Span<GFieldRef> fields_to_evaluate, + IndexMask mask, + const FieldContext &context, + Span<GVMutableArray *> dst_varrays) +{ + Vector<const GVArray *> r_varrays(fields_to_evaluate.size(), nullptr); + const int array_size = mask.min_array_size(); + + /* Destination arrays are optional. Create a small utility method to access them. */ + auto get_dst_varray_if_available = [&](int index) -> GVMutableArray * { + if (dst_varrays.is_empty()) { + return nullptr; + } + BLI_assert(dst_varrays[index] == nullptr || dst_varrays[index]->size() >= array_size); + return dst_varrays[index]; + }; + + /* Traverse the field tree and prepare some data that is used in later steps. */ + FieldTreeInfo field_tree_info = preprocess_field_tree(fields_to_evaluate); + + /* Get inputs that will be passed into the field when evaluated. */ + Vector<const GVArray *> field_context_inputs = get_field_context_inputs( + scope, mask, context, field_tree_info.deduplicated_field_inputs); + + /* Finish fields that output an input varray directly. For those we don't have to do any further + * processing. */ + for (const int out_index : fields_to_evaluate.index_range()) { + const GFieldRef &field = fields_to_evaluate[out_index]; + if (!field.node().is_input()) { + continue; + } + const FieldInput &field_input = static_cast<const FieldInput &>(field.node()); + const int field_input_index = field_tree_info.deduplicated_field_inputs.index_of(field_input); + const GVArray *varray = field_context_inputs[field_input_index]; + r_varrays[out_index] = varray; + } + + Set<GFieldRef> varying_fields = find_varying_fields(field_tree_info, field_context_inputs); + + /* Separate fields into two categories. Those that are constant and need to be evaluated only + * once, and those that need to be evaluated for every index. */ + Vector<GFieldRef> varying_fields_to_evaluate; + Vector<int> varying_field_indices; + Vector<GFieldRef> constant_fields_to_evaluate; + Vector<int> constant_field_indices; + for (const int i : fields_to_evaluate.index_range()) { + if (r_varrays[i] != nullptr) { + /* Already done. */ + continue; + } + GFieldRef field = fields_to_evaluate[i]; + if (varying_fields.contains(field)) { + varying_fields_to_evaluate.append(field); + varying_field_indices.append(i); + } + else { + constant_fields_to_evaluate.append(field); + constant_field_indices.append(i); + } + } + + /* Evaluate varying fields if necessary. */ + if (!varying_fields_to_evaluate.is_empty()) { + /* Build the procedure for those fields. */ + MFProcedure procedure; + build_multi_function_procedure_for_fields( + procedure, scope, field_tree_info, varying_fields_to_evaluate); + MFProcedureExecutor procedure_executor{"Procedure", procedure}; + /* Add multi threading capabilities to the field evaluation. */ + const int grain_size = 10000; + fn::ParallelMultiFunction parallel_procedure_executor{procedure_executor, grain_size}; + /* Utility variable to make easy to switch the executor. */ + const MultiFunction &executor_fn = parallel_procedure_executor; + + MFParamsBuilder mf_params{executor_fn, &mask}; + MFContextBuilder mf_context; + + /* Provide inputs to the procedure executor. */ + for (const GVArray *varray : field_context_inputs) { + mf_params.add_readonly_single_input(*varray); + } + + for (const int i : varying_fields_to_evaluate.index_range()) { + const GFieldRef &field = varying_fields_to_evaluate[i]; + const CPPType &type = field.cpp_type(); + const int out_index = varying_field_indices[i]; + + /* Try to get an existing virtual array that the result should be written into. */ + GVMutableArray *output_varray = get_dst_varray_if_available(out_index); + void *buffer; + if (output_varray == nullptr || !output_varray->is_span()) { + /* Allocate a new buffer for the computed result. */ + buffer = scope.linear_allocator().allocate(type.size() * array_size, type.alignment()); + + /* Make sure that elements in the buffer will be destructed. */ + PartiallyInitializedArray &destruct_helper = scope.construct<PartiallyInitializedArray>(); + destruct_helper.buffer = buffer; + destruct_helper.mask = mask; + destruct_helper.type = &type; + + r_varrays[out_index] = &scope.construct<GVArray_For_GSpan>( + GSpan{type, buffer, array_size}); + } + else { + /* Write the result into the existing span. */ + buffer = output_varray->get_internal_span().data(); + + r_varrays[out_index] = output_varray; + } + + /* Pass output buffer to the procedure executor. */ + const GMutableSpan span{type, buffer, array_size}; + mf_params.add_uninitialized_single_output(span); + } + + executor_fn.call(mask, mf_params, mf_context); + } + + /* Evaluate constant fields if necessary. */ + if (!constant_fields_to_evaluate.is_empty()) { + /* Build the procedure for those fields. */ + MFProcedure procedure; + build_multi_function_procedure_for_fields( + procedure, scope, field_tree_info, constant_fields_to_evaluate); + MFProcedureExecutor procedure_executor{"Procedure", procedure}; + MFParamsBuilder mf_params{procedure_executor, 1}; + MFContextBuilder mf_context; + + /* Provide inputs to the procedure executor. */ + for (const GVArray *varray : field_context_inputs) { + mf_params.add_readonly_single_input(*varray); + } + + for (const int i : constant_fields_to_evaluate.index_range()) { + const GFieldRef &field = constant_fields_to_evaluate[i]; + const CPPType &type = field.cpp_type(); + /* Allocate memory where the computed value will be stored in. */ + void *buffer = scope.linear_allocator().allocate(type.size(), type.alignment()); + + /* Use this to make sure that the value is destructed in the end. */ + PartiallyInitializedArray &destruct_helper = scope.construct<PartiallyInitializedArray>(); + destruct_helper.buffer = buffer; + destruct_helper.mask = IndexRange(1); + destruct_helper.type = &type; + + /* Pass output buffer to the procedure executor. */ + mf_params.add_uninitialized_single_output({type, buffer, 1}); + + /* Create virtual array that can be used after the procedure has been executed below. */ + const int out_index = constant_field_indices[i]; + r_varrays[out_index] = &scope.construct<GVArray_For_SingleValueRef>( + type, array_size, buffer); + } + + procedure_executor.call(IndexRange(1), mf_params, mf_context); + } + + /* Copy data to supplied destination arrays if necessary. In some cases the evaluation above has + * written the computed data in the right place already. */ + if (!dst_varrays.is_empty()) { + for (const int out_index : fields_to_evaluate.index_range()) { + GVMutableArray *output_varray = get_dst_varray_if_available(out_index); + if (output_varray == nullptr) { + /* Caller did not provide a destination for this output. */ + continue; + } + const GVArray *computed_varray = r_varrays[out_index]; + BLI_assert(computed_varray->type() == output_varray->type()); + if (output_varray == computed_varray) { + /* The result has been written into the destination provided by the caller already. */ + continue; + } + /* Still have to copy over the data in the destination provided by the caller. */ + if (output_varray->is_span()) { + /* Materialize into a span. */ + computed_varray->materialize_to_uninitialized(output_varray->get_internal_span().data()); + } + else { + /* Slower materialize into a different structure. */ + const CPPType &type = computed_varray->type(); + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + for (const int i : mask) { + computed_varray->get_to_uninitialized(i, buffer); + output_varray->set_by_relocate(i, buffer); + } + } + r_varrays[out_index] = output_varray; + } + } + return r_varrays; +} + +void evaluate_constant_field(const GField &field, void *r_value) +{ + ResourceScope scope; + FieldContext context; + Vector<const GVArray *> varrays = evaluate_fields(scope, {field}, IndexRange(1), context); + varrays[0]->get_to_uninitialized(0, r_value); +} + +/** + * If the field depends on some input, the same field is returned. + * Otherwise the field is evaluated and a new field is created that just computes this constant. + * + * Making the field constant has two benefits: + * - The field-tree becomes a single node, which is more efficient when the field is evaluated many + * times. + * - Memory of the input fields may be freed. + */ +GField make_field_constant_if_possible(GField field) +{ + if (field.node().depends_on_input()) { + return field; + } + const CPPType &type = field.cpp_type(); + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + evaluate_constant_field(field, buffer); + auto constant_fn = std::make_unique<CustomMF_GenericConstant>(type, buffer, true); + type.destruct(buffer); + auto operation = std::make_shared<FieldOperation>(std::move(constant_fn)); + return GField{operation, 0}; +} + +const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input, + IndexMask mask, + ResourceScope &scope) const +{ + /* By default ask the field input to create the varray. Another field context might overwrite + * the context here. */ + return field_input.get_varray_for_context(*this, mask, scope); +} + +/* -------------------------------------------------------------------- + * FieldOperation. + */ + +FieldOperation::FieldOperation(std::unique_ptr<const MultiFunction> function, + Vector<GField> inputs) + : FieldOperation(*function, std::move(inputs)) +{ + owned_function_ = std::move(function); +} + +static bool any_field_depends_on_input(Span<GField> fields) +{ + for (const GField &field : fields) { + if (field.node().depends_on_input()) { + return true; + } + } + return false; +} + +FieldOperation::FieldOperation(const MultiFunction &function, Vector<GField> inputs) + : FieldNode(false, any_field_depends_on_input(inputs)), + function_(&function), + inputs_(std::move(inputs)) +{ +} + +void FieldOperation::foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const +{ + for (const GField &field : inputs_) { + field.node().foreach_field_input(foreach_fn); + } +} + +/* -------------------------------------------------------------------- + * FieldInput. + */ + +FieldInput::FieldInput(const CPPType &type, std::string debug_name) + : FieldNode(true, true), type_(&type), debug_name_(std::move(debug_name)) +{ +} + +void FieldInput::foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const +{ + foreach_fn(*this); +} + +/* -------------------------------------------------------------------- + * FieldEvaluator. + */ + +static Vector<int64_t> indices_from_selection(const VArray<bool> &selection) +{ + /* If the selection is just a single value, it's best to avoid calling this + * function when constructing an IndexMask and use an IndexRange instead. */ + BLI_assert(!selection.is_single()); + + Vector<int64_t> indices; + if (selection.is_span()) { + Span<bool> span = selection.get_internal_span(); + for (const int64_t i : span.index_range()) { + if (span[i]) { + indices.append(i); + } + } + } + else { + for (const int i : selection.index_range()) { + if (selection[i]) { + indices.append(i); + } + } + } + return indices; +} + +int FieldEvaluator::add_with_destination(GField field, GVMutableArray &dst) +{ + const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); + dst_varrays_.append(&dst); + output_pointer_infos_.append({}); + return field_index; +} + +int FieldEvaluator::add_with_destination(GField field, GMutableSpan dst) +{ + GVMutableArray &varray = scope_.construct<GVMutableArray_For_GMutableSpan>(dst); + return this->add_with_destination(std::move(field), varray); +} + +int FieldEvaluator::add(GField field, const GVArray **varray_ptr) +{ + const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); + dst_varrays_.append(nullptr); + output_pointer_infos_.append(OutputPointerInfo{ + varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &UNUSED(scope)) { + *(const GVArray **)dst = &varray; + }}); + return field_index; +} + +int FieldEvaluator::add(GField field) +{ + const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); + dst_varrays_.append(nullptr); + output_pointer_infos_.append({}); + return field_index; +} + +void FieldEvaluator::evaluate() +{ + BLI_assert_msg(!is_evaluated_, "Cannot evaluate fields twice."); + Array<GFieldRef> fields(fields_to_evaluate_.size()); + for (const int i : fields_to_evaluate_.index_range()) { + fields[i] = fields_to_evaluate_[i]; + } + evaluated_varrays_ = evaluate_fields(scope_, fields, mask_, context_, dst_varrays_); + BLI_assert(fields_to_evaluate_.size() == evaluated_varrays_.size()); + for (const int i : fields_to_evaluate_.index_range()) { + OutputPointerInfo &info = output_pointer_infos_[i]; + if (info.dst != nullptr) { + info.set(info.dst, *evaluated_varrays_[i], scope_); + } + } + is_evaluated_ = true; +} + +IndexMask FieldEvaluator::get_evaluated_as_mask(const int field_index) +{ + const GVArray &varray = this->get_evaluated(field_index); + GVArray_Typed<bool> typed_varray{varray}; + + if (typed_varray->is_single()) { + if (typed_varray->get_internal_single()) { + return IndexRange(typed_varray.size()); + } + return IndexRange(0); + } + + return scope_.add_value(indices_from_selection(*typed_varray)).as_span(); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc index bd033a429de..9a83d8cd497 100644 --- a/source/blender/functions/intern/generic_virtual_array.cc +++ b/source/blender/functions/intern/generic_virtual_array.cc @@ -387,4 +387,47 @@ void GVMutableArray_GSpan::disable_not_applied_warning() show_not_saved_warning_ = false; } +/* -------------------------------------------------------------------- + * GVArray_For_SlicedGVArray. + */ + +void GVArray_For_SlicedGVArray::get_impl(const int64_t index, void *r_value) const +{ + varray_.get(index + offset_, r_value); +} + +void GVArray_For_SlicedGVArray::get_to_uninitialized_impl(const int64_t index, void *r_value) const +{ + varray_.get_to_uninitialized(index + offset_, r_value); +} + +/* -------------------------------------------------------------------- + * GVArray_Slice. + */ + +GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice) +{ + if (varray.is_span()) { + /* Create a new virtual for the sliced span. */ + const GSpan span = varray.get_internal_span(); + const GSpan sliced_span = span.slice(slice.start(), slice.size()); + varray_span_.emplace(sliced_span); + varray_ = &*varray_span_; + } + else if (varray.is_single()) { + /* Can just use the existing virtual array, because it's the same value for the indices in the + * slice anyway. */ + varray_ = &varray; + } + else { + /* Generic version when none of the above method works. + * We don't necessarily want to materialize the input varray because there might be + * large distances between the required indices. Then we would materialize many elements that + * are not accessed later on. + */ + varray_any_.emplace(varray, slice); + varray_ = &*varray_any_; + } +} + } // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc index c6b3b808130..f891f162820 100644 --- a/source/blender/functions/intern/multi_function_builder.cc +++ b/source/blender/functions/intern/multi_function_builder.cc @@ -20,9 +20,18 @@ namespace blender::fn { -CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, const void *value) - : type_(type), value_(value) +CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, + const void *value, + bool make_value_copy) + : type_(type), owns_value_(make_value_copy) { + if (make_value_copy) { + void *copied_value = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + type.copy_construct(value, copied_value); + value = copied_value; + } + value_ = value; + MFSignatureBuilder signature{"Constant " + type.name()}; std::stringstream ss; type.print_or_default(value, ss, type.name()); @@ -31,6 +40,14 @@ CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, const vo this->set_signature(&signature_); } +CustomMF_GenericConstant::~CustomMF_GenericConstant() +{ + if (owns_value_) { + signature_.param_types[0].data_type().single_type().destruct((void *)value_); + MEM_freeN((void *)value_); + } +} + void CustomMF_GenericConstant::call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const @@ -123,4 +140,32 @@ void CustomMF_DefaultOutput::call(IndexMask mask, MFParams params, MFContext UNU } } +CustomMF_GenericCopy::CustomMF_GenericCopy(StringRef name, MFDataType data_type) +{ + MFSignatureBuilder signature{name}; + signature.input("Input", data_type); + signature.output("Output", data_type); + signature_ = signature.build(); + this->set_signature(&signature_); +} + +void CustomMF_GenericCopy::call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const +{ + const MFDataType data_type = this->param_type(0).data_type(); + switch (data_type.category()) { + case MFDataType::Single: { + const GVArray &inputs = params.readonly_single_input(0, "Input"); + GMutableSpan outputs = params.uninitialized_single_output(1, "Output"); + inputs.materialize_to_uninitialized(mask, outputs.data()); + break; + } + case MFDataType::Vector: { + const GVVectorArray &inputs = params.readonly_vector_input(0, "Input"); + GVectorArray &outputs = params.vector_output(1, "Output"); + outputs.extend(mask, inputs); + break; + } + } +} + } // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_parallel.cc b/source/blender/functions/intern/multi_function_parallel.cc new file mode 100644 index 00000000000..5a8c621f0b3 --- /dev/null +++ b/source/blender/functions/intern/multi_function_parallel.cc @@ -0,0 +1,95 @@ +/* + * 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 "FN_multi_function_parallel.hh" + +#include "BLI_task.hh" + +namespace blender::fn { + +ParallelMultiFunction::ParallelMultiFunction(const MultiFunction &fn, const int64_t grain_size) + : fn_(fn), grain_size_(grain_size) +{ + this->set_signature(&fn.signature()); + + threading_supported_ = true; + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + if (param_type.data_type().category() == MFDataType::Vector) { + /* Vector parameters do not support threading yet. */ + threading_supported_ = false; + break; + } + } +} + +void ParallelMultiFunction::call(IndexMask full_mask, MFParams params, MFContext context) const +{ + if (full_mask.size() <= grain_size_ || !threading_supported_) { + fn_.call(full_mask, params, context); + return; + } + + threading::parallel_for(full_mask.index_range(), grain_size_, [&](const IndexRange mask_slice) { + Vector<int64_t> sub_mask_indices; + const IndexMask sub_mask = full_mask.slice_and_offset(mask_slice, sub_mask_indices); + if (sub_mask.is_empty()) { + return; + } + const int64_t input_slice_start = full_mask[mask_slice.first()]; + const int64_t input_slice_size = full_mask[mask_slice.last()] - input_slice_start + 1; + const IndexRange input_slice_range{input_slice_start, input_slice_size}; + + MFParamsBuilder sub_params{fn_, sub_mask.min_array_size()}; + ResourceScope &scope = sub_params.resource_scope(); + + /* All parameters are sliced so that the wrapped multi-function does not have to take care of + * the index offset. */ + for (const int param_index : fn_.param_indices()) { + const MFParamType param_type = fn_.param_type(param_index); + switch (param_type.category()) { + case MFParamType::SingleInput: { + const GVArray &varray = params.readonly_single_input(param_index); + const GVArray &sliced_varray = scope.construct<GVArray_Slice>(varray, input_slice_range); + sub_params.add_readonly_single_input(sliced_varray); + break; + } + case MFParamType::SingleMutable: { + const GMutableSpan span = params.single_mutable(param_index); + const GMutableSpan sliced_span = span.slice(input_slice_start, input_slice_size); + sub_params.add_single_mutable(sliced_span); + break; + } + case MFParamType::SingleOutput: { + const GMutableSpan span = params.uninitialized_single_output(param_index); + const GMutableSpan sliced_span = span.slice(input_slice_start, input_slice_size); + sub_params.add_uninitialized_single_output(sliced_span); + break; + } + case MFParamType::VectorInput: + case MFParamType::VectorMutable: + case MFParamType::VectorOutput: { + BLI_assert_unreachable(); + break; + } + } + } + + fn_.call(sub_mask, sub_params, context); + }); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc new file mode 100644 index 00000000000..fa95e8de71e --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -0,0 +1,874 @@ +/* + * 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 "FN_multi_function_procedure.hh" + +#include "BLI_dot_export.hh" +#include "BLI_stack.hh" + +namespace blender::fn { + +void MFInstructionCursor::set_next(MFProcedure &procedure, MFInstruction *new_instruction) const +{ + switch (type_) { + case Type::None: { + break; + } + case Type::Entry: { + procedure.set_entry(*new_instruction); + break; + } + case Type::Call: { + static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction); + break; + } + case Type::Branch: { + MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(instruction_); + if (branch_output_) { + branch_instruction.set_branch_true(new_instruction); + } + else { + branch_instruction.set_branch_false(new_instruction); + } + break; + } + case Type::Destruct: { + static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction); + break; + } + case Type::Dummy: { + static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction); + break; + } + } +} + +MFInstruction *MFInstructionCursor::next(MFProcedure &procedure) const +{ + switch (type_) { + case Type::None: + return nullptr; + case Type::Entry: + return procedure.entry(); + case Type::Call: + return static_cast<MFCallInstruction *>(instruction_)->next(); + case Type::Branch: { + MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(instruction_); + if (branch_output_) { + return branch_instruction.branch_true(); + } + return branch_instruction.branch_false(); + } + case Type::Destruct: + return static_cast<MFDestructInstruction *>(instruction_)->next(); + case Type::Dummy: + return static_cast<MFDummyInstruction *>(instruction_)->next(); + } + return nullptr; +} + +void MFVariable::set_name(std::string name) +{ + name_ = std::move(name); +} + +void MFCallInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(*this); + } + if (instruction != nullptr) { + instruction->prev_.append(*this); + } + next_ = instruction; +} + +void MFCallInstruction::set_param_variable(int param_index, MFVariable *variable) +{ + if (params_[param_index] != nullptr) { + params_[param_index]->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + BLI_assert(fn_->param_type(param_index).data_type() == variable->data_type()); + variable->users_.append(this); + } + params_[param_index] = variable; +} + +void MFCallInstruction::set_params(Span<MFVariable *> variables) +{ + BLI_assert(variables.size() == params_.size()); + for (const int i : variables.index_range()) { + this->set_param_variable(i, variables[i]); + } +} + +void MFBranchInstruction::set_condition(MFVariable *variable) +{ + if (condition_ != nullptr) { + condition_->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + variable->users_.append(this); + } + condition_ = variable; +} + +void MFBranchInstruction::set_branch_true(MFInstruction *instruction) +{ + if (branch_true_ != nullptr) { + branch_true_->prev_.remove_first_occurrence_and_reorder({*this, true}); + } + if (instruction != nullptr) { + instruction->prev_.append({*this, true}); + } + branch_true_ = instruction; +} + +void MFBranchInstruction::set_branch_false(MFInstruction *instruction) +{ + if (branch_false_ != nullptr) { + branch_false_->prev_.remove_first_occurrence_and_reorder({*this, false}); + } + if (instruction != nullptr) { + instruction->prev_.append({*this, false}); + } + branch_false_ = instruction; +} + +void MFDestructInstruction::set_variable(MFVariable *variable) +{ + if (variable_ != nullptr) { + variable_->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + variable->users_.append(this); + } + variable_ = variable; +} + +void MFDestructInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(*this); + } + if (instruction != nullptr) { + instruction->prev_.append(*this); + } + next_ = instruction; +} + +void MFDummyInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(*this); + } + if (instruction != nullptr) { + instruction->prev_.append(*this); + } + next_ = instruction; +} + +MFVariable &MFProcedure::new_variable(MFDataType data_type, std::string name) +{ + MFVariable &variable = *allocator_.construct<MFVariable>().release(); + variable.name_ = std::move(name); + variable.data_type_ = data_type; + variable.id_ = variables_.size(); + variables_.append(&variable); + return variable; +} + +MFCallInstruction &MFProcedure::new_call_instruction(const MultiFunction &fn) +{ + MFCallInstruction &instruction = *allocator_.construct<MFCallInstruction>().release(); + instruction.type_ = MFInstructionType::Call; + instruction.fn_ = &fn; + instruction.params_ = allocator_.allocate_array<MFVariable *>(fn.param_amount()); + instruction.params_.fill(nullptr); + call_instructions_.append(&instruction); + return instruction; +} + +MFBranchInstruction &MFProcedure::new_branch_instruction() +{ + MFBranchInstruction &instruction = *allocator_.construct<MFBranchInstruction>().release(); + instruction.type_ = MFInstructionType::Branch; + branch_instructions_.append(&instruction); + return instruction; +} + +MFDestructInstruction &MFProcedure::new_destruct_instruction() +{ + MFDestructInstruction &instruction = *allocator_.construct<MFDestructInstruction>().release(); + instruction.type_ = MFInstructionType::Destruct; + destruct_instructions_.append(&instruction); + return instruction; +} + +MFDummyInstruction &MFProcedure::new_dummy_instruction() +{ + MFDummyInstruction &instruction = *allocator_.construct<MFDummyInstruction>().release(); + instruction.type_ = MFInstructionType::Dummy; + dummy_instructions_.append(&instruction); + return instruction; +} + +MFReturnInstruction &MFProcedure::new_return_instruction() +{ + MFReturnInstruction &instruction = *allocator_.construct<MFReturnInstruction>().release(); + instruction.type_ = MFInstructionType::Return; + return_instructions_.append(&instruction); + return instruction; +} + +void MFProcedure::add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable) +{ + params_.append({interface_type, &variable}); +} + +void MFProcedure::set_entry(MFInstruction &entry) +{ + if (entry_ != nullptr) { + entry_->prev_.remove_first_occurrence_and_reorder(MFInstructionCursor::ForEntry()); + } + entry_ = &entry; + entry_->prev_.append(MFInstructionCursor::ForEntry()); +} + +MFProcedure::~MFProcedure() +{ + for (MFCallInstruction *instruction : call_instructions_) { + instruction->~MFCallInstruction(); + } + for (MFBranchInstruction *instruction : branch_instructions_) { + instruction->~MFBranchInstruction(); + } + for (MFDestructInstruction *instruction : destruct_instructions_) { + instruction->~MFDestructInstruction(); + } + for (MFDummyInstruction *instruction : dummy_instructions_) { + instruction->~MFDummyInstruction(); + } + for (MFReturnInstruction *instruction : return_instructions_) { + instruction->~MFReturnInstruction(); + } + for (MFVariable *variable : variables_) { + variable->~MFVariable(); + } +} + +bool MFProcedure::validate() const +{ + if (entry_ == nullptr) { + return false; + } + if (!this->validate_all_instruction_pointers_set()) { + return false; + } + if (!this->validate_all_params_provided()) { + return false; + } + if (!this->validate_same_variables_in_one_call()) { + return false; + } + if (!this->validate_parameters()) { + return false; + } + if (!this->validate_initialization()) { + return false; + } + return true; +} + +bool MFProcedure::validate_all_instruction_pointers_set() const +{ + for (const MFCallInstruction *instruction : call_instructions_) { + if (instruction->next_ == nullptr) { + return false; + } + } + for (const MFDestructInstruction *instruction : destruct_instructions_) { + if (instruction->next_ == nullptr) { + return false; + } + } + for (const MFBranchInstruction *instruction : branch_instructions_) { + if (instruction->branch_true_ == nullptr) { + return false; + } + if (instruction->branch_false_ == nullptr) { + return false; + } + } + for (const MFDummyInstruction *instruction : dummy_instructions_) { + if (instruction->next_ == nullptr) { + return false; + } + } + return true; +} + +bool MFProcedure::validate_all_params_provided() const +{ + for (const MFCallInstruction *instruction : call_instructions_) { + const MultiFunction &fn = instruction->fn(); + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + if (param_type.category() == MFParamType::SingleOutput) { + /* Single outputs are optional. */ + continue; + } + const MFVariable *variable = instruction->params_[param_index]; + if (variable == nullptr) { + return false; + } + } + } + for (const MFBranchInstruction *instruction : branch_instructions_) { + if (instruction->condition_ == nullptr) { + return false; + } + } + for (const MFDestructInstruction *instruction : destruct_instructions_) { + if (instruction->variable_ == nullptr) { + return false; + } + } + return true; +} + +bool MFProcedure::validate_same_variables_in_one_call() const +{ + for (const MFCallInstruction *instruction : call_instructions_) { + const MultiFunction &fn = *instruction->fn_; + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + const MFVariable *variable = instruction->params_[param_index]; + if (variable == nullptr) { + continue; + } + for (const int other_param_index : fn.param_indices()) { + if (other_param_index == param_index) { + continue; + } + const MFVariable *other_variable = instruction->params_[other_param_index]; + if (other_variable != variable) { + continue; + } + if (ELEM(param_type.interface_type(), MFParamType::Mutable, MFParamType::Output)) { + /* When a variable is used as mutable or output parameter, it can only be used once. */ + return false; + } + const MFParamType other_param_type = fn.param_type(other_param_index); + /* A variable is allowed to be used as input more than once. */ + if (other_param_type.interface_type() != MFParamType::Input) { + return false; + } + } + } + } + return true; +} + +bool MFProcedure::validate_parameters() const +{ + Set<const MFVariable *> variables; + for (const MFParameter ¶m : params_) { + /* One variable cannot be used as multiple parameters. */ + if (!variables.add(param.variable)) { + return false; + } + } + return true; +} + +bool MFProcedure::validate_initialization() const +{ + /* TODO: Issue warning when it maybe wrongly initialized. */ + for (const MFDestructInstruction *instruction : destruct_instructions_) { + const MFVariable &variable = *instruction->variable_; + const InitState state = this->find_initialization_state_before_instruction(*instruction, + variable); + if (!state.can_be_initialized) { + return false; + } + } + for (const MFBranchInstruction *instruction : branch_instructions_) { + const MFVariable &variable = *instruction->condition_; + const InitState state = this->find_initialization_state_before_instruction(*instruction, + variable); + if (!state.can_be_initialized) { + return false; + } + } + for (const MFCallInstruction *instruction : call_instructions_) { + const MultiFunction &fn = *instruction->fn_; + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + const MFVariable &variable = *instruction->params_[param_index]; + const InitState state = this->find_initialization_state_before_instruction(*instruction, + variable); + switch (param_type.interface_type()) { + case MFParamType::Input: + case MFParamType::Mutable: { + if (!state.can_be_initialized) { + return false; + } + break; + } + case MFParamType::Output: { + if (!state.can_be_uninitialized) { + return false; + } + break; + } + } + } + } + Set<const MFVariable *> variables_that_should_be_initialized_on_return; + for (const MFParameter ¶m : params_) { + if (ELEM(param.type, MFParamType::Mutable, MFParamType::Output)) { + variables_that_should_be_initialized_on_return.add_new(param.variable); + } + } + for (const MFReturnInstruction *instruction : return_instructions_) { + for (const MFVariable *variable : variables_) { + const InitState init_state = this->find_initialization_state_before_instruction(*instruction, + *variable); + if (variables_that_should_be_initialized_on_return.contains(variable)) { + if (!init_state.can_be_initialized) { + return false; + } + } + else { + if (!init_state.can_be_uninitialized) { + return false; + } + } + } + } + return true; +} + +MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction( + const MFInstruction &target_instruction, const MFVariable &target_variable) const +{ + InitState state; + + auto check_entry_instruction = [&]() { + bool caller_initialized_variable = false; + for (const MFParameter ¶m : params_) { + if (param.variable == &target_variable) { + if (ELEM(param.type, MFParamType::Input, MFParamType::Mutable)) { + caller_initialized_variable = true; + break; + } + } + } + if (caller_initialized_variable) { + state.can_be_initialized = true; + } + else { + state.can_be_uninitialized = true; + } + }; + + if (&target_instruction == entry_) { + check_entry_instruction(); + } + + Set<const MFInstruction *> checked_instructions; + Stack<const MFInstruction *> instructions_to_check; + for (const MFInstructionCursor &cursor : target_instruction.prev_) { + if (cursor.instruction() != nullptr) { + instructions_to_check.push(cursor.instruction()); + } + } + + while (!instructions_to_check.is_empty()) { + const MFInstruction &instruction = *instructions_to_check.pop(); + if (!checked_instructions.add(&instruction)) { + /* Skip if the instruction has been checked already. */ + continue; + } + bool state_modified = false; + switch (instruction.type_) { + case MFInstructionType::Call: { + const MFCallInstruction &call_instruction = static_cast<const MFCallInstruction &>( + instruction); + const MultiFunction &fn = *call_instruction.fn_; + for (const int param_index : fn.param_indices()) { + if (call_instruction.params_[param_index] == &target_variable) { + const MFParamType param_type = fn.param_type(param_index); + if (param_type.interface_type() == MFParamType::Output) { + state.can_be_initialized = true; + state_modified = true; + break; + } + } + } + break; + } + case MFInstructionType::Destruct: { + const MFDestructInstruction &destruct_instruction = + static_cast<const MFDestructInstruction &>(instruction); + if (destruct_instruction.variable_ == &target_variable) { + state.can_be_uninitialized = true; + state_modified = true; + } + break; + } + case MFInstructionType::Branch: + case MFInstructionType::Dummy: + case MFInstructionType::Return: { + /* These instruction types don't change the initialization state of variables. */ + break; + } + } + + if (!state_modified) { + if (&instruction == entry_) { + check_entry_instruction(); + } + for (const MFInstructionCursor &cursor : instruction.prev_) { + if (cursor.instruction() != nullptr) { + instructions_to_check.push(cursor.instruction()); + } + } + } + } + + return state; +} + +class MFProcedureDotExport { + private: + const MFProcedure &procedure_; + dot::DirectedGraph digraph_; + Map<const MFInstruction *, dot::Node *> dot_nodes_by_begin_; + Map<const MFInstruction *, dot::Node *> dot_nodes_by_end_; + + public: + MFProcedureDotExport(const MFProcedure &procedure) : procedure_(procedure) + { + } + + std::string generate() + { + this->create_nodes(); + this->create_edges(); + return digraph_.to_dot_string(); + } + + void create_nodes() + { + Vector<const MFInstruction *> all_instructions; + auto add_instructions = [&](auto instructions) { + all_instructions.extend(instructions.begin(), instructions.end()); + }; + add_instructions(procedure_.call_instructions_); + add_instructions(procedure_.branch_instructions_); + add_instructions(procedure_.destruct_instructions_); + add_instructions(procedure_.dummy_instructions_); + add_instructions(procedure_.return_instructions_); + + Set<const MFInstruction *> handled_instructions; + + for (const MFInstruction *representative : all_instructions) { + if (handled_instructions.contains(representative)) { + continue; + } + Vector<const MFInstruction *> block_instructions = this->get_instructions_in_block( + *representative); + std::stringstream ss; + ss << "<"; + + for (const MFInstruction *current : block_instructions) { + handled_instructions.add_new(current); + switch (current->type()) { + case MFInstructionType::Call: { + this->instruction_to_string(*static_cast<const MFCallInstruction *>(current), ss); + break; + } + case MFInstructionType::Destruct: { + this->instruction_to_string(*static_cast<const MFDestructInstruction *>(current), ss); + break; + } + case MFInstructionType::Dummy: { + this->instruction_to_string(*static_cast<const MFDummyInstruction *>(current), ss); + break; + } + case MFInstructionType::Return: { + this->instruction_to_string(*static_cast<const MFReturnInstruction *>(current), ss); + break; + } + case MFInstructionType::Branch: { + this->instruction_to_string(*static_cast<const MFBranchInstruction *>(current), ss); + break; + } + } + ss << R"(<br align="left" />)"; + } + ss << ">"; + + dot::Node &dot_node = digraph_.new_node(ss.str()); + dot_node.set_shape(dot::Attr_shape::Rectangle); + dot_nodes_by_begin_.add_new(block_instructions.first(), &dot_node); + dot_nodes_by_end_.add_new(block_instructions.last(), &dot_node); + } + } + + void create_edges() + { + auto create_edge = [&](dot::Node &from_node, + const MFInstruction *to_instruction) -> dot::DirectedEdge & { + if (to_instruction == nullptr) { + dot::Node &to_node = digraph_.new_node("missing"); + to_node.set_shape(dot::Attr_shape::Diamond); + return digraph_.new_edge(from_node, to_node); + } + dot::Node &to_node = *dot_nodes_by_begin_.lookup(to_instruction); + return digraph_.new_edge(from_node, to_node); + }; + + for (auto item : dot_nodes_by_end_.items()) { + const MFInstruction &from_instruction = *item.key; + dot::Node &from_node = *item.value; + switch (from_instruction.type()) { + case MFInstructionType::Call: { + const MFInstruction *to_instruction = + static_cast<const MFCallInstruction &>(from_instruction).next(); + create_edge(from_node, to_instruction); + break; + } + case MFInstructionType::Destruct: { + const MFInstruction *to_instruction = + static_cast<const MFDestructInstruction &>(from_instruction).next(); + create_edge(from_node, to_instruction); + break; + } + case MFInstructionType::Dummy: { + const MFInstruction *to_instruction = + static_cast<const MFDummyInstruction &>(from_instruction).next(); + create_edge(from_node, to_instruction); + break; + } + case MFInstructionType::Return: { + break; + } + case MFInstructionType::Branch: { + const MFBranchInstruction &branch_instruction = static_cast<const MFBranchInstruction &>( + from_instruction); + const MFInstruction *to_true_instruction = branch_instruction.branch_true(); + const MFInstruction *to_false_instruction = branch_instruction.branch_false(); + create_edge(from_node, to_true_instruction).attributes.set("color", "#118811"); + create_edge(from_node, to_false_instruction).attributes.set("color", "#881111"); + break; + } + } + } + + dot::Node &entry_node = this->create_entry_node(); + create_edge(entry_node, procedure_.entry()); + } + + bool has_to_be_block_begin(const MFInstruction &instruction) + { + if (instruction.prev().size() != 1) { + return true; + } + if (ELEM(instruction.prev()[0].type(), + MFInstructionCursor::Type::Branch, + MFInstructionCursor::Type::Entry)) { + return true; + } + return false; + } + + const MFInstruction &get_first_instruction_in_block(const MFInstruction &representative) + { + const MFInstruction *current = &representative; + while (!this->has_to_be_block_begin(*current)) { + current = current->prev()[0].instruction(); + if (current == &representative) { + /* There is a loop without entry or exit, just break it up here. */ + break; + } + } + return *current; + } + + const MFInstruction *get_next_instruction_in_block(const MFInstruction &instruction, + const MFInstruction &block_begin) + { + const MFInstruction *next = nullptr; + switch (instruction.type()) { + case MFInstructionType::Call: { + next = static_cast<const MFCallInstruction &>(instruction).next(); + break; + } + case MFInstructionType::Destruct: { + next = static_cast<const MFDestructInstruction &>(instruction).next(); + break; + } + case MFInstructionType::Dummy: { + next = static_cast<const MFDummyInstruction &>(instruction).next(); + break; + } + case MFInstructionType::Return: + case MFInstructionType::Branch: { + break; + } + } + if (next == nullptr) { + return nullptr; + } + if (next == &block_begin) { + return nullptr; + } + if (this->has_to_be_block_begin(*next)) { + return nullptr; + } + return next; + } + + Vector<const MFInstruction *> get_instructions_in_block(const MFInstruction &representative) + { + Vector<const MFInstruction *> instructions; + const MFInstruction &begin = this->get_first_instruction_in_block(representative); + for (const MFInstruction *current = &begin; current != nullptr; + current = this->get_next_instruction_in_block(*current, begin)) { + instructions.append(current); + } + return instructions; + } + + void variable_to_string(const MFVariable *variable, std::stringstream &ss) + { + if (variable == nullptr) { + ss << "null"; + } + else { + ss << "$" << variable->id(); + if (!variable->name().is_empty()) { + ss << "(" << variable->name() << ")"; + } + } + } + + void instruction_name_format(StringRef name, std::stringstream &ss) + { + ss << name; + } + + void instruction_to_string(const MFCallInstruction &instruction, std::stringstream &ss) + { + const MultiFunction &fn = instruction.fn(); + this->instruction_name_format(fn.name() + ": ", ss); + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + const MFVariable *variable = instruction.params()[param_index]; + ss << R"(<font color="grey30">)"; + switch (param_type.interface_type()) { + case MFParamType::Input: { + ss << "in"; + break; + } + case MFParamType::Mutable: { + ss << "mut"; + break; + } + case MFParamType::Output: { + ss << "out"; + break; + } + } + ss << " </font> "; + variable_to_string(variable, ss); + if (param_index < fn.param_amount() - 1) { + ss << ", "; + } + } + } + + void instruction_to_string(const MFDestructInstruction &instruction, std::stringstream &ss) + { + instruction_name_format("Destruct ", ss); + variable_to_string(instruction.variable(), ss); + } + + void instruction_to_string(const MFDummyInstruction &UNUSED(instruction), std::stringstream &ss) + { + instruction_name_format("Dummy ", ss); + } + + void instruction_to_string(const MFReturnInstruction &UNUSED(instruction), std::stringstream &ss) + { + instruction_name_format("Return ", ss); + + Vector<ConstMFParameter> outgoing_parameters; + for (const ConstMFParameter ¶m : procedure_.params()) { + if (ELEM(param.type, MFParamType::Mutable, MFParamType::Output)) { + outgoing_parameters.append(param); + } + } + for (const int param_index : outgoing_parameters.index_range()) { + const ConstMFParameter ¶m = outgoing_parameters[param_index]; + variable_to_string(param.variable, ss); + if (param_index < outgoing_parameters.size() - 1) { + ss << ", "; + } + } + } + + void instruction_to_string(const MFBranchInstruction &instruction, std::stringstream &ss) + { + instruction_name_format("Branch ", ss); + variable_to_string(instruction.condition(), ss); + } + + dot::Node &create_entry_node() + { + std::stringstream ss; + ss << "Entry: "; + Vector<ConstMFParameter> incoming_parameters; + for (const ConstMFParameter ¶m : procedure_.params()) { + if (ELEM(param.type, MFParamType::Input, MFParamType::Mutable)) { + incoming_parameters.append(param); + } + } + for (const int param_index : incoming_parameters.index_range()) { + const ConstMFParameter ¶m = incoming_parameters[param_index]; + variable_to_string(param.variable, ss); + if (param_index < incoming_parameters.size() - 1) { + ss << ", "; + } + } + + dot::Node &node = digraph_.new_node(ss.str()); + node.set_shape(dot::Attr_shape::Ellipse); + return node; + } +}; + +std::string MFProcedure::to_dot() const +{ + MFProcedureDotExport dot_export{*this}; + return dot_export.generate(); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure_builder.cc b/source/blender/functions/intern/multi_function_procedure_builder.cc new file mode 100644 index 00000000000..d30e6c0e14a --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure_builder.cc @@ -0,0 +1,131 @@ +/* + * 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 "FN_multi_function_procedure_builder.hh" + +namespace blender::fn { + +void MFProcedureBuilder::add_destruct(MFVariable &variable) +{ + MFDestructInstruction &instruction = procedure_->new_destruct_instruction(); + instruction.set_variable(&variable); + this->link_to_cursors(&instruction); + cursors_ = {MFInstructionCursor{instruction}}; +} + +void MFProcedureBuilder::add_destruct(Span<MFVariable *> variables) +{ + for (MFVariable *variable : variables) { + this->add_destruct(*variable); + } +} + +MFReturnInstruction &MFProcedureBuilder::add_return() +{ + MFReturnInstruction &instruction = procedure_->new_return_instruction(); + this->link_to_cursors(&instruction); + cursors_ = {}; + return instruction; +} + +MFCallInstruction &MFProcedureBuilder::add_call_with_no_variables(const MultiFunction &fn) +{ + MFCallInstruction &instruction = procedure_->new_call_instruction(fn); + this->link_to_cursors(&instruction); + cursors_ = {MFInstructionCursor{instruction}}; + return instruction; +} + +MFCallInstruction &MFProcedureBuilder::add_call_with_all_variables( + const MultiFunction &fn, Span<MFVariable *> param_variables) +{ + MFCallInstruction &instruction = this->add_call_with_no_variables(fn); + instruction.set_params(param_variables); + return instruction; +} + +Vector<MFVariable *> MFProcedureBuilder::add_call(const MultiFunction &fn, + Span<MFVariable *> input_and_mutable_variables) +{ + Vector<MFVariable *> output_variables; + MFCallInstruction &instruction = this->add_call_with_no_variables(fn); + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + switch (param_type.interface_type()) { + case MFParamType::Input: + case MFParamType::Mutable: { + MFVariable *variable = input_and_mutable_variables.first(); + instruction.set_param_variable(param_index, variable); + input_and_mutable_variables = input_and_mutable_variables.drop_front(1); + break; + } + case MFParamType::Output: { + MFVariable &variable = procedure_->new_variable(param_type.data_type(), + fn.param_name(param_index)); + instruction.set_param_variable(param_index, &variable); + output_variables.append(&variable); + break; + } + } + } + /* All passed in variables should have been dropped in the loop above. */ + BLI_assert(input_and_mutable_variables.is_empty()); + return output_variables; +} + +MFProcedureBuilder::Branch MFProcedureBuilder::add_branch(MFVariable &condition) +{ + MFBranchInstruction &instruction = procedure_->new_branch_instruction(); + instruction.set_condition(&condition); + this->link_to_cursors(&instruction); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); + + Branch branch{*procedure_, *procedure_}; + branch.branch_true.set_cursor(MFInstructionCursor{instruction, true}); + branch.branch_false.set_cursor(MFInstructionCursor{instruction, false}); + return branch; +} + +MFProcedureBuilder::Loop MFProcedureBuilder::add_loop() +{ + MFDummyInstruction &loop_begin = procedure_->new_dummy_instruction(); + MFDummyInstruction &loop_end = procedure_->new_dummy_instruction(); + this->link_to_cursors(&loop_begin); + cursors_ = {MFInstructionCursor{loop_begin}}; + + Loop loop; + loop.begin = &loop_begin; + loop.end = &loop_end; + + return loop; +} + +void MFProcedureBuilder::add_loop_continue(Loop &loop) +{ + this->link_to_cursors(loop.begin); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); +} + +void MFProcedureBuilder::add_loop_break(Loop &loop) +{ + this->link_to_cursors(loop.end); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc new file mode 100644 index 00000000000..b97282accdd --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure_executor.cc @@ -0,0 +1,1227 @@ +/* + * 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 "FN_multi_function_procedure_executor.hh" + +#include "BLI_stack.hh" + +namespace blender::fn { + +MFProcedureExecutor::MFProcedureExecutor(std::string name, const MFProcedure &procedure) + : procedure_(procedure) +{ + MFSignatureBuilder signature(std::move(name)); + + for (const ConstMFParameter ¶m : procedure.params()) { + signature.add(param.variable->name(), MFParamType(param.type, param.variable->data_type())); + } + + signature_ = signature.build(); + this->set_signature(&signature_); +} + +using IndicesSplitVectors = std::array<Vector<int64_t>, 2>; + +namespace { +enum class ValueType { + GVArray = 0, + Span = 1, + GVVectorArray = 2, + GVectorArray = 3, + OneSingle = 4, + OneVector = 5, +}; +constexpr int tot_variable_value_types = 6; +} // namespace + +/** + * During evaluation, a variable may be stored in various different forms, depending on what + * instructions do with the variables. + */ +struct VariableValue { + ValueType type; + + VariableValue(ValueType type) : type(type) + { + } +}; + +/* This variable is the unmodified virtual array from the caller. */ +struct VariableValue_GVArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVArray; + const GVArray &data; + + VariableValue_GVArray(const GVArray &data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has a different value for every index. Some values may be uninitialized. The span + * may be owned by the caller. */ +struct VariableValue_Span : public VariableValue { + static inline constexpr ValueType static_type = ValueType::Span; + void *data; + bool owned; + + VariableValue_Span(void *data, bool owned) : VariableValue(static_type), data(data), owned(owned) + { + } +}; + +/* This variable is the unmodified virtual vector array from the caller. */ +struct VariableValue_GVVectorArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVVectorArray; + const GVVectorArray &data; + + VariableValue_GVVectorArray(const GVVectorArray &data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has a different vector for every index. */ +struct VariableValue_GVectorArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVectorArray; + GVectorArray &data; + bool owned; + + VariableValue_GVectorArray(GVectorArray &data, bool owned) + : VariableValue(static_type), data(data), owned(owned) + { + } +}; + +/* This variable has the same value for every index. */ +struct VariableValue_OneSingle : public VariableValue { + static inline constexpr ValueType static_type = ValueType::OneSingle; + void *data; + bool is_initialized = false; + + VariableValue_OneSingle(void *data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has the same vector for every index. */ +struct VariableValue_OneVector : public VariableValue { + static inline constexpr ValueType static_type = ValueType::OneVector; + GVectorArray &data; + + VariableValue_OneVector(GVectorArray &data) : VariableValue(static_type), data(data) + { + } +}; + +static_assert(std::is_trivially_destructible_v<VariableValue_GVArray>); +static_assert(std::is_trivially_destructible_v<VariableValue_Span>); +static_assert(std::is_trivially_destructible_v<VariableValue_GVVectorArray>); +static_assert(std::is_trivially_destructible_v<VariableValue_GVectorArray>); +static_assert(std::is_trivially_destructible_v<VariableValue_OneSingle>); +static_assert(std::is_trivially_destructible_v<VariableValue_OneVector>); + +class VariableState; + +/** + * The #ValueAllocator is responsible for providing memory for variables and their values. It also + * manages the reuse of buffers to improve performance. + */ +class ValueAllocator : NonCopyable, NonMovable { + private: + /* Allocate with 64 byte alignment for better reusability of buffers and improved cache + * performance. */ + static constexpr inline int min_alignment = 64; + + /* Use stacks so that the most recently used buffers are reused first. This improves cache + * efficiency. */ + std::array<Stack<VariableValue *>, tot_variable_value_types> values_free_lists_; + /* The integer key is the size of one element (e.g. 4 for an integer buffer). All buffers are + * aligned to #min_alignment bytes. */ + Map<int, Stack<void *>> span_buffers_free_list_; + + public: + ValueAllocator() = default; + + ~ValueAllocator() + { + for (Stack<VariableValue *> &stack : values_free_lists_) { + while (!stack.is_empty()) { + MEM_freeN(stack.pop()); + } + } + for (Stack<void *> &stack : span_buffers_free_list_.values()) { + while (!stack.is_empty()) { + MEM_freeN(stack.pop()); + } + } + } + + template<typename... Args> VariableState *obtain_variable_state(Args &&...args); + + void release_variable_state(VariableState *state); + + VariableValue_GVArray *obtain_GVArray(const GVArray &varray) + { + return this->obtain<VariableValue_GVArray>(varray); + } + + VariableValue_GVVectorArray *obtain_GVVectorArray(const GVVectorArray &varray) + { + return this->obtain<VariableValue_GVVectorArray>(varray); + } + + VariableValue_Span *obtain_Span_not_owned(void *buffer) + { + return this->obtain<VariableValue_Span>(buffer, false); + } + + VariableValue_Span *obtain_Span(const CPPType &type, int size) + { + void *buffer = nullptr; + + const int element_size = type.size(); + const int alignment = type.alignment(); + + if (alignment > min_alignment) { + /* In this rare case we fallback to not reusing existing buffers. */ + buffer = MEM_mallocN_aligned(element_size * size, alignment, __func__); + } + else { + Stack<void *> *stack = span_buffers_free_list_.lookup_ptr(element_size); + if (stack == nullptr || stack->is_empty()) { + buffer = MEM_mallocN_aligned(element_size * size, min_alignment, __func__); + } + else { + /* Reuse existing buffer. */ + buffer = stack->pop(); + } + } + + return this->obtain<VariableValue_Span>(buffer, true); + } + + VariableValue_GVectorArray *obtain_GVectorArray_not_owned(GVectorArray &data) + { + return this->obtain<VariableValue_GVectorArray>(data, false); + } + + VariableValue_GVectorArray *obtain_GVectorArray(const CPPType &type, int size) + { + GVectorArray *vector_array = new GVectorArray(type, size); + return this->obtain<VariableValue_GVectorArray>(*vector_array, true); + } + + VariableValue_OneSingle *obtain_OneSingle(const CPPType &type) + { + void *buffer = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + return this->obtain<VariableValue_OneSingle>(buffer); + } + + VariableValue_OneVector *obtain_OneVector(const CPPType &type) + { + GVectorArray *vector_array = new GVectorArray(type, 1); + return this->obtain<VariableValue_OneVector>(*vector_array); + } + + void release_value(VariableValue *value, const MFDataType &data_type) + { + switch (value->type) { + case ValueType::GVArray: { + break; + } + case ValueType::Span: { + auto *value_typed = static_cast<VariableValue_Span *>(value); + if (value_typed->owned) { + const CPPType &type = data_type.single_type(); + /* Assumes all values in the buffer are uninitialized already. */ + Stack<void *> &buffers = span_buffers_free_list_.lookup_or_add_default(type.size()); + buffers.push(value_typed->data); + } + break; + } + case ValueType::GVVectorArray: { + break; + } + case ValueType::GVectorArray: { + auto *value_typed = static_cast<VariableValue_GVectorArray *>(value); + if (value_typed->owned) { + delete &value_typed->data; + } + break; + } + case ValueType::OneSingle: { + auto *value_typed = static_cast<VariableValue_OneSingle *>(value); + if (value_typed->is_initialized) { + const CPPType &type = data_type.single_type(); + type.destruct(value_typed->data); + } + MEM_freeN(value_typed->data); + break; + } + case ValueType::OneVector: { + auto *value_typed = static_cast<VariableValue_OneVector *>(value); + delete &value_typed->data; + break; + } + } + + Stack<VariableValue *> &stack = values_free_lists_[(int)value->type]; + stack.push(value); + } + + private: + template<typename T, typename... Args> T *obtain(Args &&...args) + { + static_assert(std::is_base_of_v<VariableValue, T>); + Stack<VariableValue *> &stack = values_free_lists_[(int)T::static_type]; + if (stack.is_empty()) { + void *buffer = MEM_mallocN(sizeof(T), __func__); + return new (buffer) T(std::forward<Args>(args)...); + } + return new (stack.pop()) T(std::forward<Args>(args)...); + } +}; + +/** + * This class keeps track of a single variable during evaluation. + */ +class VariableState : NonCopyable, NonMovable { + private: + /** The current value of the variable. The storage format may change over time. */ + VariableValue *value_; + /** Number of indices that are currently initialized in this variable. */ + int tot_initialized_; + /* This a non-owning pointer to either span buffer or #GVectorArray or null. */ + void *caller_provided_storage_ = nullptr; + + public: + VariableState(VariableValue &value, int tot_initialized, void *caller_provided_storage = nullptr) + : value_(&value), + tot_initialized_(tot_initialized), + caller_provided_storage_(caller_provided_storage) + { + } + + void destruct_self(ValueAllocator &value_allocator, const MFDataType &data_type) + { + value_allocator.release_value(value_, data_type); + value_allocator.release_variable_state(this); + } + + /* True if this contains only one value for all indices, i.e. the value for all indices is + * the same. */ + bool is_one() const + { + switch (value_->type) { + case ValueType::GVArray: + return this->value_as<VariableValue_GVArray>()->data.is_single(); + case ValueType::Span: + return tot_initialized_ == 0; + case ValueType::GVVectorArray: + return this->value_as<VariableValue_GVVectorArray>()->data.is_single_vector(); + case ValueType::GVectorArray: + return tot_initialized_ == 0; + case ValueType::OneSingle: + return true; + case ValueType::OneVector: + return true; + } + BLI_assert_unreachable(); + return false; + } + + bool is_fully_initialized(const IndexMask full_mask) + { + return tot_initialized_ == full_mask.size(); + } + + bool is_fully_uninitialized(const IndexMask full_mask) + { + UNUSED_VARS(full_mask); + return tot_initialized_ == 0; + } + + void add_as_input(MFParamsBuilder ¶ms, IndexMask mask, const MFDataType &data_type) const + { + /* Sanity check to make sure that enough values are initialized. */ + BLI_assert(mask.size() <= tot_initialized_); + + switch (value_->type) { + case ValueType::GVArray: { + params.add_readonly_single_input(this->value_as<VariableValue_GVArray>()->data); + break; + } + case ValueType::Span: { + const void *data = this->value_as<VariableValue_Span>()->data; + const GSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_readonly_single_input(span); + break; + } + case ValueType::GVVectorArray: { + params.add_readonly_vector_input(this->value_as<VariableValue_GVVectorArray>()->data); + break; + } + case ValueType::GVectorArray: { + params.add_readonly_vector_input(this->value_as<VariableValue_GVectorArray>()->data); + break; + } + case ValueType::OneSingle: { + const auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + const GPointer gpointer{data_type.single_type(), value_typed->data}; + params.add_readonly_single_input(gpointer); + break; + } + case ValueType::OneVector: { + params.add_readonly_vector_input(this->value_as<VariableValue_OneVector>()->data[0]); + break; + } + } + } + + void ensure_is_mutable(IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + if (ELEM(value_->type, ValueType::Span, ValueType::GVectorArray)) { + return; + } + + const int array_size = full_mask.min_array_size(); + + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + VariableValue_Span *new_value = nullptr; + if (caller_provided_storage_ == nullptr) { + new_value = value_allocator.obtain_Span(type, array_size); + } + else { + /* Reuse the storage provided caller when possible. */ + new_value = value_allocator.obtain_Span_not_owned(caller_provided_storage_); + } + if (value_->type == ValueType::GVArray) { + /* Fill new buffer with data from virtual array. */ + this->value_as<VariableValue_GVArray>()->data.materialize_to_uninitialized( + full_mask, new_value->data); + } + else if (value_->type == ValueType::OneSingle) { + auto *old_value_typed_ = this->value_as<VariableValue_OneSingle>(); + if (old_value_typed_->is_initialized) { + /* Fill the buffer with a single value. */ + type.fill_construct_indices(old_value_typed_->data, new_value->data, full_mask); + } + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + VariableValue_GVectorArray *new_value = nullptr; + if (caller_provided_storage_ == nullptr) { + new_value = value_allocator.obtain_GVectorArray(type, array_size); + } + else { + new_value = value_allocator.obtain_GVectorArray_not_owned( + *(GVectorArray *)caller_provided_storage_); + } + if (value_->type == ValueType::GVVectorArray) { + /* Fill new vector array with data from virtual vector array. */ + new_value->data.extend(full_mask, this->value_as<VariableValue_GVVectorArray>()->data); + } + else if (value_->type == ValueType::OneVector) { + /* Fill all indices with the same value. */ + const GSpan vector = this->value_as<VariableValue_OneVector>()->data[0]; + new_value->data.extend(full_mask, GVVectorArray_For_SingleGSpan{vector, array_size}); + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + } + } + + void add_as_mutable(MFParamsBuilder ¶ms, + IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + /* Sanity check to make sure that enough values are initialized. */ + BLI_assert(mask.size() <= tot_initialized_); + + this->ensure_is_mutable(full_mask, data_type, value_allocator); + + switch (value_->type) { + case ValueType::Span: { + void *data = this->value_as<VariableValue_Span>()->data; + const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_single_mutable(span); + break; + } + case ValueType::GVectorArray: { + params.add_vector_mutable(this->value_as<VariableValue_GVectorArray>()->data); + break; + } + case ValueType::GVArray: + case ValueType::GVVectorArray: + case ValueType::OneSingle: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + } + + void add_as_output(MFParamsBuilder ¶ms, + IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + /* Sanity check to make sure that enough values are not initialized. */ + BLI_assert(mask.size() <= full_mask.size() - tot_initialized_); + this->ensure_is_mutable(full_mask, data_type, value_allocator); + + switch (value_->type) { + case ValueType::Span: { + void *data = this->value_as<VariableValue_Span>()->data; + const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_uninitialized_single_output(span); + break; + } + case ValueType::GVectorArray: { + params.add_vector_output(this->value_as<VariableValue_GVectorArray>()->data); + break; + } + case ValueType::GVArray: + case ValueType::GVVectorArray: + case ValueType::OneSingle: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + + tot_initialized_ += mask.size(); + } + + void add_as_input__one(MFParamsBuilder ¶ms, const MFDataType &data_type) const + { + BLI_assert(this->is_one()); + + switch (value_->type) { + case ValueType::GVArray: { + params.add_readonly_single_input(this->value_as<VariableValue_GVArray>()->data); + break; + } + case ValueType::GVVectorArray: { + params.add_readonly_vector_input(this->value_as<VariableValue_GVVectorArray>()->data); + break; + } + case ValueType::OneSingle: { + const auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + GPointer ptr{data_type.single_type(), value_typed->data}; + params.add_readonly_single_input(ptr); + break; + } + case ValueType::OneVector: { + params.add_readonly_vector_input(this->value_as<VariableValue_OneVector>()->data); + break; + } + case ValueType::Span: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + } + + void ensure_is_mutable__one(const MFDataType &data_type, ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + if (ELEM(value_->type, ValueType::OneSingle, ValueType::OneVector)) { + return; + } + + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + VariableValue_OneSingle *new_value = value_allocator.obtain_OneSingle(type); + if (value_->type == ValueType::GVArray) { + this->value_as<VariableValue_GVArray>()->data.get_internal_single_to_uninitialized( + new_value->data); + new_value->is_initialized = true; + } + else if (value_->type == ValueType::Span) { + BLI_assert(tot_initialized_ == 0); + /* Nothing to do, the single value is uninitialized already. */ + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + VariableValue_OneVector *new_value = value_allocator.obtain_OneVector(type); + if (value_->type == ValueType::GVVectorArray) { + const GVVectorArray &old_vector_array = + this->value_as<VariableValue_GVVectorArray>()->data; + new_value->data.extend(IndexRange(1), old_vector_array); + } + else if (value_->type == ValueType::GVectorArray) { + BLI_assert(tot_initialized_ == 0); + /* Nothing to do. */ + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + } + } + + void add_as_mutable__one(MFParamsBuilder ¶ms, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + this->ensure_is_mutable__one(data_type, value_allocator); + + switch (value_->type) { + case ValueType::OneSingle: { + auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + params.add_single_mutable(GMutableSpan{data_type.single_type(), value_typed->data, 1}); + break; + } + case ValueType::OneVector: { + params.add_vector_mutable(this->value_as<VariableValue_OneVector>()->data); + break; + } + case ValueType::GVArray: + case ValueType::Span: + case ValueType::GVVectorArray: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + } + + void add_as_output__one(MFParamsBuilder ¶ms, + IndexMask mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + this->ensure_is_mutable__one(data_type, value_allocator); + + switch (value_->type) { + case ValueType::OneSingle: { + auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(!value_typed->is_initialized); + params.add_uninitialized_single_output( + GMutableSpan{data_type.single_type(), value_typed->data, 1}); + /* It becomes initialized when the multi-function is called. */ + value_typed->is_initialized = true; + break; + } + case ValueType::OneVector: { + auto *value_typed = this->value_as<VariableValue_OneVector>(); + BLI_assert(value_typed->data[0].is_empty()); + params.add_vector_output(value_typed->data); + break; + } + case ValueType::GVArray: + case ValueType::Span: + case ValueType::GVVectorArray: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + + tot_initialized_ += mask.size(); + } + + void destruct(IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + int new_tot_initialized = tot_initialized_ - mask.size(); + + /* Sanity check to make sure that enough indices can be destructed. */ + BLI_assert(new_tot_initialized >= 0); + + switch (value_->type) { + case ValueType::GVArray: { + if (mask.size() == full_mask.size()) { + /* All elements are destructed. The elements are owned by the caller, so we don't + * actually destruct them. */ + value_allocator.release_value(value_, data_type); + value_ = value_allocator.obtain_OneSingle(data_type.single_type()); + } + else { + /* Not all elements are destructed. Since we can't work on the original array, we have to + * create a copy first. */ + this->ensure_is_mutable(full_mask, data_type, value_allocator); + BLI_assert(value_->type == ValueType::Span); + const CPPType &type = data_type.single_type(); + type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask); + } + break; + } + case ValueType::Span: { + const CPPType &type = data_type.single_type(); + type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask); + if (new_tot_initialized == 0) { + /* Release span when all values are initialized. */ + value_allocator.release_value(value_, data_type); + value_ = value_allocator.obtain_OneSingle(data_type.single_type()); + } + break; + } + case ValueType::GVVectorArray: { + if (mask.size() == full_mask.size()) { + /* All elements are cleared. The elements are owned by the caller, so don't actually + * destruct them. */ + value_allocator.release_value(value_, data_type); + value_ = value_allocator.obtain_OneVector(data_type.vector_base_type()); + } + else { + /* Not all elements are cleared. Since we can't work on the original vector array, we + * have to create a copy first. A possible future optimization is to create the partial + * copy directly. */ + this->ensure_is_mutable(full_mask, data_type, value_allocator); + BLI_assert(value_->type == ValueType::GVectorArray); + this->value_as<VariableValue_GVectorArray>()->data.clear(mask); + } + break; + } + case ValueType::GVectorArray: { + this->value_as<VariableValue_GVectorArray>()->data.clear(mask); + break; + } + case ValueType::OneSingle: { + auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + if (mask.size() == tot_initialized_) { + const CPPType &type = data_type.single_type(); + type.destruct(value_typed->data); + value_typed->is_initialized = false; + } + break; + } + case ValueType::OneVector: { + auto *value_typed = this->value_as<VariableValue_OneVector>(); + if (mask.size() == tot_initialized_) { + value_typed->data.clear({0}); + } + break; + } + } + + tot_initialized_ = new_tot_initialized; + } + + void indices_split(IndexMask mask, IndicesSplitVectors &r_indices) + { + BLI_assert(mask.size() <= tot_initialized_); + + switch (value_->type) { + case ValueType::GVArray: { + const GVArray_Typed<bool> varray{this->value_as<VariableValue_GVArray>()->data}; + for (const int i : mask) { + r_indices[varray[i]].append(i); + } + break; + } + case ValueType::Span: { + const Span<bool> span((bool *)this->value_as<VariableValue_Span>()->data, + mask.min_array_size()); + for (const int i : mask) { + r_indices[span[i]].append(i); + } + break; + } + case ValueType::OneSingle: { + auto *value_typed = this->value_as<VariableValue_OneSingle>(); + BLI_assert(value_typed->is_initialized); + const bool condition = *(bool *)value_typed->data; + r_indices[condition].extend(mask); + break; + } + case ValueType::GVVectorArray: + case ValueType::GVectorArray: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + } + + template<typename T> T *value_as() + { + BLI_assert(value_->type == T::static_type); + return static_cast<T *>(value_); + } + + template<typename T> const T *value_as() const + { + BLI_assert(value_->type == T::static_type); + return static_cast<T *>(value_); + } +}; + +template<typename... Args> VariableState *ValueAllocator::obtain_variable_state(Args &&...args) +{ + return new VariableState(std::forward<Args>(args)...); +} + +void ValueAllocator::release_variable_state(VariableState *state) +{ + delete state; +} + +/** Keeps track of the states of all variables during evaluation. */ +class VariableStates { + private: + ValueAllocator value_allocator_; + Map<const MFVariable *, VariableState *> variable_states_; + IndexMask full_mask_; + + public: + VariableStates(IndexMask full_mask) : full_mask_(full_mask) + { + } + + ~VariableStates() + { + for (auto &&item : variable_states_.items()) { + const MFVariable *variable = item.key; + VariableState *state = item.value; + state->destruct_self(value_allocator_, variable->data_type()); + } + } + + ValueAllocator &value_allocator() + { + return value_allocator_; + } + + const IndexMask &full_mask() const + { + return full_mask_; + } + + void add_initial_variable_states(const MFProcedureExecutor &fn, + const MFProcedure &procedure, + MFParams ¶ms) + { + for (const int param_index : fn.param_indices()) { + MFParamType param_type = fn.param_type(param_index); + const MFVariable *variable = procedure.params()[param_index].variable; + + auto add_state = [&](VariableValue *value, + bool input_is_initialized, + void *caller_provided_storage = nullptr) { + const int tot_initialized = input_is_initialized ? full_mask_.size() : 0; + variable_states_.add_new(variable, + value_allocator_.obtain_variable_state( + *value, tot_initialized, caller_provided_storage)); + }; + + switch (param_type.category()) { + case MFParamType::SingleInput: { + const GVArray &data = params.readonly_single_input(param_index); + add_state(value_allocator_.obtain_GVArray(data), true); + break; + } + case MFParamType::VectorInput: { + const GVVectorArray &data = params.readonly_vector_input(param_index); + add_state(value_allocator_.obtain_GVVectorArray(data), true); + break; + } + case MFParamType::SingleOutput: { + GMutableSpan data = params.uninitialized_single_output(param_index); + add_state(value_allocator_.obtain_Span_not_owned(data.data()), false, data.data()); + break; + } + case MFParamType::VectorOutput: { + GVectorArray &data = params.vector_output(param_index); + add_state(value_allocator_.obtain_GVectorArray_not_owned(data), false, &data); + break; + } + case MFParamType::SingleMutable: { + GMutableSpan data = params.single_mutable(param_index); + add_state(value_allocator_.obtain_Span_not_owned(data.data()), true, data.data()); + break; + } + case MFParamType::VectorMutable: { + GVectorArray &data = params.vector_mutable(param_index); + add_state(value_allocator_.obtain_GVectorArray_not_owned(data), true, &data); + break; + } + } + } + } + + void add_as_param(VariableState &variable_state, + MFParamsBuilder ¶ms, + const MFParamType ¶m_type, + const IndexMask &mask) + { + const MFDataType data_type = param_type.data_type(); + switch (param_type.interface_type()) { + case MFParamType::Input: { + variable_state.add_as_input(params, mask, data_type); + break; + } + case MFParamType::Mutable: { + variable_state.add_as_mutable(params, mask, full_mask_, data_type, value_allocator_); + break; + } + case MFParamType::Output: { + variable_state.add_as_output(params, mask, full_mask_, data_type, value_allocator_); + break; + } + } + } + + void add_as_param__one(VariableState &variable_state, + MFParamsBuilder ¶ms, + const MFParamType ¶m_type, + const IndexMask &mask) + { + const MFDataType data_type = param_type.data_type(); + switch (param_type.interface_type()) { + case MFParamType::Input: { + variable_state.add_as_input__one(params, data_type); + break; + } + case MFParamType::Mutable: { + variable_state.add_as_mutable__one(params, data_type, value_allocator_); + break; + } + case MFParamType::Output: { + variable_state.add_as_output__one(params, mask, data_type, value_allocator_); + break; + } + } + } + + void destruct(const MFVariable &variable, const IndexMask &mask) + { + VariableState &variable_state = this->get_variable_state(variable); + variable_state.destruct(mask, full_mask_, variable.data_type(), value_allocator_); + } + + VariableState &get_variable_state(const MFVariable &variable) + { + return *variable_states_.lookup_or_add_cb( + &variable, [&]() { return this->create_new_state_for_variable(variable); }); + } + + VariableState *create_new_state_for_variable(const MFVariable &variable) + { + MFDataType data_type = variable.data_type(); + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + return value_allocator_.obtain_variable_state(*value_allocator_.obtain_OneSingle(type), 0); + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + return value_allocator_.obtain_variable_state(*value_allocator_.obtain_OneVector(type), 0); + } + } + BLI_assert_unreachable(); + return nullptr; + } +}; + +static bool evaluate_as_one(const MultiFunction &fn, + Span<VariableState *> param_variable_states, + const IndexMask &mask, + const IndexMask &full_mask) +{ + if (fn.depends_on_context()) { + return false; + } + if (mask.size() < full_mask.size()) { + return false; + } + for (VariableState *state : param_variable_states) { + if (state != nullptr && !state->is_one()) { + return false; + } + } + return true; +} + +static void execute_call_instruction(const MFCallInstruction &instruction, + IndexMask mask, + VariableStates &variable_states, + const MFContext &context) +{ + const MultiFunction &fn = instruction.fn(); + + Vector<VariableState *> param_variable_states; + param_variable_states.resize(fn.param_amount()); + + for (const int param_index : fn.param_indices()) { + const MFVariable *variable = instruction.params()[param_index]; + if (variable == nullptr) { + param_variable_states[param_index] = nullptr; + } + else { + VariableState &variable_state = variable_states.get_variable_state(*variable); + param_variable_states[param_index] = &variable_state; + } + } + + /* If all inputs to the function are constant, it's enough to call the function only once instead + * of for every index. */ + if (evaluate_as_one(fn, param_variable_states, mask, variable_states.full_mask())) { + MFParamsBuilder params(fn, 1); + + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + VariableState *variable_state = param_variable_states[param_index]; + if (variable_state == nullptr) { + params.add_ignored_single_output(); + } + else { + variable_states.add_as_param__one(*variable_state, params, param_type, mask); + } + } + + fn.call(IndexRange(1), params, context); + } + else { + MFParamsBuilder params(fn, &mask); + + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + VariableState *variable_state = param_variable_states[param_index]; + if (variable_state == nullptr) { + params.add_ignored_single_output(); + } + else { + variable_states.add_as_param(*variable_state, params, param_type, mask); + } + } + + fn.call(mask, params, context); + } +} + +/** An index mask, that might own the indices if necessary. */ +struct InstructionIndices { + bool is_owned; + Vector<int64_t> owned_indices; + IndexMask referenced_indices; + + IndexMask mask() const + { + if (this->is_owned) { + return this->owned_indices.as_span(); + } + return this->referenced_indices; + } +}; + +/** Contains information about the next instruction that should be executed. */ +struct NextInstructionInfo { + const MFInstruction *instruction = nullptr; + InstructionIndices indices; + + IndexMask mask() const + { + return this->indices.mask(); + } + + operator bool() const + { + return this->instruction != nullptr; + } +}; + +/** + * Keeps track of the next instruction for all indices and decides in which order instructions are + * evaluated. + */ +class InstructionScheduler { + private: + Map<const MFInstruction *, Vector<InstructionIndices>> indices_by_instruction_; + + public: + InstructionScheduler() = default; + + void add_referenced_indices(const MFInstruction &instruction, IndexMask mask) + { + if (mask.is_empty()) { + return; + } + InstructionIndices new_indices; + new_indices.is_owned = false; + new_indices.referenced_indices = mask; + indices_by_instruction_.lookup_or_add_default(&instruction).append(std::move(new_indices)); + } + + void add_owned_indices(const MFInstruction &instruction, Vector<int64_t> indices) + { + if (indices.is_empty()) { + return; + } + BLI_assert(IndexMask::indices_are_valid_index_mask(indices)); + + InstructionIndices new_indices; + new_indices.is_owned = true; + new_indices.owned_indices = std::move(indices); + indices_by_instruction_.lookup_or_add_default(&instruction).append(std::move(new_indices)); + } + + void add_previous_instruction_indices(const MFInstruction &instruction, + NextInstructionInfo &instr_info) + { + indices_by_instruction_.lookup_or_add_default(&instruction) + .append(std::move(instr_info.indices)); + } + + NextInstructionInfo pop_next() + { + if (indices_by_instruction_.is_empty()) { + return {}; + } + /* TODO: Implement better mechanism to determine next instruction. */ + const MFInstruction *instruction = *indices_by_instruction_.keys().begin(); + + NextInstructionInfo next_instruction_info; + next_instruction_info.instruction = instruction; + next_instruction_info.indices = this->pop_indices_array(instruction); + return next_instruction_info; + } + + private: + InstructionIndices pop_indices_array(const MFInstruction *instruction) + { + Vector<InstructionIndices> *indices = indices_by_instruction_.lookup_ptr(instruction); + if (indices == nullptr) { + return {}; + } + InstructionIndices r_indices = (*indices).pop_last(); + BLI_assert(!r_indices.mask().is_empty()); + if (indices->is_empty()) { + indices_by_instruction_.remove_contained(instruction); + } + return r_indices; + } +}; + +void MFProcedureExecutor::call(IndexMask full_mask, MFParams params, MFContext context) const +{ + BLI_assert(procedure_.validate()); + + LinearAllocator<> allocator; + + VariableStates variable_states{full_mask}; + variable_states.add_initial_variable_states(*this, procedure_, params); + + InstructionScheduler scheduler; + scheduler.add_referenced_indices(*procedure_.entry(), full_mask); + + /* Loop until all indices got to a return instruction. */ + while (NextInstructionInfo instr_info = scheduler.pop_next()) { + const MFInstruction &instruction = *instr_info.instruction; + switch (instruction.type()) { + case MFInstructionType::Call: { + const MFCallInstruction &call_instruction = static_cast<const MFCallInstruction &>( + instruction); + execute_call_instruction(call_instruction, instr_info.mask(), variable_states, context); + scheduler.add_previous_instruction_indices(*call_instruction.next(), instr_info); + break; + } + case MFInstructionType::Branch: { + const MFBranchInstruction &branch_instruction = static_cast<const MFBranchInstruction &>( + instruction); + const MFVariable *condition_var = branch_instruction.condition(); + VariableState &variable_state = variable_states.get_variable_state(*condition_var); + + IndicesSplitVectors new_indices; + variable_state.indices_split(instr_info.mask(), new_indices); + scheduler.add_owned_indices(*branch_instruction.branch_false(), new_indices[false]); + scheduler.add_owned_indices(*branch_instruction.branch_true(), new_indices[true]); + break; + } + case MFInstructionType::Destruct: { + const MFDestructInstruction &destruct_instruction = + static_cast<const MFDestructInstruction &>(instruction); + const MFVariable *variable = destruct_instruction.variable(); + variable_states.destruct(*variable, instr_info.mask()); + scheduler.add_previous_instruction_indices(*destruct_instruction.next(), instr_info); + break; + } + case MFInstructionType::Dummy: { + const MFDummyInstruction &dummy_instruction = static_cast<const MFDummyInstruction &>( + instruction); + scheduler.add_previous_instruction_indices(*dummy_instruction.next(), instr_info); + break; + } + case MFInstructionType::Return: { + /* Don't insert the indices back into the scheduler. */ + break; + } + } + } + + for (const int param_index : this->param_indices()) { + const MFParamType param_type = this->param_type(param_index); + const MFVariable *variable = procedure_.params()[param_index].variable; + VariableState &variable_state = variable_states.get_variable_state(*variable); + switch (param_type.interface_type()) { + case MFParamType::Input: { + /* Input variables must be destructed in the end. */ + BLI_assert(variable_state.is_fully_uninitialized(full_mask)); + break; + } + case MFParamType::Mutable: + case MFParamType::Output: { + /* Mutable and output variables must be initialized in the end. */ + BLI_assert(variable_state.is_fully_initialized(full_mask)); + /* Make sure that the data is in the memory provided by the caller. */ + variable_state.ensure_is_mutable( + full_mask, param_type.data_type(), variable_states.value_allocator()); + break; + } + } + } +} + +} // namespace blender::fn diff --git a/source/blender/functions/tests/FN_field_test.cc b/source/blender/functions/tests/FN_field_test.cc new file mode 100644 index 00000000000..041cdbd0f00 --- /dev/null +++ b/source/blender/functions/tests/FN_field_test.cc @@ -0,0 +1,294 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_cpp_type.hh" +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_test_common.hh" + +namespace blender::fn::tests { + +TEST(field, ConstantFunction) +{ + /* TODO: Figure out how to not use another "FieldOperation(" inside of std::make_shared. */ + GField constant_field{std::make_shared<FieldOperation>( + FieldOperation(std::make_unique<CustomMF_Constant<int>>(10), {})), + 0}; + + Array<int> result(4); + + FieldContext context; + FieldEvaluator evaluator{context, 4}; + evaluator.add_with_destination(constant_field, result.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result[0], 10); + EXPECT_EQ(result[1], 10); + EXPECT_EQ(result[2], 10); + EXPECT_EQ(result[3], 10); +} + +class IndexFieldInput final : public FieldInput { + public: + IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index") + { + } + + const GVArray *get_varray_for_context(const FieldContext &UNUSED(context), + IndexMask mask, + ResourceScope &scope) const final + { + auto index_func = [](int i) { return i; }; + return &scope.construct< + GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>( + mask.min_array_size(), mask.min_array_size(), index_func); + } +}; + +TEST(field, VArrayInput) +{ + GField index_field{std::make_shared<IndexFieldInput>()}; + + Array<int> result_1(4); + + FieldContext context; + FieldEvaluator evaluator{context, 4}; + evaluator.add_with_destination(index_field, result_1.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result_1[0], 0); + EXPECT_EQ(result_1[1], 1); + EXPECT_EQ(result_1[2], 2); + EXPECT_EQ(result_1[3], 3); + + /* Evaluate a second time, just to test that the first didn't break anything. */ + Array<int> result_2(10); + + const Array<int64_t> indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldEvaluator evaluator_2{context, &mask}; + evaluator_2.add_with_destination(index_field, result_2.as_mutable_span()); + evaluator_2.evaluate(); + EXPECT_EQ(result_2[2], 2); + EXPECT_EQ(result_2[4], 4); + EXPECT_EQ(result_2[6], 6); + EXPECT_EQ(result_2[8], 8); +} + +TEST(field, VArrayInputMultipleOutputs) +{ + std::shared_ptr<FieldInput> index_input = std::make_shared<IndexFieldInput>(); + GField field_1{index_input}; + GField field_2{index_input}; + + Array<int> result_1(10); + Array<int> result_2(10); + + const Array<int64_t> indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldContext context; + FieldEvaluator evaluator{context, &mask}; + evaluator.add_with_destination(field_1, result_1.as_mutable_span()); + evaluator.add_with_destination(field_2, result_2.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result_1[2], 2); + EXPECT_EQ(result_1[4], 4); + EXPECT_EQ(result_1[6], 6); + EXPECT_EQ(result_1[8], 8); + EXPECT_EQ(result_2[2], 2); + EXPECT_EQ(result_2[4], 4); + EXPECT_EQ(result_2[6], 6); + EXPECT_EQ(result_2[8], 8); +} + +TEST(field, InputAndFunction) +{ + GField index_field{std::make_shared<IndexFieldInput>()}; + + std::unique_ptr<MultiFunction> add_fn = std::make_unique<CustomMF_SI_SI_SO<int, int, int>>( + "add", [](int a, int b) { return a + b; }); + GField output_field{std::make_shared<FieldOperation>( + FieldOperation(std::move(add_fn), {index_field, index_field})), + 0}; + + Array<int> result(10); + + const Array<int64_t> indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldContext context; + FieldEvaluator evaluator{context, &mask}; + evaluator.add_with_destination(output_field, result.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result[2], 4); + EXPECT_EQ(result[4], 8); + EXPECT_EQ(result[6], 12); + EXPECT_EQ(result[8], 16); +} + +TEST(field, TwoFunctions) +{ + GField index_field{std::make_shared<IndexFieldInput>()}; + + std::unique_ptr<MultiFunction> add_fn = std::make_unique<CustomMF_SI_SI_SO<int, int, int>>( + "add", [](int a, int b) { return a + b; }); + GField add_field{std::make_shared<FieldOperation>( + FieldOperation(std::move(add_fn), {index_field, index_field})), + 0}; + + std::unique_ptr<MultiFunction> add_10_fn = std::make_unique<CustomMF_SI_SO<int, int>>( + "add_10", [](int a) { return a + 10; }); + GField result_field{ + std::make_shared<FieldOperation>(FieldOperation(std::move(add_10_fn), {add_field})), 0}; + + Array<int> result(10); + + const Array<int64_t> indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldContext context; + FieldEvaluator evaluator{context, &mask}; + evaluator.add_with_destination(result_field, result.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result[2], 14); + EXPECT_EQ(result[4], 18); + EXPECT_EQ(result[6], 22); + EXPECT_EQ(result[8], 26); +} + +class TwoOutputFunction : public MultiFunction { + private: + MFSignature signature_; + + public: + TwoOutputFunction(StringRef name) + { + MFSignatureBuilder signature{name}; + signature.single_input<int>("In1"); + signature.single_input<int>("In2"); + signature.single_output<int>("Add"); + signature.single_output<int>("Add10"); + signature_ = signature.build(); + this->set_signature(&signature_); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray<int> &in1 = params.readonly_single_input<int>(0, "In1"); + const VArray<int> &in2 = params.readonly_single_input<int>(1, "In2"); + MutableSpan<int> add = params.uninitialized_single_output<int>(2, "Add"); + MutableSpan<int> add_10 = params.uninitialized_single_output<int>(3, "Add10"); + mask.foreach_index([&](const int64_t i) { + add[i] = in1[i] + in2[i]; + add_10[i] = add[i] + 10; + }); + } +}; + +TEST(field, FunctionTwoOutputs) +{ + /* Also use two separate input fields, why not. */ + GField index_field_1{std::make_shared<IndexFieldInput>()}; + GField index_field_2{std::make_shared<IndexFieldInput>()}; + + std::shared_ptr<FieldOperation> fn = std::make_shared<FieldOperation>(FieldOperation( + std::make_unique<TwoOutputFunction>("SI_SI_SO_SO"), {index_field_1, index_field_2})); + + GField result_field_1{fn, 0}; + GField result_field_2{fn, 1}; + + Array<int> result_1(10); + Array<int> result_2(10); + + const Array<int64_t> indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldContext context; + FieldEvaluator evaluator{context, &mask}; + evaluator.add_with_destination(result_field_1, result_1.as_mutable_span()); + evaluator.add_with_destination(result_field_2, result_2.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result_1[2], 4); + EXPECT_EQ(result_1[4], 8); + EXPECT_EQ(result_1[6], 12); + EXPECT_EQ(result_1[8], 16); + EXPECT_EQ(result_2[2], 14); + EXPECT_EQ(result_2[4], 18); + EXPECT_EQ(result_2[6], 22); + EXPECT_EQ(result_2[8], 26); +} + +TEST(field, TwoFunctionsTwoOutputs) +{ + GField index_field{std::make_shared<IndexFieldInput>()}; + + std::shared_ptr<FieldOperation> fn = std::make_shared<FieldOperation>(FieldOperation( + std::make_unique<TwoOutputFunction>("SI_SI_SO_SO"), {index_field, index_field})); + + Array<int64_t> mask_indices = {2, 4, 6, 8}; + IndexMask mask = mask_indices.as_span(); + + Field<int> result_field_1{fn, 0}; + Field<int> intermediate_field{fn, 1}; + + std::unique_ptr<MultiFunction> add_10_fn = std::make_unique<CustomMF_SI_SO<int, int>>( + "add_10", [](int a) { return a + 10; }); + Field<int> result_field_2{ + std::make_shared<FieldOperation>(FieldOperation(std::move(add_10_fn), {intermediate_field})), + 0}; + + FieldContext field_context; + FieldEvaluator field_evaluator{field_context, &mask}; + const VArray<int> *result_1 = nullptr; + const VArray<int> *result_2 = nullptr; + field_evaluator.add(result_field_1, &result_1); + field_evaluator.add(result_field_2, &result_2); + field_evaluator.evaluate(); + + EXPECT_EQ(result_1->get(2), 4); + EXPECT_EQ(result_1->get(4), 8); + EXPECT_EQ(result_1->get(6), 12); + EXPECT_EQ(result_1->get(8), 16); + EXPECT_EQ(result_2->get(2), 24); + EXPECT_EQ(result_2->get(4), 28); + EXPECT_EQ(result_2->get(6), 32); + EXPECT_EQ(result_2->get(8), 36); +} + +TEST(field, SameFieldTwice) +{ + GField constant_field{ + std::make_shared<FieldOperation>(std::make_unique<CustomMF_Constant<int>>(10)), 0}; + + FieldContext field_context; + IndexMask mask{IndexRange(2)}; + ResourceScope scope; + Vector<const GVArray *> results = evaluate_fields( + scope, {constant_field, constant_field}, mask, field_context); + + GVArray_Typed<int> varray1{*results[0]}; + GVArray_Typed<int> varray2{*results[1]}; + + EXPECT_EQ(varray1->get(0), 10); + EXPECT_EQ(varray1->get(1), 10); + EXPECT_EQ(varray2->get(0), 10); + EXPECT_EQ(varray2->get(1), 10); +} + +TEST(field, IgnoredOutput) +{ + static OptionalOutputsFunction fn; + Field<int> field{std::make_shared<FieldOperation>(fn), 0}; + + FieldContext field_context; + FieldEvaluator field_evaluator{field_context, 10}; + const VArray<int> *results = nullptr; + field_evaluator.add(field, &results); + field_evaluator.evaluate(); + + EXPECT_EQ(results->get(0), 5); + EXPECT_EQ(results->get(3), 5); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_procedure_test.cc b/source/blender/functions/tests/FN_multi_function_procedure_test.cc new file mode 100644 index 00000000000..0b4b88fba3d --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_procedure_test.cc @@ -0,0 +1,344 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_procedure_builder.hh" +#include "FN_multi_function_procedure_executor.hh" +#include "FN_multi_function_test_common.hh" + +namespace blender::fn::tests { + +TEST(multi_function_procedure, SimpleTest) +{ + /** + * procedure(int var1, int var2, int *var4) { + * int var3 = var1 + var2; + * var4 = var2 + var3; + * var4 += 10; + * } + */ + + CustomMF_SI_SI_SO<int, int, int> add_fn{"add", [](int a, int b) { return a + b; }}; + CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_input_parameter<int>(); + MFVariable *var2 = &builder.add_single_input_parameter<int>(); + auto [var3] = builder.add_call<1>(add_fn, {var1, var2}); + auto [var4] = builder.add_call<1>(add_fn, {var2, var3}); + builder.add_call(add_10_fn, {var4}); + builder.add_destruct({var1, var2, var3}); + builder.add_return(); + builder.add_output_parameter(*var4); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor executor{"My Procedure", procedure}; + + MFParamsBuilder params{executor, 3}; + MFContextBuilder context; + + Array<int> input_array = {1, 2, 3}; + params.add_readonly_single_input(input_array.as_span()); + params.add_readonly_single_input_value(3); + + Array<int> output_array(3); + params.add_uninitialized_single_output(output_array.as_mutable_span()); + + executor.call(IndexRange(3), params, context); + + EXPECT_EQ(output_array[0], 17); + EXPECT_EQ(output_array[1], 18); + EXPECT_EQ(output_array[2], 19); +} + +TEST(multi_function_procedure, BranchTest) +{ + /** + * procedure(int &var1, bool var2) { + * if (var2) { + * var1 += 100; + * } + * else { + * var1 += 10; + * } + * var1 += 10; + * } + */ + + CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }}; + CustomMF_SM<int> add_100_fn{"add_100", [](int &a) { a += 100; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_mutable_parameter<int>(); + MFVariable *var2 = &builder.add_single_input_parameter<bool>(); + + MFProcedureBuilder::Branch branch = builder.add_branch(*var2); + branch.branch_false.add_call(add_10_fn, {var1}); + branch.branch_true.add_call(add_100_fn, {var1}); + builder.set_cursor_after_branch(branch); + builder.add_call(add_10_fn, {var1}); + builder.add_destruct({var2}); + builder.add_return(); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{"Condition Test", procedure}; + MFParamsBuilder params(procedure_fn, 5); + + Array<int> values_a = {1, 5, 3, 6, 2}; + Array<bool> values_cond = {true, false, true, true, false}; + + params.add_single_mutable(values_a.as_mutable_span()); + params.add_readonly_single_input(values_cond.as_span()); + + MFContextBuilder context; + procedure_fn.call({1, 2, 3, 4}, params, context); + + EXPECT_EQ(values_a[0], 1); + EXPECT_EQ(values_a[1], 25); + EXPECT_EQ(values_a[2], 113); + EXPECT_EQ(values_a[3], 116); + EXPECT_EQ(values_a[4], 22); +} + +TEST(multi_function_procedure, EvaluateOne) +{ + /** + * procedure(int var1, int *var2) { + * var2 = var1 + 10; + * } + */ + + int tot_evaluations = 0; + CustomMF_SI_SO<int, int> add_10_fn{"add_10", [&](int a) { + tot_evaluations++; + return a + 10; + }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_input_parameter<int>(); + auto [var2] = builder.add_call<1>(add_10_fn, {var1}); + builder.add_destruct(*var1); + builder.add_return(); + builder.add_output_parameter(*var2); + + MFProcedureExecutor procedure_fn{"Evaluate One", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array<int> values_out = {1, 2, 3, 4, 5}; + params.add_readonly_single_input_value(1); + params.add_uninitialized_single_output(values_out.as_mutable_span()); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(values_out[0], 11); + EXPECT_EQ(values_out[1], 11); + EXPECT_EQ(values_out[2], 3); + EXPECT_EQ(values_out[3], 11); + EXPECT_EQ(values_out[4], 11); + /* We expect only one evaluation, because the input is constant. */ + EXPECT_EQ(tot_evaluations, 1); +} + +TEST(multi_function_procedure, SimpleLoop) +{ + /** + * procedure(int count, int *out) { + * out = 1; + * int index = 0' + * loop { + * if (index >= count) { + * break; + * } + * out *= 2; + * index += 1; + * } + * out += 1000; + * } + */ + + CustomMF_Constant<int> const_1_fn{1}; + CustomMF_Constant<int> const_0_fn{0}; + CustomMF_SI_SI_SO<int, int, bool> greater_or_equal_fn{"greater or equal", + [](int a, int b) { return a >= b; }}; + CustomMF_SM<int> double_fn{"double", [](int &a) { a *= 2; }}; + CustomMF_SM<int> add_1000_fn{"add 1000", [](int &a) { a += 1000; }}; + CustomMF_SM<int> add_1_fn{"add 1", [](int &a) { a += 1; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var_count = &builder.add_single_input_parameter<int>("count"); + auto [var_out] = builder.add_call<1>(const_1_fn); + var_out->set_name("out"); + auto [var_index] = builder.add_call<1>(const_0_fn); + var_index->set_name("index"); + + MFProcedureBuilder::Loop loop = builder.add_loop(); + auto [var_condition] = builder.add_call<1>(greater_or_equal_fn, {var_index, var_count}); + var_condition->set_name("condition"); + MFProcedureBuilder::Branch branch = builder.add_branch(*var_condition); + branch.branch_true.add_destruct(*var_condition); + branch.branch_true.add_loop_break(loop); + branch.branch_false.add_destruct(*var_condition); + builder.set_cursor_after_branch(branch); + builder.add_call(double_fn, {var_out}); + builder.add_call(add_1_fn, {var_index}); + builder.add_loop_continue(loop); + builder.set_cursor_after_loop(loop); + builder.add_call(add_1000_fn, {var_out}); + builder.add_destruct({var_count, var_index}); + builder.add_return(); + builder.add_output_parameter(*var_out); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{"Simple Loop", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array<int> counts = {4, 3, 7, 6, 4}; + Array<int> results(5, -1); + + params.add_readonly_single_input(counts.as_span()); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(results[0], 1016); + EXPECT_EQ(results[1], 1008); + EXPECT_EQ(results[2], -1); + EXPECT_EQ(results[3], 1064); + EXPECT_EQ(results[4], 1016); +} + +TEST(multi_function_procedure, Vectors) +{ + /** + * procedure(vector<int> v1, vector<int> &v2, vector<int> *v3) { + * v1.extend(v2); + * int constant = 5; + * v2.append(constant); + * v2.extend(v1); + * int len = sum(v2); + * v3 = range(len); + * } + */ + + CreateRangeFunction create_range_fn; + ConcatVectorsFunction extend_fn; + GenericAppendFunction append_fn{CPPType::get<int>()}; + SumVectorFunction sum_elements_fn; + CustomMF_Constant<int> constant_5_fn{5}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var_v1 = &builder.add_input_parameter(MFDataType::ForVector<int>()); + MFVariable *var_v2 = &builder.add_parameter(MFParamType::ForMutableVector(CPPType::get<int>())); + builder.add_call(extend_fn, {var_v1, var_v2}); + auto [var_constant] = builder.add_call<1>(constant_5_fn); + builder.add_call(append_fn, {var_v2, var_constant}); + builder.add_destruct(*var_constant); + builder.add_call(extend_fn, {var_v2, var_v1}); + auto [var_len] = builder.add_call<1>(sum_elements_fn, {var_v2}); + auto [var_v3] = builder.add_call<1>(create_range_fn, {var_len}); + builder.add_destruct({var_v1, var_len}); + builder.add_return(); + builder.add_output_parameter(*var_v3); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{"Vectors", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array<int> v1 = {5, 2, 3}; + GVectorArray v2{CPPType::get<int>(), 5}; + GVectorArray v3{CPPType::get<int>(), 5}; + + int value_10 = 10; + v2.append(0, &value_10); + v2.append(4, &value_10); + + params.add_readonly_vector_input(v1.as_span()); + params.add_vector_mutable(v2); + params.add_vector_output(v3); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(v2[0].size(), 6); + EXPECT_EQ(v2[1].size(), 4); + EXPECT_EQ(v2[2].size(), 0); + EXPECT_EQ(v2[3].size(), 4); + EXPECT_EQ(v2[4].size(), 6); + + EXPECT_EQ(v3[0].size(), 35); + EXPECT_EQ(v3[1].size(), 15); + EXPECT_EQ(v3[2].size(), 0); + EXPECT_EQ(v3[3].size(), 15); + EXPECT_EQ(v3[4].size(), 35); +} + +TEST(multi_function_procedure, BufferReuse) +{ + /** + * procedure(int a, int *out) { + * int b = a + 10; + * int c = c + 10; + * int d = d + 10; + * int e = d + 10; + * out = e + 10; + * } + */ + + CustomMF_SI_SO<int, int> add_10_fn{"add 10", [](int a) { return a + 10; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var_a = &builder.add_single_input_parameter<int>(); + auto [var_b] = builder.add_call<1>(add_10_fn, {var_a}); + builder.add_destruct(*var_a); + auto [var_c] = builder.add_call<1>(add_10_fn, {var_b}); + builder.add_destruct(*var_b); + auto [var_d] = builder.add_call<1>(add_10_fn, {var_c}); + builder.add_destruct(*var_c); + auto [var_e] = builder.add_call<1>(add_10_fn, {var_d}); + builder.add_destruct(*var_d); + auto [var_out] = builder.add_call<1>(add_10_fn, {var_e}); + builder.add_destruct(*var_e); + builder.add_return(); + builder.add_output_parameter(*var_out); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{"Buffer Reuse", procedure}; + + Array<int> inputs = {4, 1, 6, 2, 3}; + Array<int> results(5, -1); + + MFParamsBuilder params{procedure_fn, 5}; + params.add_readonly_single_input(inputs.as_span()); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + procedure_fn.call({0, 2, 3, 4}, params, context); + + EXPECT_EQ(results[0], 54); + EXPECT_EQ(results[1], -1); + EXPECT_EQ(results[2], 56); + EXPECT_EQ(results[3], 52); + EXPECT_EQ(results[4], 53); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 91c72a51dd6..d99993b35ac 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -263,7 +263,7 @@ TEST(multi_function, CustomMF_Constant) TEST(multi_function, CustomMF_GenericConstant) { int value = 42; - CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value}; + CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value, false}; EXPECT_EQ(fn.param_name(0), "42"); Array<int> outputs(4, 0); @@ -328,5 +328,32 @@ TEST(multi_function, CustomMF_Convert) EXPECT_EQ(outputs[2], 9); } +TEST(multi_function, IgnoredOutputs) +{ + OptionalOutputsFunction fn; + { + MFParamsBuilder params(fn, 10); + params.add_ignored_single_output("Out 1"); + params.add_ignored_single_output("Out 2"); + MFContextBuilder context; + fn.call(IndexRange(10), params, context); + } + { + Array<int> results_1(10); + Array<std::string> results_2(10, NoInitialization()); + + MFParamsBuilder params(fn, 10); + params.add_uninitialized_single_output(results_1.as_mutable_span(), "Out 1"); + params.add_uninitialized_single_output(results_2.as_mutable_span(), "Out 2"); + MFContextBuilder context; + fn.call(IndexRange(10), params, context); + + EXPECT_EQ(results_1[0], 5); + EXPECT_EQ(results_1[3], 5); + EXPECT_EQ(results_1[9], 5); + EXPECT_EQ(results_2[0], "hello, this is a long string"); + } +} + } // namespace } // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh index 51c8fac8a96..2a64cc302fe 100644 --- a/source/blender/functions/tests/FN_multi_function_test_common.hh +++ b/source/blender/functions/tests/FN_multi_function_test_common.hh @@ -171,4 +171,33 @@ class SumVectorFunction : public MultiFunction { } }; +class OptionalOutputsFunction : public MultiFunction { + public: + OptionalOutputsFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Optional Outputs"}; + signature.single_output<int>("Out 1"); + signature.single_output<std::string>("Out 2"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + if (params.single_output_is_required(0, "Out 1")) { + MutableSpan<int> values = params.uninitialized_single_output<int>(0, "Out 1"); + values.fill_indices(mask, 5); + } + MutableSpan<std::string> values = params.uninitialized_single_output<std::string>(1, "Out 2"); + for (const int i : mask) { + new (&values[i]) std::string("hello, this is a long string"); + } + } +}; + } // namespace blender::fn::tests diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index ec965c9a29f..adf68e534bb 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC intern/MOD_gpencilarray.c intern/MOD_gpencilbuild.c intern/MOD_gpencilcolor.c + intern/MOD_gpencildash.c intern/MOD_gpencilhook.c intern/MOD_gpencillattice.c intern/MOD_gpencillength.c diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h index 18310bd5dff..043186155b7 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -46,6 +46,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply; extern GpencilModifierTypeInfo modifierType_Gpencil_Texture; extern GpencilModifierTypeInfo modifierType_Gpencil_Weight; extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart; +extern GpencilModifierTypeInfo modifierType_Gpencil_Dash; /* MOD_gpencil_util.c */ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 6409c86b6e3..5eb1eeab780 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -65,6 +65,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) INIT_GP_TYPE(Texture); INIT_GP_TYPE(Weight); INIT_GP_TYPE(Lineart); + INIT_GP_TYPE(Dash); #undef INIT_GP_TYPE } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c new file mode 100644 index 00000000000..ba33edd6a94 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -0,0 +1,387 @@ +/* + * 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) 2021, Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup modifiers + */ + +#include <stdio.h> +#include <string.h> + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_defaults.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" + +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_lib_query.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_screen.h" + +#include "MEM_guardedalloc.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" + +#include "BLT_translation.h" + +#include "MOD_gpencil_modifiertypes.h" +#include "MOD_gpencil_ui_common.h" +#include "MOD_gpencil_util.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" + +static void initData(GpencilModifierData *md) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(dmd, modifier)); + + MEMCPY_STRUCT_AFTER(dmd, DNA_struct_default_get(DashGpencilModifierData), modifier); + + DashGpencilModifierSegment *ds = DNA_struct_default_alloc(DashGpencilModifierSegment); + ds->dmd = dmd; + BLI_strncpy(ds->name, DATA_("Segment"), sizeof(ds->name)); + + dmd->segments = ds; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)target; + const DashGpencilModifierData *dmd_src = (const DashGpencilModifierData *)md; + + BKE_gpencil_modifier_copydata_generic(md, target); + + dmd->segments = MEM_dupallocN(dmd_src->segments); +} + +static void freeData(GpencilModifierData *md) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)md; + + MEM_SAFE_FREE(dmd->segments); +} + +/** + * Gap==0 means to start the next segment at the immediate next point, which will leave a visual + * gap of "1 point". This makes the algorithm give the same visual appearance as displayed on the + * UI and also simplifies the check for "no-length" situation where SEG==0 (which will not produce + * any effective dash). + */ +static int real_gap(const DashGpencilModifierSegment *ds) +{ + return ds->gap - 1; +} + +static bool stroke_dash(const bGPDstroke *gps, + const DashGpencilModifierData *dmd, + ListBase *r_strokes) +{ + int new_stroke_offset = 0; + int trim_start = 0; + + for (int i = 0; i < dmd->segments_len; i++) { + if (dmd->segments[i].dash + real_gap(&dmd->segments[i]) < 1) { + BLI_assert_unreachable(); + /* This means there's a part that doesn't have any length, can't do dot-dash. */ + return false; + } + } + + const DashGpencilModifierSegment *const first_segment = &dmd->segments[0]; + const DashGpencilModifierSegment *const last_segment = &dmd->segments[dmd->segments_len - 1]; + const DashGpencilModifierSegment *ds = first_segment; + + /* Determine starting configuration using offset. */ + int offset_trim = dmd->dash_offset; + while (offset_trim < 0) { + ds = (ds == first_segment) ? last_segment : ds - 1; + offset_trim += ds->dash + real_gap(ds); + } + + /* This segment is completely removed from view by the index offset, ignore it. */ + while (ds->dash + real_gap(ds) < offset_trim) { + offset_trim -= ds->dash + real_gap(ds); + ds = (ds == last_segment) ? first_segment : ds + 1; + } + + /* This segment is partially visible at the beginning of the stroke. */ + if (ds->dash > offset_trim) { + trim_start = offset_trim; + } + else { + /* This segment is not visible but the gap immediately after this segment is partially visible, + * use next segment's dash. */ + new_stroke_offset += ds->dash + real_gap(ds) - offset_trim; + ds = (ds == last_segment) ? first_segment : ds + 1; + } + + while (new_stroke_offset < gps->totpoints - 1) { + const int seg = ds->dash - trim_start; + if (!(seg || real_gap(ds))) { + ds = (ds == last_segment) ? first_segment : ds + 1; + continue; + } + + const int size = MIN2(gps->totpoints - new_stroke_offset, seg); + if (size == 0) { + continue; + } + + bGPDstroke *stroke = BKE_gpencil_stroke_new( + ds->mat_nr < 0 ? gps->mat_nr : ds->mat_nr, size, gps->thickness); + + for (int is = 0; is < size; is++) { + bGPDspoint *p = &gps->points[new_stroke_offset + is]; + stroke->points[is].x = p->x; + stroke->points[is].y = p->y; + stroke->points[is].z = p->z; + stroke->points[is].pressure = p->pressure * ds->radius; + stroke->points[is].strength = p->strength * ds->opacity; + } + BLI_addtail(r_strokes, stroke); + + if (gps->dvert) { + BKE_gpencil_dvert_ensure(stroke); + for (int di = 0; di < stroke->totpoints; di++) { + MDeformVert *dv = &gps->dvert[new_stroke_offset + di]; + if (dv && dv->totweight && dv->dw) { + MDeformWeight *dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + __func__); + memcpy(dw, dv->dw, sizeof(MDeformWeight) * dv->totweight); + stroke->dvert[di].dw = dw; + stroke->dvert[di].totweight = dv->totweight; + stroke->dvert[di].flag = dv->flag; + } + } + } + + new_stroke_offset += seg + real_gap(ds); + ds = (ds == last_segment) ? first_segment : ds + 1; + trim_start = 0; + } + + return true; +} + +static void apply_dash_for_frame( + Object *ob, bGPDlayer *gpl, bGPdata *gpd, bGPDframe *gpf, DashGpencilModifierData *dmd) +{ + if (dmd->segments_len == 0) { + return; + } + + ListBase result = {NULL, NULL}; + + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + if (is_stroke_affected_by_modifier(ob, + dmd->layername, + dmd->material, + dmd->pass_index, + dmd->layer_pass, + 1, + gpl, + gps, + dmd->flag & GP_LENGTH_INVERT_LAYER, + dmd->flag & GP_LENGTH_INVERT_PASS, + dmd->flag & GP_LENGTH_INVERT_LAYERPASS, + dmd->flag & GP_LENGTH_INVERT_MATERIAL)) { + stroke_dash(gps, dmd, &result); + BLI_remlink(&gpf->strokes, gps); + BKE_gpencil_free_stroke(gps); + } + } + bGPDstroke *gps_dash; + while ((gps_dash = BLI_pophead(&result))) { + BLI_addtail(&gpf->strokes, gps_dash); + BKE_gpencil_stroke_geometry_update(gpd, gps_dash); + } +} + +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *UNUSED(depsgraph), + GpencilModifierData *md, + Object *ob) +{ + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md); + } + } +} + +/* -------------------------------- */ + +/* Generic "generateStrokes" callback */ +static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) +{ + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + BKE_gpencil_frame_active_set(depsgraph, gpd); + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) { + return; + } + apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md); + } +} + +static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + DashGpencilModifierData *mmd = (DashGpencilModifierData *)md; + + walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); +} + +static void segment_list_item(struct uiList *UNUSED(ui_list), + struct bContext *UNUSED(C), + struct uiLayout *layout, + struct PointerRNA *UNUSED(idataptr), + struct PointerRNA *itemptr, + int UNUSED(icon), + struct PointerRNA *UNUSED(active_dataptr), + const char *UNUSED(active_propname), + int UNUSED(index), + int UNUSED(flt_flag)) +{ + uiLayout *row = uiLayoutRow(layout, true); + uiItemR(row, itemptr, "name", UI_ITEM_R_NO_BG, "", ICON_NONE); +} + +static void panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "dash_offset", 0, NULL, ICON_NONE); + + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetPropSep(row, false); + + uiTemplateList(row, + (bContext *)C, + "MOD_UL_dash_segment", + "", + ptr, + "segments", + ptr, + "segment_active_index", + NULL, + 3, + 10, + 0, + 1, + UI_TEMPLATE_LIST_FLAG_NONE); + + uiLayout *col = uiLayoutColumn(row, false); + uiLayoutSetContextPointer(col, "modifier", ptr); + + uiLayout *sub = uiLayoutColumn(col, true); + uiItemO(sub, "", ICON_ADD, "GPENCIL_OT_segment_add"); + uiItemO(sub, "", ICON_REMOVE, "GPENCIL_OT_segment_remove"); + uiItemS(col); + sub = uiLayoutColumn(col, true); + uiItemEnumO_string(sub, "", ICON_TRIA_UP, "GPENCIL_OT_segment_move", "type", "UP"); + uiItemEnumO_string(sub, "", ICON_TRIA_DOWN, "GPENCIL_OT_segment_move", "type", "DOWN"); + + DashGpencilModifierData *dmd = ptr->data; + + if (dmd->segment_active_index >= 0 && dmd->segment_active_index < dmd->segments_len) { + PointerRNA ds_ptr; + RNA_pointer_create(ptr->owner_id, + &RNA_DashGpencilModifierSegment, + &dmd->segments[dmd->segment_active_index], + &ds_ptr); + + sub = uiLayoutColumn(layout, true); + uiItemR(sub, &ds_ptr, "dash", 0, NULL, ICON_NONE); + uiItemR(sub, &ds_ptr, "gap", 0, NULL, ICON_NONE); + + sub = uiLayoutColumn(layout, false); + uiItemR(sub, &ds_ptr, "radius", 0, NULL, ICON_NONE); + uiItemR(sub, &ds_ptr, "opacity", 0, NULL, ICON_NONE); + uiItemR(sub, &ds_ptr, "material_index", 0, NULL, ICON_NONE); + } + + gpencil_modifier_panel_end(layout, ptr); +} + +static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + gpencil_modifier_masking_panel_draw(panel, true, false); +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = gpencil_modifier_panel_register( + region_type, eGpencilModifierType_Dash, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); + + uiListType *list_type = MEM_callocN(sizeof(uiListType), "dash modifier segment uilist"); + strcpy(list_type->idname, "MOD_UL_dash_segment"); + list_type->draw_item = segment_list_item; + WM_uilisttype_add(list_type); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Dash = { + /* name */ "Dot Dash", + /* structName */ "DashGpencilModifierData", + /* structSize */ sizeof(DashGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ NULL, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ bakeModifier, + /* remapTime */ NULL, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, + /* panelRegister */ panelRegister, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index 857c683d95a..6aa0e6c152e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -108,27 +108,6 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke } } -static void bakeModifier(Main *UNUSED(bmain), - Depsgraph *UNUSED(depsgraph), - GpencilModifierData *md, - Object *ob) -{ - - bGPdata *gpd = ob->data; - - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - applyLength(lmd, gpd, gps); - } - } - } -} - -/* -------------------------------- */ - -/* Generic "generateStrokes" callback */ static void deformStroke(GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), Object *ob, @@ -154,6 +133,23 @@ static void deformStroke(GpencilModifierData *md, } } +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) +{ + + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + deformStroke(md, depsgraph, ob, gpl, gpf, gps); + } + } + } +} + static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { LengthGpencilModifierData *mmd = (LengthGpencilModifierData *)md; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 73ca4b9c529..01488a8b2de 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -390,6 +390,8 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE); + uiItemR(layout, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); } static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index 9e9eba3d61e..ae862ce3eda 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -324,7 +324,7 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); - uiLayoutSetActive(layout, RNA_boolean_get(ptr, "random")); + uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_random")); uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c index 4142a1c02dc..b00db1ba2d2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c @@ -132,7 +132,7 @@ static void deformStroke(GpencilModifierData *md, const float val = mmd->factor * weight; /* perform smoothing */ if (mmd->flag & GP_SMOOTH_MOD_LOCATION) { - BKE_gpencil_stroke_smooth(gps, i, val); + BKE_gpencil_stroke_smooth_point(gps, i, val); } if (mmd->flag & GP_SMOOTH_MOD_STRENGTH) { BKE_gpencil_stroke_smooth_strength(gps, i, val); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c index f444103ae3f..ee5ba7e92ca 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c @@ -76,16 +76,12 @@ static void deformStroke(GpencilModifierData *md, SubdivGpencilModifierData *mmd = (SubdivGpencilModifierData *)md; bGPdata *gpd = ob->data; - /* It makes sense when adding points to a straight line */ - /* e.g. for creating thickness variation in later modifiers. */ - const int minimum_vert = (mmd->flag & GP_SUBDIV_SIMPLE) ? 2 : 3; - if (!is_stroke_affected_by_modifier(ob, mmd->layername, mmd->material, mmd->pass_index, mmd->layer_pass, - minimum_vert, + 2, gpl, gps, mmd->flag & GP_SUBDIV_INVERT_LAYER, @@ -95,7 +91,10 @@ static void deformStroke(GpencilModifierData *md, return; } - BKE_gpencil_stroke_subdivide(gpd, gps, mmd->level, mmd->type); + /* For strokes with less than 3 points, only the Simple Subdivision makes sense. */ + short type = gps->totpoints < 3 ? GP_SUBDIV_SIMPLE : mmd->type; + + BKE_gpencil_stroke_subdivide(gpd, gps, mmd->level, type); /* If the stroke is cyclic, must generate the closing geometry. */ if (gps->flag & GP_STROKE_CYCLIC) { diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 4b71011b99a..134d9707ade 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -304,6 +304,9 @@ typedef struct LineartRenderBuffer { bool filter_face_mark_invert; bool filter_face_mark_boundaries; + bool force_crease; + bool sharp_as_crease; + /* Keep an copy of these data so when line art is running it's self-contained. */ bool cam_is_persp; float cam_obmat[4][4]; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index c71cde8ec43..725cc0741f0 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1453,7 +1453,7 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, LineartTriangle *rt_array, LineartVert *rv_array, float crease_threshold, - bool no_crease, + bool use_auto_smooth, bool use_freestyle_edge, bool use_freestyle_face, BMesh *bm_if_freestyle) @@ -1533,10 +1533,20 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } - if (rb->use_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { - if (!no_crease) { + if (rb->use_crease) { + if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; } + else { + bool do_crease = true; + if (!rb->force_crease && !use_auto_smooth && + (BM_elem_flag_test(ll->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(lr->f, BM_ELEM_SMOOTH))) { + do_crease = false; + } + if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { + edge_flag_result |= LRT_EDGE_FLAG_CREASE; + } + } } if (rb->use_material && (ll->f->mat_nr != lr->f->mat_nr)) { edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; @@ -1746,9 +1756,14 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu eln->object_ref = orig_ob; obi->v_eln = eln; + bool use_auto_smooth = false; if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold); } + if (obi->original_me->flag & ME_AUTOSMOOTH) { + use_crease = cosf(obi->original_me->smoothresh); + use_auto_smooth = true; + } else { use_crease = rb->crease_threshold; } @@ -1832,15 +1847,15 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu e = BM_edge_at_index(bm, i); /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ - char eflag = lineart_identify_feature_line(rb, - e, - ort, - orv, - use_crease, - orig_ob->type == OB_FONT, - can_find_freestyle_edge, - can_find_freestyle_face, - bm); + uint16_t eflag = lineart_identify_feature_line(rb, + e, + ort, + orv, + use_crease, + use_auto_smooth, + can_find_freestyle_edge, + can_find_freestyle_face, + bm); if (eflag) { /* Only allocate for feature lines (instead of all lines) to save memory. * If allow duplicated edges, one edge gets added multiple times if it has multiple types. */ @@ -3057,6 +3072,9 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->allow_duplicated_types = (lmd->calculation_flags & LRT_ALLOW_OVERLAP_EDGE_TYPES) != 0; + rb->force_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SMOOTH_SURFACES) != 0; + rb->sharp_as_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SHARP_EDGES) != 0; + int16_t edge_types = lmd->edge_types_override; rb->use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index f834ee5b234..62b748b7edf 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -54,7 +54,8 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, const char *fragcode, const char *geomcode, const char *libcode, - const char *defines); + const char *defines, + const char *name); GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, const char *geomcode, @@ -85,6 +86,8 @@ void GPU_shader_free(GPUShader *shader); void GPU_shader_bind(GPUShader *shader); void GPU_shader_unbind(void); +const char *GPU_shader_get_name(GPUShader *shader); + /* Returns true if transform feedback was successfully enabled. */ bool GPU_shader_transform_feedback_enable(GPUShader *shader, struct GPUVertBuf *vertbuf); void GPU_shader_transform_feedback_disable(GPUShader *shader); diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index 8b2a14a1a96..0ecd5f3eb7b 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -120,7 +120,7 @@ void GPU_viewport_free(GPUViewport *viewport); void GPU_viewport_colorspace_set(GPUViewport *viewport, ColorManagedViewSettings *view_settings, - ColorManagedDisplaySettings *display_settings, + const ColorManagedDisplaySettings *display_settings, float dither); void GPU_viewport_bind_from_offscreen(GPUViewport *viewport, struct GPUOffScreen *ofs); diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index c754a649924..9340d311472 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -229,7 +229,8 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, const char *fragcode, const char *geomcode, const char *libcode, - const char *defines) + const char *defines, + const char *name) { char *libcodecat = nullptr; @@ -240,6 +241,9 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); } + /* Use pyGPUShader as default name for shader. */ + const char *shname = name != nullptr ? name : "pyGPUShader"; + GPUShader *sh = GPU_shader_create_ex(vertcode, fragcode, geomcode, @@ -249,7 +253,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, GPU_SHADER_TFB_NONE, nullptr, 0, - "pyGPUShader"); + shname); MEM_SAFE_FREE(libcodecat); return sh; @@ -369,6 +373,17 @@ void GPU_shader_unbind(void) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Shader name + * \{ */ + +const char *GPU_shader_get_name(GPUShader *shader) +{ + return unwrap(shader)->name_get(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Transform feedback * * TODO(fclem): Should be replaced by compute shaders. diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 96bf1ec40b0..dd63edea0db 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -556,7 +556,7 @@ void GPU_viewport_bind_from_offscreen(GPUViewport *viewport, struct GPUOffScreen void GPU_viewport_colorspace_set(GPUViewport *viewport, ColorManagedViewSettings *view_settings, - ColorManagedDisplaySettings *display_settings, + const ColorManagedDisplaySettings *display_settings, float dither) { /** diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 772fc19d919..2855d5078ff 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -460,7 +460,7 @@ void GLBackend::capabilities_init() GCaps.mem_stats_support = GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo; GCaps.shader_image_load_store_support = GLEW_ARB_shader_image_load_store; - GCaps.compute_shader_support = GLEW_ARB_compute_shader; + GCaps.compute_shader_support = GLEW_ARB_compute_shader && GLEW_VERSION_4_3; if (GCaps.compute_shader_support) { glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &GCaps.max_work_group_count[0]); glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &GCaps.max_work_group_count[1]); diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 4ad7aa98484..dd8e6549e24 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -150,6 +150,14 @@ bool IMB_initImBuf( /** * Create a copy of a pixel buffer and wrap it to a new ImBuf + * (transferring ownership to the in imbuf). + * \attention Defined in allocimbuf.c + */ +struct ImBuf *IMB_allocFromBufferOwn( + unsigned int *rect, float *rectf, unsigned int w, unsigned int h, unsigned int channels); + +/** + * Create a copy of a pixel buffer and wrap it to a new ImBuf * \attention Defined in allocimbuf.c */ struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, diff --git a/source/blender/imbuf/IMB_thumbs.h b/source/blender/imbuf/IMB_thumbs.h index 9dd0cbe895f..e1a315a0bd2 100644 --- a/source/blender/imbuf/IMB_thumbs.h +++ b/source/blender/imbuf/IMB_thumbs.h @@ -52,6 +52,7 @@ typedef enum ThumbSource { #define THUMB_SIZE_MAX (100 * 1024 * 1024) #define PREVIEW_RENDER_DEFAULT_HEIGHT 128 +#define PREVIEW_RENDER_LARGE_HEIGHT 256 /* Note this can also be used as versioning system, * to force refreshing all thumbnails if e.g. we change some thumb generating code or so. diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 90c863878ff..1369f8cc0f8 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -430,6 +430,41 @@ bool imb_addrectImBuf(ImBuf *ibuf) return false; } +/** + * \param take_ownership: When true, the buffers become owned by the resulting image. + */ +struct ImBuf *IMB_allocFromBufferOwn( + unsigned int *rect, float *rectf, unsigned int w, unsigned int h, unsigned int channels) +{ + ImBuf *ibuf = NULL; + + if (!(rect || rectf)) { + return NULL; + } + + ibuf = IMB_allocImBuf(w, h, 32, 0); + + ibuf->channels = channels; + + /* Avoid #MEM_dupallocN since the buffers might not be allocated using guarded-allocation. */ + if (rectf) { + BLI_assert(MEM_allocN_len(rectf) == sizeof(float[4]) * w * h); + ibuf->rect_float = rectf; + + ibuf->flags |= IB_rectfloat; + ibuf->mall |= IB_rectfloat; + } + if (rect) { + BLI_assert(MEM_allocN_len(rect) == sizeof(uchar[4]) * w * h); + ibuf->rect = rect; + + ibuf->flags |= IB_rect; + ibuf->mall |= IB_rect; + } + + return ibuf; +} + struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, const float *rectf, unsigned int w, @@ -445,13 +480,21 @@ struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, ibuf = IMB_allocImBuf(w, h, 32, 0); ibuf->channels = channels; + + /* Avoid #MEM_dupallocN since the buffers might not be allocated using guarded-allocation. */ if (rectf) { - ibuf->rect_float = MEM_dupallocN(rectf); + const size_t size = sizeof(float[4]) * w * h; + ibuf->rect_float = MEM_mallocN(size, __func__); + memcpy(ibuf->rect_float, rectf, size); + ibuf->flags |= IB_rectfloat; ibuf->mall |= IB_rectfloat; } if (rect) { - ibuf->rect = MEM_dupallocN(rect); + const size_t size = sizeof(uchar[4]) * w * h; + ibuf->rect = MEM_mallocN(size, __func__); + memcpy(ibuf->rect, rect, size); + ibuf->flags |= IB_rect; ibuf->mall |= IB_rect; } diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index a09f06726a6..aa1da65253d 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -347,7 +347,7 @@ static ImBuf *thumb_create_ex(const char *file_path, tsize = PREVIEW_RENDER_DEFAULT_HEIGHT; break; case THB_LARGE: - tsize = PREVIEW_RENDER_DEFAULT_HEIGHT * 2; + tsize = PREVIEW_RENDER_LARGE_HEIGHT; break; case THB_FAIL: tsize = 1; diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index 0a3a43bb21f..0b5e927f02f 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -117,7 +117,9 @@ struct Mesh *ABC_read_mesh(struct CacheReader *reader, struct Mesh *existing_mesh, const float time, const char **err_str, - int read_flags); + const int read_flags, + const char *velocity_name, + const float velocity_scale); bool ABC_mesh_topology_changed(struct CacheReader *reader, struct Object *ob, @@ -133,16 +135,6 @@ struct CacheReader *CacheReader_open_alembic_object(struct CacheArchiveHandle *h struct Object *object, const char *object_path); -bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name); - -/* r_vertex_velocities should point to a preallocated array of num_vertices floats */ -int ABC_read_velocity_cache(struct CacheReader *reader, - const char *velocity_name, - float time, - float velocity_scale, - int num_vertices, - float *r_vertex_velocities); - #ifdef __cplusplus } #endif diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc index 910e04f3bf5..3665494c046 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc @@ -118,7 +118,7 @@ void ABCAbstractWriter::update_bounding_box(Object *object) if (!bb) { if (object->type != OB_CAMERA) { - CLOG_WARN(&LOG, "Bounding box is null!\n"); + CLOG_WARN(&LOG, "Bounding box is null!"); } bounding_box_.min.x = bounding_box_.min.y = bounding_box_.min.z = 0; bounding_box_.max.x = bounding_box_.max.y = bounding_box_.max.z = 0; diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 131b60b90fb..8f410978211 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -25,6 +25,7 @@ #include "BLI_assert.h" #include "BLI_math_vector.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_lib_id.h" #include "BKE_material.h" @@ -108,9 +109,6 @@ void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *contex OBoolProperty type(typeContainer, "meshtype"); type.set(subsurf_modifier_ == nullptr); } - - Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph); - liquid_sim_modifier_ = get_liquid_sim_modifier(scene_eval, context->object); } Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const @@ -144,21 +142,6 @@ bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const return false; } -ModifierData *ABCGenericMeshWriter::get_liquid_sim_modifier(Scene *scene, Object *ob) -{ - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim); - - if (md && (BKE_modifier_is_enabled(scene, md, eModifierMode_Render))) { - FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md); - - if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) { - return md; - } - } - - return nullptr; -} - bool ABCGenericMeshWriter::is_supported(const HierarchyContext *context) const { if (args_.export_params->visible_objects_only) { @@ -284,8 +267,7 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config); } - if (liquid_sim_modifier_ != nullptr) { - get_velocities(mesh, velocities); + if (get_velocities(mesh, velocities)) { mesh_sample.setVelocities(V3fArraySample(velocities)); } @@ -368,11 +350,6 @@ void ABCGenericMeshWriter::write_face_sets(Object *object, struct Mesh *mesh, Sc void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me) { - if (liquid_sim_modifier_ != nullptr) { - /* We don't need anything more for liquid meshes. */ - return; - } - if (frame_has_been_written_ || !args_.export_params->vcolors) { return; } @@ -387,27 +364,28 @@ void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me) write_custom_data(arb_geom_params, m_custom_data_config, &me->ldata, CD_MLOOPCOL); } -void ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels) +bool ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels) { + /* Export velocity attribute output by fluid sim, sequence cache modifier + * and geometry nodes. */ + CustomDataLayer *velocity_layer = BKE_id_attribute_find( + &mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT); + + if (velocity_layer == nullptr) { + return false; + } + const int totverts = mesh->totvert; + const float(*mesh_velocities)[3] = reinterpret_cast<float(*)[3]>(velocity_layer->data); vels.clear(); vels.resize(totverts); - FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(liquid_sim_modifier_); - FluidsimSettings *fss = fmd->fss; - - if (fss->meshVelocities) { - float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities); - - for (int i = 0; i < totverts; i++) { - copy_yup_from_zup(vels[i].getValue(), mesh_vels); - mesh_vels += 3; - } - } - else { - std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f)); + for (int i = 0; i < totverts; i++) { + copy_yup_from_zup(vels[i].getValue(), mesh_velocities[i]); } + + return true; } void ABCGenericMeshWriter::get_geo_groups(Object *object, diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h index 0e1792b9dab..fb8a01a3bbf 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.h +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h @@ -45,7 +45,6 @@ class ABCGenericMeshWriter : public ABCAbstractWriter { * exported object. */ bool is_subd_; ModifierData *subsurf_modifier_; - ModifierData *liquid_sim_modifier_; CDStreamConfig m_custom_data_config; @@ -70,10 +69,8 @@ class ABCGenericMeshWriter : public ABCAbstractWriter { void write_subd(HierarchyContext &context, Mesh *mesh); template<typename Schema> void write_face_sets(Object *object, Mesh *mesh, Schema &schema); - ModifierData *get_liquid_sim_modifier(Scene *scene_eval, Object *ob_eval); - void write_arb_geo_params(Mesh *me); - void get_velocities(Mesh *mesh, std::vector<Imath::V3f> &vels); + bool get_velocities(Mesh *mesh, std::vector<Imath::V3f> &vels); void get_geo_groups(Object *object, Mesh *mesh, std::map<std::string, std::vector<int32_t>> &geo_groups); diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index e9736555ead..4fba6a2f0f7 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -122,6 +122,12 @@ void read_custom_data(const std::string &iobject_full_name, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss); +void read_velocity(const Alembic::Abc::ICompoundProperty &prop, + const Alembic::Abc::PropertyHeader *prop_header, + const Alembic::Abc::ISampleSelector &selector, + const CDStreamConfig &config, + const char *velocity_name, + const float velocity_scale); typedef enum { ABC_UV_SCOPE_NONE, ABC_UV_SCOPE_LOOP, diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index 27ee35d1b39..d2ec7fb84db 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -94,7 +94,7 @@ void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele { Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE); - cu->flag |= CU_DEFORM_FILL | CU_3D; + cu->flag |= CU_3D; cu->actvert = CU_ACT_NONE; cu->resolu = 1; @@ -283,6 +283,8 @@ void AbcCurveReader::read_curve_sample(Curve *cu, Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh, const ISampleSelector &sample_sel, int /*read_flag*/, + const char * /*velocity_name*/, + const float /*velocity_scale*/, const char **err_str) { ICurvesSchema::Sample sample; diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h index 075ed5ca6a1..11b23c8a8cf 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.h +++ b/source/blender/io/alembic/intern/abc_reader_curves.h @@ -45,7 +45,9 @@ class AbcCurveReader : public AbcObjectReader { void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str); void read_curve_sample(Curve *cu, diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 77edd4908bd..eab94139f55 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -27,6 +27,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_customdata_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -36,6 +37,7 @@ #include "BLI_listbase.h" #include "BLI_math_geom.h" +#include "BKE_attribute.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" @@ -43,8 +45,10 @@ #include "BKE_object.h" using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::IV3fArrayProperty; using Alembic::Abc::P3fArraySamplePtr; using Alembic::Abc::PropertyHeader; +using Alembic::Abc::V3fArraySamplePtr; using Alembic::AbcGeom::IC3fGeomParam; using Alembic::AbcGeom::IC4fGeomParam; @@ -420,6 +424,52 @@ static void get_weight_and_index(CDStreamConfig &config, config.ceil_index = i1; } +static V3fArraySamplePtr get_velocity_prop(const ICompoundProperty &schema, + const ISampleSelector &selector, + const std::string &name) +{ + for (size_t i = 0; i < schema.getNumProperties(); i++) { + const PropertyHeader &header = schema.getPropertyHeader(i); + + if (header.isCompound()) { + const ICompoundProperty &prop = ICompoundProperty(schema, header.getName()); + + if (has_property(prop, name)) { + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0); + if (velocity_prop) { + return velocity_prop.getValue(selector); + } + } + } + else if (header.isArray()) { + if (header.getName() == name) { + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0); + return velocity_prop.getValue(selector); + } + } + } + + return V3fArraySamplePtr(); +} + +static void read_velocity(const V3fArraySamplePtr &velocities, + const CDStreamConfig &config, + const float velocity_scale) +{ + CustomDataLayer *velocity_layer = BKE_id_attribute_new( + &config.mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT, nullptr); + float(*velocity)[3] = (float(*)[3])velocity_layer->data; + + const int num_velocity_vectors = static_cast<int>(velocities->size()); + BLI_assert(num_velocity_vectors == config.mesh->totvert); + + for (int i = 0; i < num_velocity_vectors; i++) { + const Imath::V3f &vel_in = (*velocities)[i]; + copy_zup_from_yup(velocity[i], vel_in.getValue()); + mul_v3_fl(velocity[i], velocity_scale); + } +} + static void read_mesh_sample(const std::string &iobject_full_name, ImportSettings *settings, const IPolyMeshSchema &schema, @@ -458,6 +508,13 @@ static void read_mesh_sample(const std::string &iobject_full_name, if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector); } + + if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) { + V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name); + if (velocities) { + read_velocity(velocities, config, settings->velocity_scale); + } + } } CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation) @@ -563,7 +620,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); m_object->data = mesh; - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr); + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr); if (read_mesh != mesh) { /* XXX FIXME: after 2.80; mesh->flag isn't copied by #BKE_mesh_nomain_to_mesh(). */ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ @@ -630,7 +687,9 @@ bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, const ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str) { IPolyMeshSchema::Sample sample; @@ -673,6 +732,8 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, /* Only read point data when streaming meshes, unless we need to create new ones. */ ImportSettings settings; settings.read_flag |= read_flag; + settings.velocity_name = velocity_name; + settings.velocity_scale = velocity_scale; if (topology_changed(existing_mesh, sample_sel)) { new_mesh = BKE_mesh_new_nomain_from_template( @@ -829,6 +890,13 @@ static void read_subd_sample(const std::string &iobject_full_name, if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector); } + + if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) { + V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name); + if (velocities) { + read_velocity(velocities, config, settings->velocity_scale); + } + } } /* ************************************************************************** */ @@ -876,7 +944,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); m_object->data = mesh; - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr); + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr); if (read_mesh != mesh) { BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true); } @@ -935,7 +1003,9 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, const ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str) { ISubDSchema::Sample sample; @@ -962,6 +1032,8 @@ Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, ImportSettings settings; settings.read_flag |= read_flag; + settings.velocity_name = velocity_name; + settings.velocity_scale = velocity_scale; if (existing_mesh->totvert != positions->size()) { new_mesh = BKE_mesh_new_nomain_from_template( diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h index 3329b723b77..d9f89cc8085 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.h +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -42,7 +42,9 @@ class AbcMeshReader : public AbcObjectReader { struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str) override; bool topology_changed(Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel) override; @@ -73,7 +75,9 @@ class AbcSubDReader : public AbcObjectReader { void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str); }; diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc index 9a5ffd04bd1..a6d46c4b42a 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.cc +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -167,6 +167,8 @@ Imath::M44d get_matrix(const IXformSchema &schema, const float time) struct Mesh *AbcObjectReader::read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &UNUSED(sample_sel), int UNUSED(read_flag), + const char *UNUSED(velocity_name), + const float UNUSED(velocity_scale), const char **UNUSED(err_str)) { return existing_mesh; diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h index 89590b26b61..6e97f841a88 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.h +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -50,6 +50,10 @@ struct ImportSettings { /* From MeshSeqCacheModifierData.read_flag */ int read_flag; + /* From CacheFile and MeshSeqCacheModifierData */ + std::string velocity_name; + float velocity_scale; + bool validate_meshes; bool always_add_cache_reader; @@ -65,6 +69,8 @@ struct ImportSettings { sequence_len(1), sequence_offset(0), read_flag(0), + velocity_name(""), + velocity_scale(1.0f), validate_meshes(false), always_add_cache_reader(false), cache_file(NULL) @@ -143,7 +149,9 @@ class AbcObjectReader { virtual struct Mesh *read_mesh(struct Mesh *mesh, const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str); virtual bool topology_changed(Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel); diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index deb945b767c..63565b902f3 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -786,7 +786,9 @@ Mesh *ABC_read_mesh(CacheReader *reader, Mesh *existing_mesh, const float time, const char **err_str, - int read_flag) + const int read_flag, + const char *velocity_name, + const float velocity_scale) { AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); if (abc_reader == nullptr) { @@ -794,7 +796,8 @@ Mesh *ABC_read_mesh(CacheReader *reader, } ISampleSelector sample_sel = sample_selector_for_time(time); - return abc_reader->read_mesh(existing_mesh, sample_sel, read_flag, err_str); + return abc_reader->read_mesh( + existing_mesh, sample_sel, read_flag, velocity_name, velocity_scale, err_str); } bool ABC_mesh_topology_changed( @@ -860,136 +863,3 @@ CacheReader *CacheReader_open_alembic_object(CacheArchiveHandle *handle, return reinterpret_cast<CacheReader *>(abc_reader); } - -/* ************************************************************************** */ - -static const PropertyHeader *get_property_header(const IPolyMeshSchema &schema, const char *name) -{ - const PropertyHeader *prop_header = schema.getPropertyHeader(name); - - if (prop_header) { - return prop_header; - } - - ICompoundProperty prop = schema.getArbGeomParams(); - - if (!has_property(prop, name)) { - return nullptr; - } - - return prop.getPropertyHeader(name); -} - -bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name) -{ - AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); - - if (!abc_reader) { - return false; - } - - IObject iobject = abc_reader->iobject(); - - if (!iobject.valid()) { - return false; - } - - const ObjectHeader &header = iobject.getHeader(); - - if (!IPolyMesh::matches(header)) { - return false; - } - - IPolyMesh mesh(iobject, kWrapExisting); - IPolyMeshSchema schema = mesh.getSchema(); - - const PropertyHeader *prop_header = get_property_header(schema, name); - - if (!prop_header) { - return false; - } - - return IV3fArrayProperty::matches(prop_header->getMetaData()); -} - -static V3fArraySamplePtr get_velocity_prop(const IPolyMeshSchema &schema, - const ISampleSelector &iss, - const std::string &name) -{ - const PropertyHeader *prop_header = schema.getPropertyHeader(name); - - if (prop_header) { - const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0); - return velocity_prop.getValue(iss); - } - - ICompoundProperty prop = schema.getArbGeomParams(); - - if (!has_property(prop, name)) { - return V3fArraySamplePtr(); - } - - const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0); - - if (velocity_prop) { - return velocity_prop.getValue(iss); - } - - return V3fArraySamplePtr(); -} - -int ABC_read_velocity_cache(CacheReader *reader, - const char *velocity_name, - const float time, - float velocity_scale, - int num_vertices, - float *r_vertex_velocities) -{ - AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); - - if (!abc_reader) { - return -1; - } - - IObject iobject = abc_reader->iobject(); - - if (!iobject.valid()) { - return -1; - } - - const ObjectHeader &header = iobject.getHeader(); - - if (!IPolyMesh::matches(header)) { - return -1; - } - - IPolyMesh mesh(iobject, kWrapExisting); - IPolyMeshSchema schema = mesh.getSchema(); - ISampleSelector sample_sel(time); - const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel); - - V3fArraySamplePtr velocities = get_velocity_prop(schema, sample_sel, velocity_name); - - if (!velocities) { - return -1; - } - - float vel[3]; - - int num_velocity_vectors = static_cast<int>(velocities->size()); - - if (num_velocity_vectors != num_vertices) { - return -1; - } - - for (size_t i = 0; i < velocities->size(); ++i) { - const Imath::V3f &vel_in = (*velocities)[i]; - copy_zup_from_yup(vel, vel_in.getValue()); - - mul_v3_fl(vel, velocity_scale); - - copy_v3_v3(r_vertex_velocities + i * 3, vel); - } - - return num_vertices; -} diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp index 82d8b6e9ff3..005197c9027 100644 --- a/source/blender/io/collada/BCAnimationCurve.cpp +++ b/source/blender/io/collada/BCAnimationCurve.cpp @@ -173,10 +173,9 @@ std::string BCAnimationCurve::get_animation_name(Object *ob) const name = ""; } else { - char *boneName = BLI_str_quoted_substrN(fcurve->rna_path, "pose.bones["); - if (boneName) { + char boneName[MAXBONENAME]; + if (BLI_str_quoted_substr(fcurve->rna_path, "pose.bones[", boneName, sizeof(boneName))) { name = id_name(ob) + "_" + std::string(boneName); - MEM_freeN(boneName); } else { name = ""; diff --git a/source/blender/io/collada/BCAnimationSampler.cpp b/source/blender/io/collada/BCAnimationSampler.cpp index d2bde193c0a..953af5adffc 100644 --- a/source/blender/io/collada/BCAnimationSampler.cpp +++ b/source/blender/io/collada/BCAnimationSampler.cpp @@ -447,10 +447,9 @@ void BCAnimationSampler::initialize_curves(BCAnimationCurveMap &curves, Object * for (; fcu; fcu = fcu->next) { object_type = BC_ANIMATION_TYPE_OBJECT; if (ob->type == OB_ARMATURE) { - char *boneName = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (boneName) { + char boneName[MAXBONENAME]; + if (BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", boneName, sizeof(boneName))) { object_type = BC_ANIMATION_TYPE_BONE; - MEM_freeN(boneName); } } diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc index 31ecf27cf7e..12de1d82c72 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.cc +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -46,7 +46,7 @@ void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTim { curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); - curve_->flag |= CU_DEFORM_FILL | CU_3D; + curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; diff --git a/source/blender/io/usd/intern/usd_reader_instance.cc b/source/blender/io/usd/intern/usd_reader_instance.cc deleted file mode 100644 index e645b0237b9..00000000000 --- a/source/blender/io/usd/intern/usd_reader_instance.cc +++ /dev/null @@ -1,64 +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. - * - * The Original Code is Copyright (C) 2021 Blender Foundation. - * All rights reserved. - */ - -#include "usd_reader_instance.h" - -#include "BKE_object.h" -#include "DNA_object_types.h" - -#include <iostream> - -namespace blender::io::usd { - -USDInstanceReader::USDInstanceReader(const pxr::UsdPrim &prim, - const USDImportParams &import_params, - const ImportSettings &settings) - : USDXformReader(prim, import_params, settings) -{ -} - -bool USDInstanceReader::valid() const -{ - return prim_.IsValid() && prim_.IsInstance(); -} - -void USDInstanceReader::create_object(Main *bmain, const double /* motionSampleTime */) -{ - this->object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str()); - this->object_->data = nullptr; - this->object_->transflag |= OB_DUPLICOLLECTION; -} - -void USDInstanceReader::set_instance_collection(Collection *coll) -{ - if (this->object_) { - this->object_->instance_collection = coll; - } -} - -pxr::SdfPath USDInstanceReader::proto_path() const -{ - if (pxr::UsdPrim master = prim_.GetMaster()) { - return master.GetPath(); - } - - return pxr::SdfPath(); -} - -} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index 02ed7c35e57..93e433e231c 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -264,7 +264,7 @@ void compute_node_loc(const int column, float *r_locx, float *r_locy, NodePlacem r_ctx->column_offsets[column] += r_ctx->vertical_step + 10.0f; } -} // End anonymous namespace. +} // namespace USDMaterialReader::USDMaterialReader(const USDImportParams ¶ms, Main *bmain) : params_(params), bmain_(bmain) diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc index 9b30b524729..d6977d9c91a 100644 --- a/source/blender/io/usd/intern/usd_reader_nurbs.cc +++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc @@ -62,7 +62,7 @@ void USDNurbsReader::create_object(Main *bmain, const double /* motionSampleTime { curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); - curve_->flag |= CU_DEFORM_FILL | CU_3D; + curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index 233b3d9da4d..8c4cc18a9af 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -20,7 +20,6 @@ #include "usd_reader_stage.h" #include "usd_reader_camera.h" #include "usd_reader_curve.h" -#include "usd_reader_instance.h" #include "usd_reader_light.h" #include "usd_reader_mesh.h" #include "usd_reader_nurbs.h" @@ -34,6 +33,7 @@ #include <pxr/usd/usdGeom/mesh.h> #include <pxr/usd/usdGeom/nurbsCurves.h> #include <pxr/usd/usdGeom/scope.h> +#include <pxr/usd/usdGeom/xform.h> #include <pxr/usd/usdLux/light.h> #include <iostream> diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index 54316e56867..61b14155dd0 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -26,6 +26,7 @@ #include "BLI_assert.h" #include "BLI_math_vector.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_lib_id.h" #include "BKE_material.h" @@ -219,7 +220,7 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) if (usd_export_context_.export_params.export_normals) { write_normals(mesh, usd_mesh); } - write_surface_velocity(context.object, mesh, usd_mesh); + write_surface_velocity(mesh, usd_mesh); /* TODO(Sybren): figure out what happens when the face groups change. */ if (frame_has_been_written_) { @@ -409,42 +410,25 @@ void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_ usd_mesh.SetNormalsInterpolation(pxr::UsdGeomTokens->faceVarying); } -void USDGenericMeshWriter::write_surface_velocity(Object *object, - const Mesh *mesh, - pxr::UsdGeomMesh usd_mesh) +void USDGenericMeshWriter::write_surface_velocity(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) { - /* Only velocities from the fluid simulation are exported. This is the most important case, - * though, as the baked mesh changes topology all the time, and thus computing the velocities - * at import time in a post-processing step is hard. */ - ModifierData *md = BKE_modifiers_findby_type(object, eModifierType_Fluidsim); - if (md == nullptr) { - return; - } + /* Export velocity attribute output by fluid sim, sequence cache modifier + * and geometry nodes. */ + CustomDataLayer *velocity_layer = BKE_id_attribute_find( + &mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT); - /* Check that the fluid sim modifier is enabled and has useful data. */ - const bool use_render = (DEG_get_mode(usd_export_context_.depsgraph) == DAG_EVAL_RENDER); - const ModifierMode required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; - const Scene *scene = DEG_get_evaluated_scene(usd_export_context_.depsgraph); - if (!BKE_modifier_is_enabled(scene, md, required_mode)) { - return; - } - FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md); - if (!fsmd->fss || fsmd->fss->type != OB_FLUIDSIM_DOMAIN) { - return; - } - FluidsimSettings *fss = fsmd->fss; - if (!fss->meshVelocities) { + if (velocity_layer == nullptr) { return; } + const float(*velocities)[3] = reinterpret_cast<float(*)[3]>(velocity_layer->data); + /* Export per-vertex velocity vectors. */ pxr::VtVec3fArray usd_velocities; usd_velocities.reserve(mesh->totvert); - FluidVertexVelocity *mesh_velocities = fss->meshVelocities; - for (int vertex_idx = 0, totvert = mesh->totvert; vertex_idx < totvert; - ++vertex_idx, ++mesh_velocities) { - usd_velocities.push_back(pxr::GfVec3f(mesh_velocities->vel)); + for (int vertex_idx = 0, totvert = mesh->totvert; vertex_idx < totvert; ++vertex_idx) { + usd_velocities.push_back(pxr::GfVec3f(velocities[vertex_idx])); } pxr::UsdTimeCode timecode = get_export_time_code(); diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h index 6345f2d4240..d60a6c4a59b 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.h +++ b/source/blender/io/usd/intern/usd_writer_mesh.h @@ -49,7 +49,7 @@ class USDGenericMeshWriter : public USDAbstractWriter { const MaterialFaceGroups &usd_face_groups); void write_uv_maps(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); void write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); - void write_surface_velocity(Object *object, const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); + void write_surface_velocity(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); }; class USDMeshWriter : public USDGenericMeshWriter { diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index b9f31b88188..398534ba8f0 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -322,8 +322,6 @@ typedef enum eGp_Vertex_Mode { typedef enum eGP_Sculpt_Flag { /* invert the effect of the brush */ GP_SCULPT_FLAG_INVERT = (1 << 0), - /* smooth brush affects pressure values as well */ - GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 2), /* temporary invert action */ GP_SCULPT_FLAG_TMP_INVERT = (1 << 3), } eGP_Sculpt_Flag; diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 520fc6c1b00..a2433dbbbbd 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -302,8 +302,11 @@ typedef struct Curve { float fsize_realtime; /** - * A pointer to curve data from geometry nodes, currently only set for evaluated - * objects by the dependency graph iterator, and owned by #geometry_set_eval. + * A pointer to curve data from evaluation. Owned by the object's #geometry_set_eval, either as a + * geometry instance or the data of the evaluated #CurveComponent. The curve may also contain + * data in the #nurb list, but for evaluated curves this is the proper place to retrieve data, + * since it also contains the result of geometry nodes evaluation, and isn't just a copy of the + * original object data. */ struct CurveEval *curve_eval; @@ -344,8 +347,7 @@ enum { CU_DS_EXPAND = 1 << 11, /** make use of the path radius if this is enabled (default for new curves) */ CU_PATH_RADIUS = 1 << 12, - /** fill 2d curve after deformation */ - CU_DEFORM_FILL = 1 << 13, + /* CU_DEFORM_FILL = 1 << 13, */ /* DEPRECATED */ /** fill bevel caps */ CU_FILL_CAPS = 1 << 14, /** map taper object to beveled area */ diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index e5282409329..6acea8da15f 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -31,6 +31,8 @@ extern "C" { #endif +struct AnonymousAttributeID; + /** Descriptor and storage for a custom data layer. */ typedef struct CustomDataLayer { /** Type of data in layer. */ @@ -53,6 +55,13 @@ typedef struct CustomDataLayer { char name[64]; /** Layer data. */ void *data; + /** + * Run-time identifier for this layer. If no one has a strong reference to this id anymore, + * the layer can be removed. The custom data layer only has a weak reference to the id, because + * otherwise there will always be a strong reference and the attribute can't be removed + * automatically. + */ + const struct AnonymousAttributeID *anonymous_id; } CustomDataLayer; #define MAX_CUSTOMDATA_LAYER_NAME 64 diff --git a/source/blender/makesdna/DNA_fluid_defaults.h b/source/blender/makesdna/DNA_fluid_defaults.h index 95f5b8b66b0..4135c4d40a8 100644 --- a/source/blender/makesdna/DNA_fluid_defaults.h +++ b/source/blender/makesdna/DNA_fluid_defaults.h @@ -50,7 +50,6 @@ .tex_flags = NULL, \ .tex_range_field = NULL, \ .guide_parent = NULL, \ - .mesh_velocities = NULL, \ .effector_weights = NULL, /* #BKE_effector_add_weights. */ \ .p0 = {0.0f, 0.0f, 0.0f}, \ .p1 = {0.0f, 0.0f, 0.0f}, \ @@ -122,7 +121,6 @@ .mesh_smoothen_pos = 1, \ .mesh_smoothen_neg = 1, \ .mesh_scale = 2, \ - .totvert = 0, \ .mesh_generator = FLUID_DOMAIN_MESH_IMPROVED, \ .particle_type = 0, \ .particle_scale = 1, \ diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index 835af3c6ff8..0cbef540306 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -480,10 +480,6 @@ enum { SM_HRES_FULLSAMPLE = 2, }; -typedef struct FluidDomainVertexVelocity { - float vel[3]; -} FluidDomainVertexVelocity; - typedef struct FluidDomainSettings { /* -- Runtime-only fields (from here on). -- */ @@ -509,8 +505,6 @@ typedef struct FluidDomainSettings { struct GPUTexture *tex_flags; struct GPUTexture *tex_range_field; struct Object *guide_parent; - /** Vertex velocities of simulated fluid mesh. */ - struct FluidDomainVertexVelocity *mesh_velocities; struct EffectorWeights *effector_weights; /* Domain object data. */ @@ -607,9 +601,8 @@ typedef struct FluidDomainSettings { int mesh_smoothen_pos; int mesh_smoothen_neg; int mesh_scale; - int totvert; short mesh_generator; - char _pad6[6]; /* Unused. */ + char _pad6[2]; /* Unused. */ /* Secondary particle options. */ int particle_type; diff --git a/source/blender/makesdna/DNA_freestyle_types.h b/source/blender/makesdna/DNA_freestyle_types.h index 4d4fbaed29a..ab1e7aa903c 100644 --- a/source/blender/makesdna/DNA_freestyle_types.h +++ b/source/blender/makesdna/DNA_freestyle_types.h @@ -40,7 +40,7 @@ enum { FREESTYLE_RIDGES_AND_VALLEYS_FLAG = 1 << 1, FREESTYLE_MATERIAL_BOUNDARIES_FLAG = 1 << 2, FREESTYLE_FACE_SMOOTHNESS_FLAG = 1 << 3, - FREESTYLE_ADVANCED_OPTIONS_FLAG = 1 << 4, + /* FREESTYLE_ADVANCED_OPTIONS_FLAG = 1 << 4, */ /* UNUSED */ FREESTYLE_CULLING = 1 << 5, FREESTYLE_VIEW_MAP_CACHE = 1 << 6, FREESTYLE_AS_RENDER_PASS = 1 << 7, diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 81e9abc4916..450527c7443 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -304,7 +304,7 @@ .opacity = 1.0f, \ .flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \ .crease_threshold = DEG2RAD(140.0f), \ - .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES, \ + .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | LRT_USE_CREASE_ON_SHARP_EDGES, \ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ .overscan = 0.1f,\ @@ -319,5 +319,23 @@ .material = NULL,\ } +#define _DNA_DEFAULT_DashGpencilModifierData \ + { \ + .dash_offset = 0, \ + .segments = NULL, \ + .segments_len = 1, \ + .segment_active_index = 0, \ + } + +#define _DNA_DEFAULT_DashGpencilModifierSegment \ + { \ + .name = "", \ + .dash = 2, \ + .gap = 1, \ + .radius = 1.0f, \ + .opacity = 1.0f, \ + .mat_nr = -1, \ + } + /* clang-format off */ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index c91afa58cb1..d3429329ef6 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -56,6 +56,7 @@ typedef enum GpencilModifierType { eGpencilModifierType_Lineart = 19, eGpencilModifierType_Length = 20, eGpencilModifierType_Weight = 21, + eGpencilModifierType_Dash = 22, /* Keep last. */ NUM_GREASEPENCIL_MODIFIER_TYPES, } GpencilModifierType; @@ -507,6 +508,39 @@ typedef enum eLengthGpencil_Type { GP_LENGTH_ABSOLUTE = 1, } eLengthGpencil_Type; +typedef struct DashGpencilModifierSegment { + char name[64]; + /* For path reference. */ + struct DashGpencilModifierData *dmd; + int dash; + int gap; + float radius; + float opacity; + int mat_nr; + int _pad; +} DashGpencilModifierSegment; + +typedef struct DashGpencilModifierData { + GpencilModifierData modifier; + /** Material for filtering. */ + struct Material *material; + /** Layer name. */ + char layername[64]; + /** Custom index for passes. */ + int pass_index; + /** Flags. */ + int flag; + /** Custom index for passes. */ + int layer_pass; + + int dash_offset; + + DashGpencilModifierSegment *segments; + int segments_len; + int segment_active_index; + +} DashGpencilModifierData; + typedef struct MirrorGpencilModifierData { GpencilModifierData modifier; struct Object *object; diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 22408687daf..30ca9540735 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -94,11 +94,17 @@ typedef struct RenderSlot { struct RenderResult *render; } RenderSlot; -typedef struct ImageTile_Runtime { +typedef struct ImageTile_RuntimeTextureSlot { int tilearray_layer; int _pad; int tilearray_offset[2]; int tilearray_size[2]; +} ImageTile_RuntimeTextureSlot; + +typedef struct ImageTile_Runtime { + /* Data per `eImageTextureResolution`. + * Should match `IMA_TEXTURE_RESOLUTION_LEN` */ + ImageTile_RuntimeTextureSlot slots[2]; } ImageTile_Runtime; typedef struct ImageTile { @@ -132,6 +138,15 @@ typedef enum eGPUTextureTarget { TEXTARGET_COUNT, } eGPUTextureTarget; +/* Resolution variations that can be cached for an image. */ +typedef enum eImageTextureResolution { + IMA_TEXTURE_RESOLUTION_FULL = 0, + IMA_TEXTURE_RESOLUTION_LIMITED, + + /* Not an option, but holds the number of options defined for this struct. */ + IMA_TEXTURE_RESOLUTION_LEN +} eImageTextureResolution; + typedef struct Image { ID id; @@ -140,8 +155,8 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; - /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */ - struct GPUTexture *gputexture[3][2]; + /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes, 2 = IMA_TEXTURE_RESOLUTION_LEN. */ + struct GPUTexture *gputexture[3][2][2]; /* sources from: */ ListBase anims; @@ -233,12 +248,15 @@ enum { IMA_GPU_PARTIAL_REFRESH = (1 << 1), /** All mipmap levels in OpenGL texture set? */ IMA_GPU_MIPMAP_COMPLETE = (1 << 2), - /** Current texture resolution won't be limited by the GL Texture Limit user preference. */ - IMA_GPU_MAX_RESOLUTION = (1 << 3), + /* Reuse the max resolution textures as they fit in the limited scale. */ + IMA_GPU_REUSE_MAX_RESOLUTION = (1 << 3), + /* Has any limited scale textures been allocated. + * Adds additional checks to reuse max resolution images when they fit inside limited scale. */ + IMA_GPU_HAS_LIMITED_SCALE_TEXTURES = (1 << 4), }; /* Image.source, where the image comes from */ -enum { +typedef enum eImageSource { /* IMA_SRC_CHECK = 0, */ /* UNUSED */ IMA_SRC_FILE = 1, IMA_SRC_SEQUENCE = 2, @@ -246,10 +264,10 @@ enum { IMA_SRC_GENERATED = 4, IMA_SRC_VIEWER = 5, IMA_SRC_TILED = 6, -}; +} eImageSource; /* Image.type, how to handle or generate the image */ -enum { +typedef enum eImageType { IMA_TYPE_IMAGE = 0, IMA_TYPE_MULTILAYER = 1, /* generated */ @@ -257,7 +275,7 @@ enum { /* viewers */ IMA_TYPE_R_RESULT = 4, IMA_TYPE_COMPOSITE = 5, -}; +} eImageType; /* Image.gen_type */ enum { diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index cdb09c3af50..bdc9bcb6980 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -47,6 +47,8 @@ typedef enum eLineartMainFlags { LRT_CHAIN_LOOSE_EDGES = (1 << 12), LRT_CHAIN_GEOMETRY_SPACE = (1 << 13), LRT_ALLOW_OVERLAP_EDGE_TYPES = (1 << 14), + LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), + LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16), } eLineartMainFlags; typedef enum eLineartEdgeFlag { diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 1b3dbd148df..5b2694f420b 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -419,10 +419,6 @@ .velocity_scale = 1.0f, \ .reader = NULL, \ .reader_object_path = "", \ - .vertex_velocities = NULL, \ - .num_vertices = 0, \ - .velocity_delta = 0.0f, \ - .last_lookup_time = 0.0f, \ } #define _DNA_DEFAULT_MirrorModifierData \ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 13213f70fed..31daa778b03 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1942,6 +1942,7 @@ typedef struct MeshCacheModifierData { float factor; char deform_mode; + char defgrp_name[64]; char _pad[7]; /* play_mode == MOD_MESHCACHE_PLAY_CFEA */ @@ -1958,6 +1959,11 @@ typedef struct MeshCacheModifierData { char filepath[1024]; } MeshCacheModifierData; +/* MeshCache modifier flags. */ +enum { + MOD_MESHCACHE_INVERT_VERTEX_GROUP = 1 << 0, +}; + enum { MOD_MESHCACHE_TYPE_MDD = 1, MOD_MESHCACHE_TYPE_PC2 = 2, @@ -2138,10 +2144,6 @@ enum { MOD_NORMALEDIT_MIX_MUL = 3, }; -typedef struct MeshCacheVertexVelocity { - float vel[3]; -} MeshCacheVertexVelocity; - typedef struct MeshSeqCacheModifierData { ModifierData modifier; @@ -2157,25 +2159,6 @@ typedef struct MeshSeqCacheModifierData { /* Runtime. */ struct CacheReader *reader; char reader_object_path[1024]; - - /* Vertex velocities read from the cache. The velocities are not automatically read during - * modifier execution, and therefore have to manually be read when needed. This is only used - * through the RNA for now. */ - struct MeshCacheVertexVelocity *vertex_velocities; - - /* The number of vertices of the Alembic mesh, set when the modifier is executed. */ - int num_vertices; - - /* Time (in frames or seconds) between two velocity samples. Automatically computed to - * scale the velocity vectors at render time for generating proper motion blur data. */ - float velocity_delta; - - /* Caches the scene time (in seconds) used to lookup data in the Alembic archive when the - * modifier was last executed. Used to access Alembic samples through the RNA. */ - float last_lookup_time; - - int _pad1; - void *_pad2; } MeshSeqCacheModifierData; /* MeshSeqCacheModifierData.read_flag */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 361fefa59d2..c4cbc71762c 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -216,6 +216,16 @@ typedef enum eNodeSocketFlag { SOCK_HIDE_LABEL = (1 << 12), } eNodeSocketFlag; +/** Workaround to forward-declare C++ type in C header. */ +#ifdef __cplusplus +namespace blender::nodes { +class NodeDeclaration; +} +using NodeDeclarationHandle = blender::nodes::NodeDeclaration; +#else +typedef struct NodeDeclarationHandle NodeDeclarationHandle; +#endif + /* TODO: Limit data in bNode to what we want to see saved. */ typedef struct bNode { struct bNode *next, *prev, *new_node; @@ -315,6 +325,26 @@ typedef struct bNode { * needs to be a float to feed GPU_uniform. */ float sss_id; + + /** + * Describes the desired interface of the node. This is run-time data only. + * The actual interface of the node may deviate from the declaration temporarily. + * It's possible to sync the actual state of the node to the desired state. Currently, this is + * only done when a node is created or loaded. + * + * In the future, we may want to keep more data only in the declaration, so that it does not have + * to be synced to other places that are stored in files. That especially applies to data that + * can't be edited by users directly (e.g. min/max values of sockets, tooltips, ...). + * + * The declaration of a node can be recreated at any time when it is used. Caching it here is + * just a bit more efficient when it is used a lot. To make sure that the cache is up-to-date, + * call #nodeDeclarationEnsure before using it. + * + * Currently, the declaration is the same for every node of the same type. Going forward, that is + * intended to change though. Especially when nodes become more dynamic with respect to how many + * sockets they have. + */ + NodeDeclarationHandle *declaration; } bNode; /* node->flag */ @@ -1437,6 +1467,17 @@ typedef struct NodeGeometryRaycast { char _pad[1]; } NodeGeometryRaycast; +typedef struct NodeGeometryCurveFill { + uint8_t mode; +} NodeGeometryCurveFill; + +typedef struct NodeGeometryAttributeCapture { + /* CustomDataType. */ + int8_t data_type; + /* AttributeDomain. */ + int8_t domain; +} NodeGeometryAttributeCapture; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1885,6 +1926,7 @@ typedef enum GeometryNodeTriangulateQuads { typedef enum GeometryNodePointInstanceType { GEO_NODE_POINT_INSTANCE_TYPE_OBJECT = 0, GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION = 1, + GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY = 2, } GeometryNodePointInstanceType; typedef enum GeometryNodePointInstanceFlag { @@ -2008,6 +2050,11 @@ typedef enum GeometryNodeRaycastMapMode { GEO_NODE_RAYCAST_NEAREST = 1, } GeometryNodeRaycastMapMode; +typedef enum GeometryNodeCurveFillMode { + GEO_NODE_CURVE_FILL_MODE_TRIANGULATED = 0, + GEO_NODE_CURVE_FILL_MODE_NGONS = 1, +} GeometryNodeCurveFillMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 0250d853898..5a88ce7c9f5 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -158,8 +158,7 @@ typedef struct Object_Runtime { struct ID *data_orig; /** * Object data structure created during object evaluation. It has all modifiers applied. - * The type is determined by the type of the original object. For example, for mesh and curve - * objects, this is a mesh. For a volume object, this is a volume. + * The type is determined by the type of the original object. */ struct ID *data_eval; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 863c53615c1..5475e1bacd8 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -913,6 +913,13 @@ enum eFileDetails { #define FILE_MAX_LIBEXTRA (FILE_MAX + MAX_ID_NAME) +/** + * Maximum level of recursions accepted for #FileSelectParams.recursion_level. Rather than a + * completely arbitrary limit or none at all, make it just enough to support the most extreme case + * where the maximal path length is used with single letter directory/file names only. + */ +#define FILE_SELECT_MAX_RECURSIONS (FILE_MAX_LIBEXTRA / 2) + /* filesel types */ typedef enum eFileSelectType { FILE_LOADLIB = 1, @@ -936,13 +943,13 @@ typedef enum eFileSel_Action { * (WM and BLO code area, see #eBLOLibLinkFlags in BLO_readfile.h). */ typedef enum eFileSel_Params_Flag { - FILE_PARAMS_FLAG_UNUSED_1 = (1 << 0), /* cleared */ + FILE_APPEND_SET_FAKEUSER = (1 << 0), FILE_RELPATH = (1 << 1), FILE_LINK = (1 << 2), FILE_HIDE_DOT = (1 << 3), FILE_AUTOSELECT = (1 << 4), FILE_ACTIVE_COLLECTION = (1 << 5), - FILE_PARAMS_FLAG_UNUSED_6 = (1 << 6), /* cleared */ + FILE_APPEND_RECURSIVE = (1 << 6), FILE_DIRSEL_ONLY = (1 << 7), FILE_FILTER = (1 << 8), FILE_OBDATA_INSTANCE = (1 << 9), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 27376432092..4f86201ced2 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -646,7 +646,8 @@ typedef struct UserDef_Experimental { char use_sculpt_tools_tilt; char use_extended_asset_browser; char use_override_templates; - char _pad[5]; + char use_geometry_nodes_fields; + char _pad[4]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; @@ -924,9 +925,10 @@ typedef struct UserDef { short sequencer_proxy_setup; /* eUserpref_SeqProxySetup */ float collection_instance_empty_size; - char _pad10[3]; + char _pad10[2]; - char statusbar_flag; /* eUserpref_StatusBar_Flag */ + char file_preview_type; /* eUserpref_File_Preview_Type */ + char statusbar_flag; /* eUserpref_StatusBar_Flag */ struct WalkNavigation walk_navigation; @@ -996,7 +998,7 @@ typedef enum eUserPref_Flag { USER_NONUMPAD = (1 << 13), USER_ADD_CURSORALIGNED = (1 << 14), USER_FILECOMPRESS = (1 << 15), - USER_SAVE_PREVIEWS = (1 << 16), + USER_FLAG_UNUSED_5 = (1 << 16), /* dirty */ USER_CUSTOM_RANGE = (1 << 17), USER_ADD_EDITMODE = (1 << 18), USER_ADD_VIEWALIGNED = (1 << 19), @@ -1010,6 +1012,14 @@ typedef enum eUserPref_Flag { USER_FLAG_UNUSED_27 = (1 << 27), /* dirty */ } eUserPref_Flag; +/** #UserDef.file_preview_type */ +typedef enum eUserpref_File_Preview_Type { + USER_FILE_PREVIEW_NONE = 0, + USER_FILE_PREVIEW_AUTO, + USER_FILE_PREVIEW_SCREENSHOT, + USER_FILE_PREVIEW_CAMERA, +} eUserpref_File_Preview_Type; + typedef enum eUserPref_PrefFlag { USER_PREF_FLAG_SAVE = (1 << 0), } eUserPref_PrefFlag; @@ -1126,7 +1136,9 @@ typedef enum eUserpref_TableAPI { /** #UserDef.app_flag */ typedef enum eUserpref_APP_Flag { - USER_APP_LOCK_UI_LAYOUT = (1 << 0), + USER_APP_LOCK_CORNER_SPLIT = (1 << 0), + USER_APP_HIDE_REGION_TOGGLE = (1 << 1), + USER_APP_LOCK_EDGE_RESIZE = (1 << 2), } eUserpref_APP_Flag; /** #UserDef.statusbar_flag */ diff --git a/source/blender/makesdna/DNA_uuid_types.h b/source/blender/makesdna/DNA_uuid_types.h new file mode 100644 index 00000000000..30c8beaa628 --- /dev/null +++ b/source/blender/makesdna/DNA_uuid_types.h @@ -0,0 +1,43 @@ +/* + * 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 DNA + */ + +#pragma once + +#include "DNA_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Universally Unique Identifier according to RFC4122. + */ +typedef struct UUID { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} UUID; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index a573e2f9e8c..4cb8610f6ac 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -68,6 +68,8 @@ * #BLO_update_defaults_startup_blend & #blo_do_versions_userdef. */ +#define DNA_DEPRECATED_ALLOW + #include <limits.h> #include <stdio.h> #include <stdlib.h> @@ -319,6 +321,8 @@ SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(WeightGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierSegment); #undef SDNA_DEFAULT_DECL_STRUCT @@ -547,6 +551,8 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(WeightGpencilModifierData), SDNA_DEFAULT_DECL(LineartGpencilModifierData), SDNA_DEFAULT_DECL(LengthGpencilModifierData), + SDNA_DEFAULT_DECL(DashGpencilModifierData), + SDNA_DEFAULT_DECL(DashGpencilModifierSegment), }; #undef SDNA_DEFAULT_DECL #undef SDNA_DEFAULT_DECL_EX diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 061c3462a69..0b7848b6662 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -141,6 +141,7 @@ static const char *includefiles[] = { "DNA_volume_types.h", "DNA_simulation_types.h", "DNA_pointcache_types.h", + "DNA_uuid_types.h", "DNA_asset_types.h", /* see comment above before editing! */ @@ -1678,6 +1679,7 @@ int main(int argc, char **argv) #include "DNA_texture_types.h" #include "DNA_tracking_types.h" #include "DNA_userdef_types.h" +#include "DNA_uuid_types.h" #include "DNA_vec_types.h" #include "DNA_vfont_types.h" #include "DNA_view2d_types.h" diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index abbe609d0ef..ce53e3390e1 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -177,6 +177,7 @@ extern StructRNA RNA_CompositorNodeMixRGB; extern StructRNA RNA_CompositorNodeNormal; extern StructRNA RNA_CompositorNodeNormalize; extern StructRNA RNA_CompositorNodeOutputFile; +extern StructRNA RNA_CompositorNodePosterize; extern StructRNA RNA_CompositorNodePremulKey; extern StructRNA RNA_CompositorNodeRGB; extern StructRNA RNA_CompositorNodeRGBToBW; @@ -221,6 +222,8 @@ extern StructRNA RNA_CurvePoint; extern StructRNA RNA_CurveProfile; extern StructRNA RNA_CurveProfilePoint; extern StructRNA RNA_DampedTrackConstraint; +extern StructRNA RNA_DashGpencilModifierData; +extern StructRNA RNA_DashGpencilModifierSegment; extern StructRNA RNA_DataTransferModifier; extern StructRNA RNA_DecimateModifier; extern StructRNA RNA_Depsgraph; @@ -1117,13 +1120,17 @@ bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop); char *RNA_path_append( const char *path, PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey); +#if 0 /* UNUSED. */ char *RNA_path_back(const char *path); +#endif /* path_resolve() variants only ensure that a valid pointer (and optionally property) exist */ bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop); bool RNA_path_resolve_full( PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); +bool RNA_path_resolve_full_maybe_null( + PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); /* path_resolve_property() variants ensure that pointer + property both exist */ bool RNA_path_resolve_property(PointerRNA *ptr, @@ -1162,7 +1169,7 @@ char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *nee struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path); -char *RNA_path_from_ID_to_struct(PointerRNA *ptr); +char *RNA_path_from_ID_to_struct(const PointerRNA *ptr); char *RNA_path_from_real_ID_to_struct(struct Main *bmain, PointerRNA *ptr, struct ID **r_real); @@ -1192,7 +1199,7 @@ char *RNA_path_full_property_py(struct Main *bmain, struct PropertyRNA *prop, int index); char *RNA_path_struct_property_py(struct PointerRNA *ptr, struct PropertyRNA *prop, int index); -char *RNA_path_property_py(struct PointerRNA *ptr, struct PropertyRNA *prop, int index); +char *RNA_path_property_py(const struct PointerRNA *ptr, struct PropertyRNA *prop, int index); /* Quick name based property access * diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 8f8ad077935..eb887e1881b 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -931,11 +931,10 @@ static void rna_ID_user_remap(ID *id, Main *bmain, ID *new_id) static struct ID *rna_ID_make_local(struct ID *self, Main *bmain, bool clear_proxy) { - BKE_lib_id_make_local( - bmain, self, false, clear_proxy ? 0 : LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BKE_lib_id_make_local(bmain, self, clear_proxy ? 0 : LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); ID *ret_id = self->newid ? self->newid : self; - BKE_id_clear_newpoin(self); + BKE_id_newptr_and_tag_clear(self); return ret_id; } diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index a2905018cc7..fceb6d045c3 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -4859,75 +4859,119 @@ PointerRNA rna_array_lookup_int( /* RNA Path - Experiment */ -static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int bracket) +/** + * Extract the first token from `path`. + * + * \param path: Extract the token from path, step the pointer to the beginning of the next token + * \return The nil terminated token. + */ +static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen) { - const char *p; int len = 0; - if (bracket) { - /* get data between [], check escaping quotes and back-slashes with #BLI_str_unescape. */ - if (**path == '[') { - (*path)++; - } - else { - return NULL; - } + /* Get data until `.` or `[`. */ + const char *p = *path; + while (*p && !ELEM(*p, '.', '[')) { + len++; + p++; + } - p = *path; + /* Empty, return. */ + if (UNLIKELY(len == 0)) { + return NULL; + } - /* 2 kinds of look-ups now, quoted or unquoted. */ - if (*p != '"') { - while (*p && (*p != ']')) { - len++; - p++; - } - } - else { - const char *p_end = BLI_str_escape_find_quote(p + 1); - if (p_end == NULL) { - /* No Matching quote. */ - return NULL; - } - /* Skip the last quoted char to get the `]`. */ - p_end += 1; + /* Try to use fixed buffer if possible. */ + char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__); + memcpy(buf, *path, sizeof(char) * len); + buf[len] = '\0'; - len += (p_end - p); - p = p_end; - } + if (*p == '.') { + p++; + } + *path = p; - if (*p != ']') { + return buf; +} + +/** + * Extract the first token in brackets from `path` (with quoted text support). + * + * - `[0]` -> `0` + * - `["Some\"Quote"]` -> `Some"Quote` + * + * \param path: Extract the token from path, step the pointer to the beginning of the next token + * (past quoted text and brackets). + * \return The nil terminated token. + */ +static char *rna_path_token_in_brackets(const char **path, + char *fixedbuf, + int fixedlen, + bool *r_quoted) +{ + int len = 0; + bool quoted = false; + + BLI_assert(r_quoted != NULL); + + /* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */ + if (UNLIKELY(**path != '[')) { + return NULL; + } + + (*path)++; + const char *p = *path; + + /* 2 kinds of look-ups now, quoted or unquoted. */ + if (*p == '"') { + /* Find the matching quote. */ + (*path)++; + p = *path; + const char *p_end = BLI_str_escape_find_quote(p); + if (p_end == NULL) { + /* No Matching quote. */ return NULL; } + /* Exclude the last quote from the length. */ + len += (p_end - p); + + /* Skip the last quoted char to get the `]`. */ + p_end += 1; + p = p_end; + quoted = true; } else { - /* Get data until `.` or `[`. */ - p = *path; - - while (*p && *p != '.' && *p != '[') { + /* Find the matching bracket. */ + while (*p && (*p != ']')) { len++; p++; } } - /* empty, return */ - if (len == 0) { + if (UNLIKELY(*p != ']')) { + return NULL; + } + + /* Empty, return. */ + if (UNLIKELY(len == 0)) { return NULL; } /* Try to use fixed buffer if possible. */ char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__); - /* copy string, taking into account escaped ] */ - if (bracket) { + /* Copy string, taking into account escaped ']' */ + if (quoted) { BLI_str_unescape(buf, *path, len); - p = (*path) + len; + /* +1 to step over the last quote. */ + BLI_assert((*path)[len] == '"'); + p = (*path) + len + 1; } else { memcpy(buf, *path, sizeof(char) * len); buf[len] = '\0'; } - - /* set path to start of next token */ + /* Set path to start of next token. */ if (*p == ']') { p++; } @@ -4936,20 +4980,9 @@ static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int } *path = p; - return buf; -} + *r_quoted = quoted; -static int rna_token_strip_quotes(char *token) -{ - if (token[0] == '"') { - int len = strlen(token); - if (len >= 2 && token[len - 1] == '"') { - /* strip away "" */ - token[len - 1] = '\0'; - return 1; - } - } - return 0; + return buf; } static bool rna_path_parse_collection_key(const char **path, @@ -4968,18 +5001,19 @@ static bool rna_path_parse_collection_key(const char **path, } if (**path == '[') { + bool quoted; char *token; /* resolve the lookup with [] brackets */ - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1); + token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed); if (!token) { return false; } /* check for "" to see if it is a string */ - if (rna_token_strip_quotes(token)) { - if (RNA_property_collection_lookup_string(ptr, prop, token + 1, r_nextptr)) { + if (quoted) { + if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) { /* pass */ } else { @@ -5041,15 +5075,16 @@ static bool rna_path_parse_array_index(const char **path, /* multi index resolve */ if (**path == '[') { - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1); + bool quoted; + token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed); if (token == NULL) { /* invalid syntax blah[] */ return false; } /* check for "" to see if it is a string */ - if (rna_token_strip_quotes(token)) { - temp_index = RNA_property_array_item_index(prop, *(token + 1)); + if (quoted) { + temp_index = RNA_property_array_item_index(prop, *token); } else { /* otherwise do int lookup */ @@ -5066,7 +5101,7 @@ static bool rna_path_parse_array_index(const char **path, } else if (dim == 1) { /* location.x || scale.X, single dimension arrays only */ - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 0); + token = rna_path_token(path, fixedbuf, sizeof(fixedbuf)); if (token == NULL) { /* invalid syntax blah. */ return false; @@ -5166,8 +5201,7 @@ static bool rna_path_parse(PointerRNA *ptr, RNA_POINTER_INVALIDATE(&nextptr); } - int use_id_prop = (*path == '[') ? 1 : 0; - char *token; + const bool use_id_prop = (*path == '['); /* custom property lookup ? * C.object["someprop"] */ @@ -5177,8 +5211,10 @@ static bool rna_path_parse(PointerRNA *ptr, } /* look up property name in current struct */ - token = rna_path_token(&path, fixedbuf, sizeof(fixedbuf), use_id_prop); - + bool quoted = false; + char *token = use_id_prop ? + rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), "ed) : + rna_path_token(&path, fixedbuf, sizeof(fixedbuf)); if (!token) { return false; } @@ -5186,8 +5222,8 @@ static bool rna_path_parse(PointerRNA *ptr, prop = NULL; if (use_id_prop) { /* look up property name in current struct */ IDProperty *group = RNA_struct_idprops(&curptr, 0); - if (group && rna_token_strip_quotes(token)) { - prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token + 1); + if (group && quoted) { + prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token); } } else { @@ -5326,6 +5362,18 @@ bool RNA_path_resolve_full( } /** + * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data. + * + * \note While it's correct to ignore the value of #PointerRNA.data + * most callers need to know if the resulting pointer was found and not null. + */ +bool RNA_path_resolve_full_maybe_null( + PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) +{ + return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true); +} + +/** * Resolve the given RNA Path to find both the pointer AND property * indicated by fully resolving the path. * @@ -5473,7 +5521,9 @@ char *RNA_path_append( return result; } -char *RNA_path_back(const char *path) +/* Having both path append & back seems like it could be useful, + * this function isn't used at the moment. */ +static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path) { char fixedbuf[256]; const char *previous, *current; @@ -5492,7 +5542,7 @@ char *RNA_path_back(const char *path) while (*current) { char *token; - token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), 0); + token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf)); if (!token) { return NULL; @@ -5502,7 +5552,8 @@ char *RNA_path_back(const char *path) } /* in case of collection we also need to strip off [] */ - token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), 1); + bool quoted; + token = rna_path_token_in_brackets(¤t, fixedbuf, sizeof(fixedbuf), "ed); if (token && token != fixedbuf) { MEM_freeN(token); } @@ -5690,7 +5741,7 @@ char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle) return NULL; } -static char *rna_path_from_ID_to_idpgroup(PointerRNA *ptr) +static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr) { PointerRNA id_ptr; @@ -5775,7 +5826,7 @@ static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_re return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL; } -char *RNA_path_from_ID_to_struct(PointerRNA *ptr) +char *RNA_path_from_ID_to_struct(const PointerRNA *ptr) { char *ptrpath = NULL; @@ -5786,7 +5837,7 @@ char *RNA_path_from_ID_to_struct(PointerRNA *ptr) if (!RNA_struct_is_ID(ptr->type)) { if (ptr->type->path) { /* if type has a path to some ID, use it */ - ptrpath = ptr->type->path(ptr); + ptrpath = ptr->type->path((PointerRNA *)ptr); } else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) { PointerRNA parentptr; @@ -6156,7 +6207,7 @@ char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index) * Get the struct.property as a python representation, eg: * some_prop[10] */ -char *RNA_path_property_py(PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index) +char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index) { char *ret; diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 2c552970c82..f8a36c1b2e6 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -788,7 +788,7 @@ bool RNA_struct_override_matches(Main *bmain, continue; } - CLOG_INFO(&LOG, 5, "Override Checking %s\n", rna_path); + CLOG_INFO(&LOG, 5, "Override Checking %s", rna_path); IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(override, rna_path); if (ignore_overridden && op != NULL) { diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index b4ea70c33ab..49e813e6a6c 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -41,8 +41,8 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {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_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point values"}, + {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit values"}, {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"}, @@ -54,8 +54,8 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { {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_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point values"}, + {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit values"}, {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"}, @@ -443,7 +443,7 @@ static void rna_def_attribute_float_vector(BlenderRNA *brna) srna = RNA_def_struct(brna, "FloatVectorAttribute", "Attribute"); RNA_def_struct_sdna(srna, "CustomDataLayer"); RNA_def_struct_ui_text( - srna, "Float Vector Attribute", "Vector geometry attribute, with floating-point precision"); + srna, "Float Vector Attribute", "Vector geometry attribute, with floating-point values"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); @@ -479,7 +479,7 @@ static void rna_def_attribute_float_color(BlenderRNA *brna) srna = RNA_def_struct(brna, "FloatColorAttribute", "Attribute"); RNA_def_struct_sdna(srna, "CustomDataLayer"); RNA_def_struct_ui_text( - srna, "Float Color Attribute", "Color geometry attribute, with floating-point precision"); + srna, "Float Color Attribute", "Color geometry attribute, with floating-point values"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "FloatColorAttributeValue"); @@ -514,7 +514,7 @@ static void rna_def_attribute_byte_color(BlenderRNA *brna) srna = RNA_def_struct(brna, "ByteColorAttribute", "Attribute"); RNA_def_struct_sdna(srna, "CustomDataLayer"); RNA_def_struct_ui_text( - srna, "Byte Color Attribute", "Color geometry attribute, with 8-bit precision"); + srna, "Byte Color Attribute", "Color geometry attribute, with 8-bit values"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "ByteColorAttributeValue"); @@ -639,7 +639,7 @@ static void rna_def_attribute_float2(BlenderRNA *brna) srna = RNA_def_struct(brna, "Float2Attribute", "Attribute"); RNA_def_struct_sdna(srna, "CustomDataLayer"); RNA_def_struct_ui_text( - srna, "Float2 Attribute", "2D vector geometry attribute, with floating-point precision"); + srna, "Float2 Attribute", "2D vector geometry attribute, with floating-point values"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "Float2AttributeValue"); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index aef1c76910c..d32de7394cc 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1808,13 +1808,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Trim Stroke Ends", "Trim intersecting stroke ends"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - prop = RNA_def_property(srna, "use_edit_pressure", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "sculpt_flag", GP_SCULPT_FLAG_SMOOTH_PRESSURE); - RNA_def_property_ui_text( - prop, "Affect Pressure", "Affect pressure values as well when smoothing strokes"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "sculpt_flag"); RNA_def_property_enum_items(prop, prop_direction_items); diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 1ac6dd021e9..5c12fc3a227 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -189,7 +189,7 @@ static char *rna_ColorRamp_path(PointerRNA *ptr) SH_NODE_VALTORGB, CMP_NODE_VALTORGB, TEX_NODE_VALTORGB, - GEO_NODE_ATTRIBUTE_COLOR_RAMP)) { + GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP)) { if (node->storage == ptr->data) { /* all node color ramp properties called 'color_ramp' * prepend path from ID to the node @@ -320,7 +320,7 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * SH_NODE_VALTORGB, CMP_NODE_VALTORGB, TEX_NODE_VALTORGB, - GEO_NODE_ATTRIBUTE_COLOR_RAMP)) { + GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP)) { ED_node_tag_update_nodetree(bmain, ntree, node); } } diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 9c6659a7130..0bfb1200f49 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1809,12 +1809,6 @@ static void rna_def_curve(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Twist Smooth", "Smoothing iteration for tangents"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); - prop = RNA_def_property(srna, "use_fill_deform", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_DEFORM_FILL); - RNA_def_property_ui_text( - prop, "Fill Deformed", "Fill curve after applying shape keys and all modifiers"); - RNA_def_property_update(prop, 0, "rna_Curve_update_data"); - prop = RNA_def_property(srna, "use_fill_caps", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_FILL_CAPS); RNA_def_property_ui_text(prop, "Fill Caps", "Fill caps for beveled curves"); diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 10e899b7ee3..90e77406f23 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -1242,22 +1242,6 @@ static void rna_Fluid_flowtype_set(struct PointerRNA *ptr, int value) #else -static void rna_def_fluid_mesh_vertices(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "FluidDomainVertexVelocity", NULL); - RNA_def_struct_ui_text(srna, "Fluid Mesh Velocity", "Velocity of a simulated fluid mesh"); - RNA_def_struct_ui_icon(srna, ICON_VERTEXSEL); - - prop = RNA_def_property(srna, "velocity", PROP_FLOAT, PROP_VELOCITY); - RNA_def_property_array(prop, 3); - RNA_def_property_float_sdna(prop, NULL, "vel"); - RNA_def_property_ui_text(prop, "Velocity", ""); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); -} - static void rna_def_fluid_domain_settings(BlenderRNA *brna) { StructRNA *srna; @@ -2019,14 +2003,6 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mesh generator", "Which particle level set generator to use"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_update"); - prop = RNA_def_property(srna, "mesh_vertices", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "mesh_velocities", "totvert"); - RNA_def_property_struct_type(prop, "FluidDomainVertexVelocity"); - RNA_def_property_ui_text( - prop, "Fluid Mesh Vertices", "Vertices of the fluid mesh generated by simulation"); - - rna_def_fluid_mesh_vertices(brna); - prop = RNA_def_property(srna, "use_mesh", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_MESH); RNA_def_property_ui_text(prop, "Use Mesh", "Enable fluid mesh (using amplification)"); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 3e5dce64c7b..f06c8a5325c 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -2005,6 +2005,14 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) prop, "Custom Channel Color", "Custom color for animation channel in Dopesheet"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Layer Opacity (Annotations). */ + prop = RNA_def_property(srna, "annotation_opacity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "opacity"); + RNA_def_property_range(prop, 0.0, 1.0f); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Opacity", "Annotation Layer Opacity"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Stroke Drawing Color (Annotations) */ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_array(prop, 3); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 4e95174e42b..4fa33424994 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -35,6 +35,7 @@ #include "BLI_math.h" #include "BLI_rand.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" @@ -68,9 +69,14 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_BUILD, "Build", "Create duplication of strokes"}, + {eGpencilModifierType_Dash, + "GP_DASH", + ICON_MOD_DASH, + "Dot Dash", + "Generate dot-dash styled strokes"}, {eGpencilModifierType_Lineart, "GP_LINEART", - ICON_MOD_EDGESPLIT, /* TODO: Use a proper icon. */ + ICON_MOD_LINEART, "Line Art", "Generate line art strokes from selected source"}, {eGpencilModifierType_Mirror, @@ -116,7 +122,7 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { "Deform strokes using lattice"}, {eGpencilModifierType_Length, "GP_LENGTH", - ICON_MOD_EDGESPLIT, + ICON_MOD_LENGTH, "Length", "Extend or shrink strokes"}, {eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"}, @@ -268,6 +274,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr) return &RNA_TextureGpencilModifier; case eGpencilModifierType_Lineart: return &RNA_LineartGpencilModifier; + case eGpencilModifierType_Dash: + return &RNA_DashGpencilModifierData; /* Default */ case eGpencilModifierType_None: case NUM_GREASEPENCIL_MODIFIER_TYPES: @@ -282,19 +290,19 @@ static void rna_GpencilModifier_name_set(PointerRNA *ptr, const char *value) GpencilModifierData *gmd = ptr->data; char oldname[sizeof(gmd->name)]; - /* make a copy of the old name first */ + /* Make a copy of the old name first. */ BLI_strncpy(oldname, gmd->name, sizeof(gmd->name)); - /* copy the new name into the name slot */ + /* Copy the new name into the name slot. */ BLI_strncpy_utf8(gmd->name, value, sizeof(gmd->name)); - /* make sure the name is truly unique */ + /* Make sure the name is truly unique. */ if (ptr->owner_id) { Object *ob = (Object *)ptr->owner_id; BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, gmd); } - /* fix all the animation data which may link to this */ + /* Fix all the animation data which may link to this. */ BKE_animdata_fix_paths_rename_all(NULL, "grease_pencil_modifiers", oldname, gmd->name); } @@ -674,6 +682,59 @@ static void rna_Lineart_end_level_set(PointerRNA *ptr, int value) lmd->level_start = MIN2(value, lmd->level_start); } +static void rna_GpencilDash_segments_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)ptr->data; + rna_iterator_array_begin( + iter, dmd->segments, sizeof(DashGpencilModifierSegment), dmd->segments_len, false, NULL); +} + +static char *rna_DashGpencilModifierSegment_path(PointerRNA *ptr) +{ + DashGpencilModifierSegment *ds = (DashGpencilModifierSegment *)ptr->data; + + DashGpencilModifierData *dmd = (DashGpencilModifierData *)ds->dmd; + + BLI_assert(dmd != NULL); + + char name_esc[sizeof(dmd->modifier.name) * 2 + 1]; + + BLI_str_escape(name_esc, dmd->modifier.name, sizeof(name_esc)); + + return BLI_sprintfN("grease_pencil_modifiers[\"%s\"].segments[\"%s\"]", name_esc, ds->name); +} + +static bool dash_segment_name_exists_fn(void *arg, const char *name) +{ + const DashGpencilModifierData *dmd = (const DashGpencilModifierData *)arg; + for (int i = 0; i < dmd->segments_len; i++) { + if (STREQ(dmd->segments[i].name, name)) { + return true; + } + } + return false; +} + +static void rna_DashGpencilModifierSegment_name_set(PointerRNA *ptr, const char *value) +{ + DashGpencilModifierSegment *ds = ptr->data; + + char oldname[sizeof(ds->name)]; + BLI_strncpy(oldname, ds->name, sizeof(ds->name)); + + BLI_strncpy_utf8(ds->name, value, sizeof(ds->name)); + + BLI_assert(ds->dmd != NULL); + BLI_uniquename_cb( + dash_segment_name_exists_fn, ds->dmd, "Segment", '.', ds->name, sizeof(ds->name)); + + char prefix[256]; + sprintf(prefix, "grease_pencil_modifiers[\"%s\"].segments", ds->dmd->modifier.name); + + /* Fix all the animation data which may link to this. */ + BKE_animdata_fix_paths_rename_all(NULL, prefix, oldname, ds->name); +} + #else static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) @@ -2902,7 +2963,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "Line Art Modifier", "Generate line art strokes from selected source"); RNA_def_struct_sdna(srna, "LineartGpencilModifierData"); - RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT); + RNA_def_struct_ui_icon(srna, ICON_MOD_LINEART); RNA_define_lib_overridable(true); @@ -3188,6 +3249,18 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Masks", "Mask bits to match from Collection Line Art settings"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_crease_on_smooth", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "calculation_flags", LRT_USE_CREASE_ON_SMOOTH_SURFACES); + RNA_def_property_ui_text( + prop, "Crease On Smooth Surfaces", "Allow crease edges to show inside smooth surfaces"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_crease_on_sharp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_CREASE_ON_SHARP_EDGES); + RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_define_lib_overridable(false); } @@ -3199,7 +3272,7 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) srna = RNA_def_struct(brna, "LengthGpencilModifier", "GpencilModifier"); RNA_def_struct_ui_text(srna, "Length Modifier", "Stretch or shrink strokes"); RNA_def_struct_sdna(srna, "LengthGpencilModifierData"); - RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT); + RNA_def_struct_ui_icon(srna, ICON_MOD_LENGTH); RNA_define_lib_overridable(true); @@ -3275,6 +3348,136 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_define_lib_overridable(false); } +static void rna_def_modifier_gpencildash(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "DashGpencilModifierSegment", NULL); + RNA_def_struct_ui_text(srna, "Dash Modifier Segment", "Configuration for a single dash segment"); + RNA_def_struct_sdna(srna, "DashGpencilModifierSegment"); + RNA_def_struct_path_func(srna, "rna_DashGpencilModifierSegment_path"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the dash segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_DashGpencilModifierSegment_name_set"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "dash", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, INT16_MAX); + RNA_def_property_ui_text( + prop, + "Dash", + "The number of consecutive points from the original stroke to include in this segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "gap", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, INT16_MAX); + RNA_def_property_ui_text(prop, "Gap", "The number of points skipped after this segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_FACTOR | PROP_UNSIGNED); + RNA_def_property_ui_range(prop, 0, 1, 0.1, 2); + RNA_def_property_ui_text( + prop, "Radius", "The factor to apply to the original point's radius for the new points"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_range(prop, 0, 1, 0.1, 2); + RNA_def_property_ui_text( + prop, "Opacity", "The factor to apply to the original point's opacity for the new points"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "mat_nr"); + RNA_def_property_range(prop, -1, INT16_MAX); + RNA_def_property_ui_text( + prop, + "Material Index", + "Use this index on generated segment. -1 means using the existing material"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + srna = RNA_def_struct(brna, "DashGpencilModifierData", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Dash Modifier", "Create dot-dash effect for strokes"); + RNA_def_struct_sdna(srna, "DashGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_DASH); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "segments", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "DashGpencilModifierSegment"); + RNA_def_property_collection_sdna(prop, NULL, "segments", NULL); + RNA_def_property_collection_funcs(prop, + "rna_GpencilDash_segments_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + NULL, + NULL, + NULL, + NULL); + RNA_def_property_ui_text(prop, "Segments", ""); + + prop = RNA_def_property(srna, "segment_active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Active Dash Segement Index", "Active index in the segment list"); + + prop = RNA_def_property(srna, "dash_offset", PROP_INT, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Offset", + "Offset into each stroke before the beginning of the dashed segment generation"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Common properties. */ + + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "layername"); + RNA_def_property_ui_text(prop, "Layer", "Layer name"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect"); + 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); + RNA_def_property_ui_text(prop, "Pass", "Pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYER); + RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_MATERIAL); + RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "layer_pass"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Layer pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYERPASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + RNA_define_lib_overridable(false); +} + void RNA_def_greasepencil_modifier(BlenderRNA *brna) { StructRNA *srna; @@ -3352,6 +3555,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna) rna_def_modifier_gpencilweight(brna); rna_def_modifier_gpencillineart(brna); rna_def_modifier_gpencillength(brna); + rna_def_modifier_gpencildash(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index e44ddb07d53..4a013dc9bd7 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -404,7 +404,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0][IMA_TEXTURE_RESOLUTION_FULL]; return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 144950235c8..8d0d3adab8b 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -173,12 +173,6 @@ static void rna_Material_active_paint_texture_index_update(Main *bmain, continue; } - Object *obedit = NULL; - { - ViewLayer *view_layer = WM_window_get_active_view_layer(win); - obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - } - ScrArea *area; for (area = screen->areabase.first; area; area = area->next) { SpaceLink *sl; @@ -186,7 +180,7 @@ static void rna_Material_active_paint_texture_index_update(Main *bmain, if (sl->spacetype == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)sl; if (!sima->pin) { - ED_space_image_set(bmain, sima, obedit, image, true); + ED_space_image_set(bmain, sima, image, true); } } } diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index f11d845c582..c99dfea524f 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -755,6 +755,7 @@ RNA_MOD_VGROUP_NAME_SET(LaplacianDeform, anchor_grp_name); RNA_MOD_VGROUP_NAME_SET(LaplacianSmooth, defgrp_name); RNA_MOD_VGROUP_NAME_SET(Lattice, name); RNA_MOD_VGROUP_NAME_SET(Mask, vgroup); +RNA_MOD_VGROUP_NAME_SET(MeshCache, defgrp_name); RNA_MOD_VGROUP_NAME_SET(MeshDeform, defgrp_name); RNA_MOD_VGROUP_NAME_SET(NormalEdit, defgrp_name); RNA_MOD_VGROUP_NAME_SET(Shrinkwrap, vgroup_name); @@ -1600,51 +1601,6 @@ static bool rna_Modifier_show_expanded_get(PointerRNA *ptr) return md->ui_expand_flag & UI_PANEL_DATA_EXPAND_ROOT; } -static int rna_MeshSequenceCacheModifier_has_velocity_get(PointerRNA *ptr) -{ -# ifdef WITH_ALEMBIC - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; - return ABC_has_vec3_array_property_named(mcmd->reader, mcmd->cache_file->velocity_name); -# else - return false; - UNUSED_VARS(ptr); -# endif -} - -static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr) -{ -# ifdef WITH_ALEMBIC - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; - - if (mcmd->num_vertices == 0) { - return 0; - } - - if (mcmd->vertex_velocities) { - MEM_freeN(mcmd->vertex_velocities); - } - - mcmd->vertex_velocities = MEM_mallocN(sizeof(MeshCacheVertexVelocity) * mcmd->num_vertices, - "Mesh Cache Velocities"); - - int num_read = ABC_read_velocity_cache(mcmd->reader, - mcmd->cache_file->velocity_name, - mcmd->last_lookup_time, - mcmd->velocity_scale * mcmd->velocity_delta, - mcmd->num_vertices, - (float *)mcmd->vertex_velocities); - - if (num_read == -1 || num_read != mcmd->num_vertices) { - return false; - } - - return true; -# else - return false; - UNUSED_VARS(ptr); -# endif -} - static bool rna_NodesModifier_node_group_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { bNodeTree *ntree = value.data; @@ -6045,6 +6001,20 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Influence", "Influence of the deformation"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "defgrp_name"); + RNA_def_property_ui_text( + prop, + "Vertex Group", + "Name of the Vertex Group which determines the influence of the modifier per point"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshCacheModifier_defgrp_name_set"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_MESHCACHE_INVERT_VERTEX_GROUP); + RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + /* -------------------------------------------------------------------- */ /* Axis Conversion */ prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE); @@ -6103,22 +6073,6 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) RNA_define_lib_overridable(false); } -static void rna_def_mesh_cache_velocities(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "MeshCacheVertexVelocity", NULL); - RNA_def_struct_ui_text(srna, "Mesh Cache Velocity", "Velocity attribute of an Alembic mesh"); - RNA_def_struct_ui_icon(srna, ICON_VERTEXSEL); - - prop = RNA_def_property(srna, "velocity", PROP_FLOAT, PROP_VELOCITY); - RNA_def_property_array(prop, 3); - RNA_def_property_float_sdna(prop, NULL, "vel"); - RNA_def_property_ui_text(prop, "Velocity", ""); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); -} - static void rna_def_modifier_meshseqcache(BlenderRNA *brna) { StructRNA *srna; @@ -6175,26 +6129,6 @@ static void rna_def_modifier_meshseqcache(BlenderRNA *brna) "Multiplier used to control the magnitude of the velocity vectors for time effects"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); - /* -------------------------- Velocity Vectors -------------------------- */ - - prop = RNA_def_property(srna, "vertex_velocities", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "vertex_velocities", "num_vertices"); - RNA_def_property_struct_type(prop, "MeshCacheVertexVelocity"); - RNA_def_property_ui_text( - prop, "Fluid Mesh Vertices", "Vertices of the fluid mesh generated by simulation"); - - rna_def_mesh_cache_velocities(brna); - - prop = RNA_def_property(srna, "has_velocity", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_ui_text(prop, "Has Velocity Cache", ""); - RNA_def_property_boolean_funcs(prop, "rna_MeshSequenceCacheModifier_has_velocity_get", NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - - prop = RNA_def_property(srna, "read_velocity", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_ui_text(prop, "Read Velocity Cache", ""); - RNA_def_property_boolean_funcs(prop, "rna_MeshSequenceCacheModifier_read_velocity_get", NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_define_lib_overridable(false); } diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index a32ed49ae1e..4344c6dbb65 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -140,7 +140,7 @@ static void rna_def_movieclip_proxy(BlenderRNA *brna) PropertyRNA *prop; static const EnumPropertyItem clip_tc_items[] = { - {IMB_TC_NONE, "NONE", 0, "No TC in use", ""}, + {IMB_TC_NONE, "NONE", 0, "None", ""}, {IMB_TC_RECORD_RUN, "RECORD_RUN", 0, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 8d672e9b570..86e134799aa 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9295,6 +9295,11 @@ static void def_geo_point_instance(StructRNA *srna) ICON_NONE, "Collection", "Instance an entire collection on all points"}, + {GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY, + "GEOMETRY", + ICON_NONE, + "Geometry", + "Copy geometry to all points"}, {0, NULL, 0, NULL, NULL}, }; @@ -10086,6 +10091,12 @@ static void def_geo_curve_resample(StructRNA *srna) PropertyRNA *prop; static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_SAMPLE_EVALUATED, + "EVALUATED", + 0, + "Evaluated", + "Output the input spline's evaluated points, based on the resolution attribute for NURBS " + "and Bezier splines. Poly splines are unchanged"}, {GEO_NODE_CURVE_SAMPLE_COUNT, "COUNT", 0, @@ -10261,6 +10272,44 @@ static void def_geo_raycast(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_fill(StructRNA *srna) +{ + static const EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_FILL_MODE_TRIANGULATED, "TRIANGLES", 0, "Triangles", ""}, + {GEO_NODE_CURVE_FILL_MODE_NGONS, "NGONS", 0, "N-gons", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveFill", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_attribute_capture(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeCapture", "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_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to store the data in"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index d3cd3158db1..99865078cbe 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -224,6 +224,12 @@ static EnumPropertyItem instance_items_empty[] = { INSTANCE_ITEM_COLLECTION, {0, NULL, 0, NULL, NULL}, }; + +static EnumPropertyItem instance_items_font[] = { + {0, "NONE", 0, "None", ""}, + {OB_DUPLIVERTS, "VERTS", 0, "Vertices", "Use Object Font on characters"}, + {0, NULL, 0, NULL, NULL}, +}; #endif #undef INSTANCE_ITEMS_SHARED #undef INSTANCE_ITEM_COLLECTION @@ -762,6 +768,9 @@ static const EnumPropertyItem *rna_Object_instance_type_itemf(bContext *UNUSED(C else if (ob->type == OB_POINTCLOUD) { item = instance_items_pointcloud; } + else if (ob->type == OB_FONT) { + item = instance_items_font; + } else { item = instance_items_nogroup; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 0583cdeb1bb..badaaa14aa4 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2845,7 +2845,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) "IMAGE", ICON_IMAGE_DATA, "Image", - "Strick stroke to the image"}, + "Stick stroke to the image"}, /* Weird, GP_PROJECT_VIEWALIGN is inverted. */ {0, "VIEW", ICON_RESTRICT_VIEW_ON, "View", "Stick stroke to the view"}, {0, NULL, 0, NULL, NULL}, @@ -4597,12 +4597,12 @@ void rna_def_freestyle_settings(BlenderRNA *brna) {FREESTYLE_CONTROL_SCRIPT_MODE, "SCRIPT", 0, - "Python Scripting Mode", + "Python Scripting", "Advanced mode for using style modules written in Python"}, {FREESTYLE_CONTROL_EDITOR_MODE, "EDITOR", 0, - "Parameter Editor Mode", + "Parameter Editor", "Basic mode for interactive style parameter editing"}, {0, NULL, 0, NULL, NULL}, }; @@ -4613,7 +4613,7 @@ void rna_def_freestyle_settings(BlenderRNA *brna) {FREESTYLE_QI_RANGE, "RANGE", 0, - "QI Range", + "Quantitative Invisibility", "Select feature edges within a range of quantitative invisibility (QI) values"}, {0, NULL, 0, NULL, NULL}, }; @@ -4930,14 +4930,6 @@ void rna_def_freestyle_settings(BlenderRNA *brna) prop, "Face Smoothness", "Take face smoothness into account in view map calculation"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); - prop = RNA_def_property(srna, "use_advanced_options", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", FREESTYLE_ADVANCED_OPTIONS_FLAG); - RNA_def_property_ui_text( - prop, - "Advanced Options", - "Enable advanced edge detection options (sphere radius and Kr derivative epsilon)"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); - prop = RNA_def_property(srna, "use_view_map_cache", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FREESTYLE_VIEW_MAP_CACHE); RNA_def_property_ui_text( @@ -4957,11 +4949,13 @@ void rna_def_freestyle_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "sphere_radius", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "sphere_radius"); + RNA_def_property_float_default(prop, 1.0); RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_ui_text(prop, "Sphere Radius", "Sphere radius for computing curvatures"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); prop = RNA_def_property(srna, "kr_derivative_epsilon", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_default(prop, 0.0); RNA_def_property_float_sdna(prop, NULL, "dkr_epsilon"); RNA_def_property_range(prop, -1000.0, 1000.0); 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 fc6b076939e..6bf9058a40d 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -507,7 +507,6 @@ static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA *UNUSED(ptr)) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = OBACT(view_layer); - Object *obedit = OBEDIT_FROM_OBACT(ob); bScreen *screen; Image *ima = scene->toolsettings->imapaint.canvas; @@ -520,7 +519,7 @@ static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA *UNUSED(ptr)) SpaceImage *sima = (SpaceImage *)slink; if (!sima->pin) { - ED_space_image_set(bmain, sima, obedit, ima, true); + ED_space_image_set(bmain, sima, ima, true); } } } diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 9dee7bc841c..cd87e4d10c1 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -106,7 +106,7 @@ typedef struct SequenceSearchData { static void rna_SequenceElement_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { StripElem *se = (StripElem *)ptr->data; @@ -125,7 +125,7 @@ static void rna_Sequence_invalidate_raw_update(Main *UNUSED(bmain), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { Sequence *seq = (Sequence *)ptr->data; @@ -139,7 +139,7 @@ static void rna_Sequence_invalidate_preprocessed_update(Main *UNUSED(bmain), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { Sequence *seq = (Sequence *)ptr->data; @@ -153,7 +153,7 @@ static void UNUSED_FUNCTION(rna_Sequence_invalidate_composite_update)(Main *UNUS PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { Sequence *seq = (Sequence *)ptr->data; @@ -175,7 +175,7 @@ static void rna_Sequence_use_sequence(Main *bmain, Scene *scene, PointerRNA *ptr rna_Sequence_invalidate_raw_update(bmain, scene, ptr); /* Changing recursion changes set of IDs which needs to be remapped by the copy-on-write. * the only way for this currently is to tag the ID for ID_RECALC_COPY_ON_WRITE. */ - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed) { Sequence *seq = (Sequence *)ptr->data; if (seq->scene != NULL) { @@ -192,7 +192,7 @@ static void rna_SequenceEditor_sequences_all_begin(CollectionPropertyIterator *i PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); SeqCollection *all_strips = SEQ_query_all_strips_recursive(&ed->seqbase); BLI_Iterator *bli_iter = MEM_callocN(sizeof(BLI_Iterator), __func__); @@ -286,7 +286,7 @@ static void rna_Sequence_views_format_update(Main *bmain, Scene *scene, PointerR static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); Sequence *tseq; SEQ_time_update_sequence_bounds(scene, seq); @@ -474,7 +474,7 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value) { Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); /* check channel increment or decrement */ @@ -522,7 +522,7 @@ static Sequence *sequence_get_by_transform(Editing *ed, StripTransform *transfor static char *rna_SequenceTransform_path(PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_transform(ed, ptr->data); if (seq && seq->name + 2) { @@ -541,7 +541,7 @@ static void rna_SequenceTransform_update(Main *UNUSED(bmain), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_transform(ed, ptr->data); SEQ_relations_invalidate_cache_preprocessed(scene, seq); @@ -574,7 +574,7 @@ static Sequence *sequence_get_by_crop(Editing *ed, StripCrop *crop) static char *rna_SequenceCrop_path(PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_crop(ed, ptr->data); if (seq && seq->name + 2) { @@ -591,7 +591,7 @@ static char *rna_SequenceCrop_path(PointerRNA *ptr) static void rna_SequenceCrop_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_crop(ed, ptr->data); SEQ_relations_invalidate_cache_preprocessed(scene, seq); @@ -845,6 +845,17 @@ static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *scene, Pointer DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); } +static void rna_Sequence_pan_range( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + Scene *scene = (Scene *)ptr->owner_id; + + *min = -FLT_MAX; + *max = FLT_MAX; + *softmax = 1 + (int)(scene->r.ffcodecdata.audio_channels > 2); + *softmin = -*softmax; +} + static int rna_Sequence_input_count_get(PointerRNA *ptr) { Sequence *seq = (Sequence *)(ptr->data); @@ -910,7 +921,7 @@ static void rna_SequenceElement_filename_set(PointerRNA *ptr, const char *value) static void rna_Sequence_reopen_files_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); SEQ_relations_free_imbuf(scene, &ed->seqbase, false); rna_Sequence_invalidate_raw_update(bmain, scene, ptr); @@ -960,7 +971,7 @@ static Sequence *sequence_get_by_proxy(Editing *ed, StripProxy *proxy) static void rna_Sequence_tcindex_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_proxy(ed, ptr->data); SEQ_add_reload_new_file(bmain, scene, seq, false); @@ -970,7 +981,7 @@ static void rna_Sequence_tcindex_update(Main *bmain, Scene *UNUSED(scene), Point static void rna_SequenceProxy_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_proxy(ed, ptr->data); SEQ_relations_invalidate_cache_preprocessed(scene, seq); } @@ -1029,7 +1040,7 @@ static char *rna_SequenceColorBalance_path(PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; SequenceModifierData *smd; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_colorbalance(ed, ptr->data, &smd); if (seq && seq->name + 2) { @@ -1061,7 +1072,7 @@ static void rna_SequenceColorBalance_update(Main *UNUSED(bmain), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); SequenceModifierData *smd; Sequence *seq = sequence_get_by_colorbalance(ed, ptr->data, &smd); @@ -1071,7 +1082,7 @@ static void rna_SequenceColorBalance_update(Main *UNUSED(bmain), static void rna_SequenceEditor_overlay_lock_set(PointerRNA *ptr, bool value) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return; @@ -1091,7 +1102,7 @@ static void rna_SequenceEditor_overlay_lock_set(PointerRNA *ptr, bool value) static int rna_SequenceEditor_overlay_frame_get(PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return scene->r.cfra; @@ -1108,7 +1119,7 @@ static int rna_SequenceEditor_overlay_frame_get(PointerRNA *ptr) static void rna_SequenceEditor_overlay_frame_set(PointerRNA *ptr, int value) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return; @@ -1172,7 +1183,7 @@ static StructRNA *rna_SequenceModifier_refine(struct PointerRNA *ptr) static char *rna_SequenceModifier_path(PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); SequenceModifierData *smd = ptr->data; Sequence *seq = sequence_get_by_modifier(ed, smd); @@ -1194,7 +1205,7 @@ static void rna_SequenceModifier_name_set(PointerRNA *ptr, const char *value) { SequenceModifierData *smd = ptr->data; Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_modifier(ed, smd); AnimData *adt; char oldname[sizeof(smd->name)]; @@ -1226,7 +1237,7 @@ static void rna_SequenceModifier_update(Main *UNUSED(bmain), Scene *UNUSED(scene { /* strip from other scenes could be modified, so using active scene is not reliable */ Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_modifier(ed, ptr->data); SEQ_relations_invalidate_cache_preprocessed(scene, seq); @@ -1235,7 +1246,7 @@ static void rna_SequenceModifier_update(Main *UNUSED(bmain), Scene *UNUSED(scene static bool rna_SequenceModifier_otherSequence_poll(PointerRNA *ptr, PointerRNA value) { Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_modifier(ed, ptr->data); Sequence *cur = (Sequence *)value.data; @@ -1304,7 +1315,7 @@ static void rna_SequenceModifier_strip_set(PointerRNA *ptr, { SequenceModifierData *smd = ptr->data; Scene *scene = (Scene *)ptr->owner_id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_modifier(ed, smd); Sequence *target = (Sequence *)value.data; @@ -1440,7 +1451,7 @@ static void rna_def_strip_proxy(BlenderRNA *brna) PropertyRNA *prop; static const EnumPropertyItem seq_tc_items[] = { - {SEQ_PROXY_TC_NONE, "NONE", 0, "No TC in use", ""}, + {SEQ_PROXY_TC_NONE, "NONE", 0, "None", ""}, {SEQ_PROXY_TC_RECORD_RUN, "RECORD_RUN", 0, @@ -2559,8 +2570,10 @@ static void rna_def_sound(BlenderRNA *brna) prop = RNA_def_property(srna, "pan", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "pan"); - RNA_def_property_range(prop, -2.0f, 2.0f); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -2, 2, 1, 2); RNA_def_property_ui_text(prop, "Pan", "Playback panning of the sound (only for Mono sources)"); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_Sequence_pan_range"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_audio_update"); prop = RNA_def_property(srna, "show_waveform", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 7ddbf450e6a..a0564d3435b 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -105,7 +105,7 @@ static Sequence *rna_Sequence_split( ID *id, Sequence *seq, Main *bmain, ReportList *reports, int frame, int split_method) { Scene *scene = (Scene *)id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); const char *error_msg = NULL; @@ -127,7 +127,7 @@ static Sequence *rna_Sequence_split( static Sequence *rna_Sequence_parent_meta(ID *id, Sequence *seq_self) { Scene *scene = (Scene *)id; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); return SEQ_find_metastrip_by_sequence(&ed->seqbase, NULL, seq_self); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 5ab13e7b44e..e3985b26eb0 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1622,14 +1622,7 @@ static void rna_SpaceImageEditor_image_set(PointerRNA *ptr, { BLI_assert(BKE_id_is_in_global_main(value.data)); SpaceImage *sima = ptr->data; - bScreen *screen = (bScreen *)ptr->owner_id; - Object *obedit = NULL; - wmWindow *win = ED_screen_window_find(screen, G_MAIN->wm.first); - if (win != NULL) { - ViewLayer *view_layer = WM_window_get_active_view_layer(win); - obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - } - ED_space_image_set(G_MAIN, sima, obedit, (Image *)value.data, false); + ED_space_image_set(G_MAIN, sima, (Image *)value.data, false); } static void rna_SpaceImageEditor_mask_set(PointerRNA *ptr, @@ -1803,6 +1796,16 @@ static const EnumPropertyItem *rna_SpaceImageEditor_pivot_itemf(bContext *UNUSED } } +static void rna_SpaceUVEditor_tile_grid_shape_set(PointerRNA *ptr, const int *values) +{ + SpaceImage *data = (SpaceImage *)(ptr->data); + + int clamp[2] = {10, 100}; + for (int i = 0; i < 2; i++) { + data->tile_grid_shape[i] = CLAMPIS(values[i], 1, clamp[i]); + } +} + /* Space Text Editor */ static void rna_SpaceTextEditor_word_wrap_set(PointerRNA *ptr, bool value) @@ -2281,7 +2284,7 @@ static void seq_build_proxy(bContext *C, PointerRNA *ptr) SpaceSeq *sseq = ptr->data; Scene *scene = CTX_data_scene(C); - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene, false)); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); GSet *file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); wmJob *wm_job = ED_seq_proxy_wm_job_get(C); @@ -2615,6 +2618,18 @@ static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr) return (int)strlen(entry->name); } +static void rna_FileBrowser_FileSelectEntry_relative_path_get(PointerRNA *ptr, char *value) +{ + const FileDirEntry *entry = ptr->data; + strcpy(value, entry->relpath); +} + +static int rna_FileBrowser_FileSelectEntry_relative_path_length(PointerRNA *ptr) +{ + const FileDirEntry *entry = ptr->data; + return (int)strlen(entry->relpath); +} + static const EnumPropertyItem *rna_FileBrowser_FileSelectEntry_id_type_itemf( bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) { @@ -3412,7 +3427,8 @@ static void rna_def_space_image_uv(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "tile_grid_shape"); RNA_def_property_array(prop, 2); RNA_def_property_int_default(prop, 1); - RNA_def_property_range(prop, 1, 10); + RNA_def_property_range(prop, 1, 100); + RNA_def_property_int_funcs(prop, NULL, "rna_SpaceUVEditor_tile_grid_shape_set", NULL); RNA_def_property_ui_text( prop, "Tile Grid Shape", "How many tiles will be shown in the background"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); @@ -4925,18 +4941,19 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_rotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_LOCK_ROTATION); - RNA_def_property_ui_text(prop, "Lock", "Lock view rotation in side views"); + RNA_def_property_ui_text( + prop, "Lock Rotation", "Lock view rotation of side views to Top/Front/Right"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_RegionView3D_quadview_update"); prop = RNA_def_property(srna, "show_sync_view", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_BOXVIEW); - RNA_def_property_ui_text(prop, "Box", "Sync view position between side views"); + RNA_def_property_ui_text(prop, "Sync Zoom/Pan", "Sync view position between side views"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_RegionView3D_quadview_update"); prop = RNA_def_property(srna, "use_box_clip", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_BOXCLIP); RNA_def_property_ui_text( - prop, "Clip", "Clip objects based on what's visible in other side views"); + prop, "Clip Contents", "Clip view contents based on what is visible in other side views"); RNA_def_property_update( prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_RegionView3D_quadview_clip_update"); @@ -6191,6 +6208,17 @@ static void rna_def_fileselect_entry(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_struct_name_property(srna, prop); + prop = RNA_def_property(srna, "relative_path", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_FileBrowser_FileSelectEntry_relative_path_get", + "rna_FileBrowser_FileSelectEntry_relative_path_length", + NULL); + RNA_def_property_ui_text(prop, + "Relative Path", + "Path relative to the directory currently displayed in the File " + "Browser (includes the file name)"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "id_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_id_type_items); RNA_def_property_enum_funcs(prop, diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index e06cc39a88b..f96b3fc5eee 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -622,6 +622,7 @@ static void rna_uiTemplateAssetView(uiLayout *layout, PointerRNA *active_dataptr, const char *active_propname, int filter_id_types, + int display_flags, const char *activate_opname, PointerRNA *r_activate_op_properties, const char *drag_opname, @@ -630,6 +631,7 @@ static void rna_uiTemplateAssetView(uiLayout *layout, AssetFilterSettings filter_settings = { .id_types = filter_id_types ? filter_id_types : FILTER_ID_ALL, }; + uiTemplateAssetView(layout, C, list_id, @@ -640,6 +642,7 @@ static void rna_uiTemplateAssetView(uiLayout *layout, active_dataptr, active_propname, &filter_settings, + display_flags, activate_opname, r_activate_op_properties, drag_opname, @@ -878,6 +881,25 @@ void RNA_api_ui_layout(StructRNA *srna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem asset_view_template_options[] = { + {UI_TEMPLATE_ASSET_DRAW_NO_NAMES, + "NO_NAMES", + 0, + "", + "Do not display the name of each asset underneath preview images"}, + {UI_TEMPLATE_ASSET_DRAW_NO_FILTER, + "NO_FILTER", + 0, + "", + "Do not display buttons for filtering the available assets"}, + {UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY, + "NO_LIBRARY", + 0, + "", + "Do not display buttons to choose or refresh an asset library"}, + {0, NULL, 0, NULL, NULL}, + }; + static float node_socket_color_default[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* simple layout specifiers */ @@ -1839,6 +1861,12 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_property_enum_items(parm, DummyRNA_NULL_items); RNA_def_property_enum_funcs(parm, NULL, NULL, "rna_uiTemplateAssetView_filter_id_types_itemf"); RNA_def_property_flag(parm, PROP_ENUM_FLAG); + RNA_def_enum_flag(func, + "display_options", + asset_view_template_options, + 0, + "", + "Displaying options for the asset view"); RNA_def_string(func, "activate_operator", NULL, diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 73811924c23..563c6ea35e0 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -559,6 +559,11 @@ static PointerRNA rna_UserDef_system_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_PreferencesSystem, ptr->data); } +static PointerRNA rna_UserDef_apps_get(PointerRNA *ptr) +{ + return rna_pointer_inherit_refine(ptr, &RNA_PreferencesApps, ptr->data); +} + static void rna_UserDef_audio_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) { BKE_sound_init(bmain); @@ -4590,12 +4595,6 @@ static void rna_def_userdef_view(BlenderRNA *brna) "Color range used for weight visualization in weight painting mode"); RNA_def_property_update(prop, 0, "rna_UserDef_weight_color_update"); - prop = RNA_def_property(srna, "show_layout_ui", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_UI_LAYOUT); - RNA_def_property_ui_text( - prop, "Editor Corner Splitting", "Split and join editors by dragging from corners"); - RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); - prop = RNA_def_property(srna, "show_navigate_ui", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_SHOW_GIZMO_NAVIGATE); RNA_def_property_ui_text( @@ -6059,6 +6058,14 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem preview_type_items[] = { + {USER_FILE_PREVIEW_NONE, "NONE", 0, "None", "Do not create blend previews"}, + {USER_FILE_PREVIEW_AUTO, "AUTO", 0, "Auto", "Automatically select best preview type"}, + {USER_FILE_PREVIEW_SCREENSHOT, "SCREENSHOT", 0, "Screenshot", "Capture the entire window"}, + {USER_FILE_PREVIEW_CAMERA, "CAMERA", 0, "Camera View", "Workbench render of scene"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "PreferencesFilePaths", NULL); RNA_def_struct_sdna(srna, "UserDef"); RNA_def_struct_nested(brna, srna, "Preferences"); @@ -6066,26 +6073,24 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "File Paths", "Default paths for external files"); prop = RNA_def_property(srna, "show_hidden_files_datablocks", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_HIDE_DOT); + RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_HIDE_DOT); RNA_def_property_ui_text(prop, - "Hide Dot Files/Data-Blocks", - "Hide files and data-blocks if their name start with a dot (.*)"); + "Show Hidden Files/Data-Blocks", + "Show files and data-blocks that are normally hidden"); prop = RNA_def_property(srna, "use_filter_files", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_FILTERFILEEXTS); - RNA_def_property_ui_text(prop, - "Filter File Extensions", - "Display only files with extensions in the image select window"); + RNA_def_property_ui_text(prop, "Filter Files", "Enable filtering of files in the File Browser"); - prop = RNA_def_property(srna, "hide_recent_locations", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_HIDE_RECENT); + prop = RNA_def_property(srna, "show_recent_locations", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_HIDE_RECENT); RNA_def_property_ui_text( - prop, "Hide Recent Locations", "Hide recent locations in the file selector"); + prop, "Show Recent Locations", "Show Recent locations list in the File Browser"); - prop = RNA_def_property(srna, "hide_system_bookmarks", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_HIDE_SYSTEM_BOOKMARKS); + prop = RNA_def_property(srna, "show_system_bookmarks", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_HIDE_SYSTEM_BOOKMARKS); RNA_def_property_ui_text( - prop, "Hide System Bookmarks", "Hide system bookmarks in the file selector"); + prop, "Show System Locations", "Show System locations list in the File Browser"); prop = RNA_def_property(srna, "use_relative_paths", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_RELPATHS); @@ -6214,12 +6219,9 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Recent Files", "Maximum number of recently opened files to remember"); - prop = RNA_def_property(srna, "use_save_preview_images", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_SAVE_PREVIEWS); - RNA_def_property_ui_text(prop, - "Save Preview Images", - "Enables automatic saving of preview images in the .blend file " - "as well as a thumbnail of the .blend"); + prop = RNA_def_property(srna, "file_preview_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, preview_type_items); + RNA_def_property_ui_text(prop, "File Preview Type", "What type of blend preview to create"); rna_def_userdef_filepaths_asset_library(brna); @@ -6228,6 +6230,35 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Asset Libraries", ""); } +static void rna_def_userdef_apps(BlenderRNA *brna) +{ + PropertyRNA *prop; + StructRNA *srna; + + srna = RNA_def_struct(brna, "PreferencesApps", NULL); + RNA_def_struct_sdna(srna, "UserDef"); + RNA_def_struct_nested(brna, srna, "Preferences"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Apps", "Preferences that work only for apps"); + + prop = RNA_def_property(srna, "show_corner_split", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_CORNER_SPLIT); + RNA_def_property_ui_text( + prop, "Corner Splitting", "Split and join editors by dragging from corners"); + RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); + + prop = RNA_def_property(srna, "show_edge_resize", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_EDGE_RESIZE); + RNA_def_property_ui_text(prop, "Edge Resize", "Resize editors by dragging from the edges"); + RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); + + prop = RNA_def_property(srna, "show_regions_visibility_toggle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_HIDE_REGION_TOGGLE); + RNA_def_property_ui_text( + prop, "Regions Visibility Toggle", "Header and side bars visibility toggles"); + RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); +} + static void rna_def_userdef_experimental(BlenderRNA *brna) { StructRNA *srna; @@ -6296,6 +6327,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_override_templates", 1); RNA_def_property_ui_text( prop, "Override Templates", "Enable library override template in the python API"); + + prop = RNA_def_property(srna, "use_geometry_nodes_fields", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_fields", 1); + RNA_def_property_ui_text(prop, "Geometry Nodes Fields", "Enable field nodes in geometry nodes"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) @@ -6439,6 +6474,12 @@ void RNA_def_userdef(BlenderRNA *brna) RNA_def_property_ui_text( prop, "System & OpenGL", "Graphics driver and operating system settings"); + prop = RNA_def_property(srna, "apps", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "PreferencesApps"); + RNA_def_property_pointer_funcs(prop, "rna_UserDef_apps_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Apps", "Preferences that work only for apps"); + prop = RNA_def_property(srna, "experimental", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_struct_type(prop, "PreferencesExperimental"); @@ -6500,6 +6541,7 @@ void RNA_def_userdef(BlenderRNA *brna) rna_def_userdef_studiolights(brna); rna_def_userdef_studiolight(brna); rna_def_userdef_pathcompare(brna); + rna_def_userdef_apps(brna); rna_def_userdef_experimental(brna); USERDEF_TAG_DIRTY_PROPERTY_UPDATE_DISABLE; diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 21a3c087197..c2d1ac67675 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -440,8 +440,7 @@ const EnumPropertyItem rna_enum_event_type_mask_items[] = { static const EnumPropertyItem keymap_modifiers_items[] = { {KM_ANY, "ANY", 0, "Any", ""}, {0, "NONE", 0, "None", ""}, - {1, "FIRST", 0, "First", ""}, - {2, "SECOND", 0, "Second", ""}, + {KM_MOD_HELD, "HELD", 0, "Held", ""}, {0, NULL, 0, NULL, NULL}, }; #endif @@ -468,6 +467,13 @@ const EnumPropertyItem rna_enum_operator_type_flag_items[] = { "is enabled"}, {OPTYPE_GRAB_CURSOR_X, "GRAB_CURSOR_X", 0, "Grab Pointer X", "Grab, only warping the X axis"}, {OPTYPE_GRAB_CURSOR_Y, "GRAB_CURSOR_Y", 0, "Grab Pointer Y", "Grab, only warping the Y axis"}, + {OPTYPE_DEPENDS_ON_CURSOR, + "DEPENDS_ON_CURSOR", + 0, + "Depends on Cursor", + "The initial cursor location is used, " + "when running from a menus or buttons the user is prompted to place the cursor " + "before beginning the operation"}, {OPTYPE_PRESET, "PRESET", 0, "Preset", "Display a preset button with the operators settings"}, {OPTYPE_INTERNAL, "INTERNAL", 0, "Internal", "Removes the operator from search results"}, {0, NULL, 0, NULL, NULL}, @@ -2725,38 +2731,62 @@ static void rna_def_keyconfig(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Any", "Any modifier keys pressed"); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); - prop = RNA_def_property(srna, "shift", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "shift", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "shift"); + RNA_def_property_range(prop, KM_ANY, KM_MOD_HELD); + RNA_def_property_ui_text(prop, "Shift", "Shift key pressed, -1 for any state"); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_WINDOWMANAGER); + RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + + prop = RNA_def_property(srna, "ctrl", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "ctrl"); + RNA_def_property_range(prop, KM_ANY, KM_MOD_HELD); + RNA_def_property_ui_text(prop, "Ctrl", "Control key pressed, -1 for any state"); + RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + + prop = RNA_def_property(srna, "alt", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "alt"); + RNA_def_property_range(prop, KM_ANY, KM_MOD_HELD); + RNA_def_property_ui_text(prop, "Alt", "Alt key pressed, -1 for any state"); + RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + + prop = RNA_def_property(srna, "oskey", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "oskey"); + RNA_def_property_range(prop, KM_ANY, KM_MOD_HELD); + RNA_def_property_ui_text(prop, "OS Key", "Operating system key pressed, -1 for any state"); + RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + + /* XXX(@campbellbarton): the `*_ui` suffix is only for the UI, may be removed, + * since this is only exposed so the UI can show these settings as toggle-buttons. */ + prop = RNA_def_property(srna, "shift_ui", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "shift", 0); RNA_def_property_boolean_funcs(prop, "rna_KeyMapItem_shift_get", NULL); - /* RNA_def_property_enum_sdna(prop, NULL, "shift"); */ /* RNA_def_property_enum_items(prop, keymap_modifiers_items); */ RNA_def_property_ui_text(prop, "Shift", "Shift key pressed"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_WINDOWMANAGER); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); - prop = RNA_def_property(srna, "ctrl", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "ctrl_ui", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "ctrl", 0); RNA_def_property_boolean_funcs(prop, "rna_KeyMapItem_ctrl_get", NULL); - /* RNA_def_property_enum_sdna(prop, NULL, "ctrl"); */ /* RNA_def_property_enum_items(prop, keymap_modifiers_items); */ RNA_def_property_ui_text(prop, "Ctrl", "Control key pressed"); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); - prop = RNA_def_property(srna, "alt", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "alt_ui", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "alt", 0); RNA_def_property_boolean_funcs(prop, "rna_KeyMapItem_alt_get", NULL); - /* RNA_def_property_enum_sdna(prop, NULL, "alt"); */ /* RNA_def_property_enum_items(prop, keymap_modifiers_items); */ RNA_def_property_ui_text(prop, "Alt", "Alt key pressed"); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); - prop = RNA_def_property(srna, "oskey", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "oskey_ui", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "oskey", 0); RNA_def_property_boolean_funcs(prop, "rna_KeyMapItem_oskey_get", NULL); - /* RNA_def_property_enum_sdna(prop, NULL, "oskey"); */ /* RNA_def_property_enum_items(prop, keymap_modifiers_items); */ RNA_def_property_ui_text(prop, "OS Key", "Operating system key pressed"); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + /* End `_ui` modifiers. */ prop = RNA_def_property(srna, "key_modifier", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "keymodifier"); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index e123604cbe9..7c3b119abb9 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -39,6 +39,8 @@ #include "wm_cursors.h" #include "wm_event_types.h" +#include "WM_types.h" + #include "rna_internal.h" /* own include */ /* confusing 2 enums mixed up here */ @@ -216,49 +218,70 @@ static int rna_Operator_props_popup(bContext *C, wmOperator *op, wmEvent *event) return WM_operator_props_popup(C, op, event); } +static int keymap_item_modifier_flag_from_args(bool any, int shift, int ctrl, int alt, int oskey) +{ + int modifier = 0; + if (any) { + modifier = KM_ANY; + } + else { + if (shift == KM_MOD_HELD) { + modifier |= KM_SHIFT; + } + else if (shift == KM_ANY) { + modifier |= KM_SHIFT_ANY; + } + + if (ctrl == KM_MOD_HELD) { + modifier |= KM_CTRL; + } + else if (ctrl == KM_ANY) { + modifier |= KM_CTRL_ANY; + } + + if (alt == KM_MOD_HELD) { + modifier |= KM_ALT; + } + else if (alt == KM_ANY) { + modifier |= KM_ALT_ANY; + } + + if (oskey == KM_MOD_HELD) { + modifier |= KM_OSKEY; + } + else if (oskey == KM_ANY) { + modifier |= KM_OSKEY_ANY; + } + } + return modifier; +} + static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km, ReportList *reports, const char *idname, int type, int value, bool any, - bool shift, - bool ctrl, - bool alt, - bool oskey, + int shift, + int ctrl, + int alt, + int oskey, int keymodifier, bool repeat, bool head) { - // wmWindowManager *wm = CTX_wm_manager(C); - wmKeyMapItem *kmi = NULL; - char idname_bl[OP_MAX_TYPENAME]; - int modifier = 0; - /* only on non-modal maps */ if (km->flag & KEYMAP_MODAL) { BKE_report(reports, RPT_ERROR, "Not a non-modal keymap"); return NULL; } - WM_operator_bl_idname(idname_bl, idname); - - if (shift) { - modifier |= KM_SHIFT; - } - if (ctrl) { - modifier |= KM_CTRL; - } - if (alt) { - modifier |= KM_ALT; - } - if (oskey) { - modifier |= KM_OSKEY; - } + // wmWindowManager *wm = CTX_wm_manager(C); + wmKeyMapItem *kmi = NULL; + char idname_bl[OP_MAX_TYPENAME]; + const int modifier = keymap_item_modifier_flag_from_args(any, shift, ctrl, alt, oskey); - if (any) { - modifier = KM_ANY; - } + WM_operator_bl_idname(idname_bl, idname); /* create keymap item */ kmi = WM_keymap_add_item(km, idname_bl, type, value, modifier, keymodifier); @@ -305,39 +328,22 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km, int type, int value, bool any, - bool shift, - bool ctrl, - bool alt, - bool oskey, + int shift, + int ctrl, + int alt, + int oskey, int keymodifier, bool repeat) { - wmKeyMapItem *kmi = NULL; - int modifier = 0; - int propvalue = 0; - /* only modal maps */ if ((km->flag & KEYMAP_MODAL) == 0) { BKE_report(reports, RPT_ERROR, "Not a modal keymap"); return NULL; } - if (shift) { - modifier |= KM_SHIFT; - } - if (ctrl) { - modifier |= KM_CTRL; - } - if (alt) { - modifier |= KM_ALT; - } - if (oskey) { - modifier |= KM_OSKEY; - } - - if (any) { - modifier = KM_ANY; - } + wmKeyMapItem *kmi = NULL; + const int modifier = keymap_item_modifier_flag_from_args(any, shift, ctrl, alt, oskey); + int propvalue = 0; /* not initialized yet, do delayed lookup */ if (!km->modal_items) { @@ -641,7 +647,7 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, e.is_repeat = false; e.x = x; e.y = y; - /* NOTE: KM_MOD_FIRST, KM_MOD_SECOND aren't used anywhere, set as bools. */ + e.shift = shift; e.ctrl = ctrl; e.alt = alt; @@ -1131,10 +1137,10 @@ void RNA_api_keymapitems(StructRNA *srna) parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_boolean(func, "any", 0, "Any", ""); - RNA_def_boolean(func, "shift", 0, "Shift", ""); - RNA_def_boolean(func, "ctrl", 0, "Ctrl", ""); - RNA_def_boolean(func, "alt", 0, "Alt", ""); - RNA_def_boolean(func, "oskey", 0, "OS Key", ""); + RNA_def_int(func, "shift", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Shift", "", KM_ANY, KM_MOD_HELD); + RNA_def_int(func, "ctrl", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Ctrl", "", KM_ANY, KM_MOD_HELD); + RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD); + RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD); RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", ""); RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events"); RNA_def_boolean(func, @@ -1155,10 +1161,10 @@ void RNA_api_keymapitems(StructRNA *srna) parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_boolean(func, "any", 0, "Any", ""); - RNA_def_boolean(func, "shift", 0, "Shift", ""); - RNA_def_boolean(func, "ctrl", 0, "Ctrl", ""); - RNA_def_boolean(func, "alt", 0, "Alt", ""); - RNA_def_boolean(func, "oskey", 0, "OS Key", ""); + RNA_def_int(func, "shift", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Shift", "", KM_ANY, KM_MOD_HELD); + RNA_def_int(func, "ctrl", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Ctrl", "", KM_ANY, KM_MOD_HELD); + RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD); + RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD); RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", ""); RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events"); parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", "Added key map item"); diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index febb0e14e07..6a63723d174 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -1401,7 +1401,12 @@ static void rna_def_gizmogroup(BlenderRNA *brna) "SHOW_MODAL_ALL", 0, "Show Modal All", - "Show all while interacting"}, + "Show all while interacting, as well as this group when another is being interacted with"}, + {WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE, + "EXCLUDE_MODAL", + 0, + "Exclude Modal", + "Show all except this group while interacting"}, {WM_GIZMOGROUPTYPE_TOOL_INIT, "TOOL_INIT", 0, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 6a9c9715994..2f0f11ab56d 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -786,7 +786,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, * TODO: we may need to set other dirty flags as well? */ if (use_recalc_normals) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } if (vgroup_start_cap_remap) { diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index 8fdd222402e..add95a0d248 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -243,7 +243,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index bdb791dc8e7..c5d6902e1bc 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -161,7 +161,7 @@ static Mesh *get_quick_mesh( mul_m4_v3(omat, mv->co); } - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } break; @@ -506,7 +506,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } if (result == nullptr) { @@ -541,7 +541,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } } } diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index a344a15b0c1..6cd8d70383d 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -281,7 +281,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct MEM_freeN(faceMap); if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } /* TODO(sybren): also copy flags & tags? */ diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index d94e9a988c6..521a93b199f 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -170,10 +170,10 @@ static void deformVerts(ModifierData *md, mul_m4_v3(ob->obmat, collmd->x[i].co); } - collmd->xnew = MEM_dupallocN(collmd->x); // frame end position - collmd->current_x = MEM_dupallocN(collmd->x); // inter-frame - collmd->current_xnew = MEM_dupallocN(collmd->x); // inter-frame - collmd->current_v = MEM_dupallocN(collmd->x); // inter-frame + collmd->xnew = MEM_dupallocN(collmd->x); /* Frame end position. */ + collmd->current_x = MEM_dupallocN(collmd->x); /* Inter-frame. */ + collmd->current_xnew = MEM_dupallocN(collmd->x); /* Inter-frame. */ + collmd->current_v = MEM_dupallocN(collmd->x); /* Inter-frame. */ collmd->mvert_num = mvert_num; diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index faad1175f3a..56fcbbd8b7c 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -222,7 +222,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * TIMEIT_END(decim); #endif - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 82a6e169a7a..b21a536ad8a 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -115,7 +115,7 @@ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd) result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 306e79aa647..9a8af35109a 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -814,8 +814,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) } BKE_mesh_calc_edges_loose(result); - /* Tag to recalculate normals later. */ - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 6ef64ad8bc9..74f9887a973 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -36,8 +36,11 @@ #include "DNA_screen_types.h" #include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_wrapper.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -53,6 +56,7 @@ #include "MOD_meshcache_util.h" /* utility functions */ #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" +#include "MOD_util.h" static void initData(ModifierData *md) { @@ -84,11 +88,16 @@ static bool isDisabled(const struct Scene *UNUSED(scene), static void meshcache_do(MeshCacheModifierData *mcmd, Scene *scene, Object *ob, + Mesh *mesh, float (*vertexCos_Real)[3], int numVerts) { const bool use_factor = mcmd->factor < 1.0f; - float(*vertexCos_Store)[3] = (use_factor || + int influence_group_index; + MDeformVert *dvert; + MOD_get_vgroup(ob, mesh, mcmd->defgrp_name, &dvert, &influence_group_index); + + float(*vertexCos_Store)[3] = (use_factor || influence_group_index != -1 || (mcmd->deform_mode == MOD_MESHCACHE_DEFORM_INTEGRATE)) ? MEM_malloc_arrayN( numVerts, sizeof(*vertexCos_Store), __func__) : @@ -256,7 +265,29 @@ static void meshcache_do(MeshCacheModifierData *mcmd, if (vertexCos_Store) { if (ok) { - if (use_factor) { + if (influence_group_index != -1) { + const float global_factor = (mcmd->flag & MOD_MESHCACHE_INVERT_VERTEX_GROUP) ? + -mcmd->factor : + mcmd->factor; + const float global_offset = (mcmd->flag & MOD_MESHCACHE_INVERT_VERTEX_GROUP) ? + mcmd->factor : + 0.0f; + if (mesh->dvert != NULL) { + for (int i = 0; i < numVerts; i++) { + /* For each vertex, compute its blending factor between the mesh cache (for `fac = 0`) + * and the former position of the vertex (for `fac = 1`). */ + const MDeformVert *currentIndexDVert = dvert + i; + const float local_vertex_fac = global_offset + + BKE_defvert_find_weight(currentIndexDVert, + influence_group_index) * + global_factor; + interp_v3_v3v3( + vertexCos_Real[i], vertexCos_Real[i], vertexCos_Store[i], local_vertex_fac); + } + } + } + else if (use_factor) { + /* Influence_group_index is -1. */ interp_vn_vn(*vertexCos_Real, *vertexCos_Store, mcmd->factor, numVerts * 3); } else { @@ -270,34 +301,59 @@ static void meshcache_do(MeshCacheModifierData *mcmd, static void deformVerts(ModifierData *md, const ModifierEvalContext *ctx, - Mesh *UNUSED(mesh), + Mesh *mesh, float (*vertexCos)[3], int numVerts) { MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md; Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - meshcache_do(mcmd, scene, ctx->object, vertexCos, numVerts); + Mesh *mesh_src = NULL; + + if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') { + /* `mesh_src` is only needed for vertex groups. */ + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, numVerts, false, false); + } + meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, numVerts); + + if (!ELEM(mesh_src, NULL, mesh)) { + BKE_id_free(NULL, mesh_src); + } } static void deformVertsEM(ModifierData *md, const ModifierEvalContext *ctx, - struct BMEditMesh *UNUSED(editData), - Mesh *UNUSED(mesh), + struct BMEditMesh *editData, + Mesh *mesh, float (*vertexCos)[3], int numVerts) { MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md; Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - meshcache_do(mcmd, scene, ctx->object, vertexCos, numVerts); + Mesh *mesh_src = NULL; + + if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') { + /* `mesh_src` is only needed for vertex groups. */ + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, numVerts, false, false); + } + if (mesh_src != NULL) { + BKE_mesh_wrapper_ensure_mdata(mesh_src); + } + + meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, numVerts); + + if (!ELEM(mesh_src, NULL, mesh)) { + BKE_id_free(NULL, mesh_src); + } } static void panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); uiLayoutSetPropSep(layout, true); @@ -307,6 +363,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "factor", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(layout, ptr, "deform_mode", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "interpolation", 0, NULL, ICON_NONE); + modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 259c1cb2417..bcaf294ec8b 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -105,10 +105,6 @@ static void freeData(ModifierData *md) mcmd->reader_object_path[0] = '\0'; BKE_cachefile_reader_free(mcmd->cache_file, &mcmd->reader); } - - if (mcmd->vertex_velocities) { - MEM_freeN(mcmd->vertex_velocities); - } } static bool isDisabled(const struct Scene *UNUSED(scene), @@ -233,11 +229,26 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * Mesh *result = NULL; switch (cache_file->type) { - case CACHEFILE_TYPE_ALEMBIC: + case CACHEFILE_TYPE_ALEMBIC: { # ifdef WITH_ALEMBIC - result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag); + /* Time (in frames or seconds) between two velocity samples. Automatically computed to + * scale the velocity vectors at render time for generating proper motion blur data. */ + float velocity_scale = mcmd->velocity_scale; + if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_FRAME) { + velocity_scale *= FPS; + } + + result = ABC_read_mesh(mcmd->reader, + ctx->object, + mesh, + time, + &err_str, + mcmd->read_flag, + mcmd->cache_file->velocity_name, + velocity_scale); # endif break; + } case CACHEFILE_TYPE_USD: # ifdef WITH_USD result = USD_read_mesh( @@ -248,17 +259,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * break; } - mcmd->velocity_delta = 1.0f; - if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_SECOND) { - mcmd->velocity_delta /= FPS; - } - - mcmd->last_lookup_time = time; - - if (result != NULL) { - mcmd->num_vertices = result->totvert; - } - if (err_str) { BKE_modifier_set_error(ctx->object, md, "%s", err_str); } diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index 6116cf8146a..7fd90c71c9f 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -117,7 +117,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = mirrorModifier__doMirror(mmd, ctx->object, mesh); if (result != mesh) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } return result; } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 9d8630b21e7..6b976b016e1 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -68,6 +68,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "WM_types.h" + #include "RNA_access.h" #include "RNA_enum_types.h" @@ -85,8 +87,10 @@ #include "NOD_geometry.h" #include "NOD_geometry_nodes_eval_log.hh" +#include "FN_field.hh" #include "FN_multi_function.hh" +using blender::ColorGeometry4f; using blender::destruct_ptr; using blender::float3; using blender::FunctionRef; @@ -289,6 +293,17 @@ static bool logging_enabled(const ModifierEvalContext *ctx) return true; } +static const std::string use_attribute_suffix = "_use_attribute"; +static const std::string attribute_name_suffix = "_attribute_name"; + +/** + * \return Whether using an attribute to input values of this type is supported. + */ +static bool socket_type_has_attribute_toggle(const bNodeSocket &socket) +{ + return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT); +} + static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) { switch (socket.type) { @@ -329,8 +344,24 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) ui_data->max = ui_data->soft_max = (double)value->max; ui_data->default_array = (double *)MEM_mallocN(sizeof(double[3]), "mod_prop_default"); ui_data->default_array_len = 3; - for (int i = 3; i < 3; i++) { - ui_data->default_array[i] = (double)value->value[i]; + for (const int i : IndexRange(3)) { + ui_data->default_array[i] = double(value->value[i]); + } + return property; + } + case SOCK_RGBA: { + bNodeSocketValueRGBA *value = (bNodeSocketValueRGBA *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.array.len = 4; + idprop.array.type = IDP_FLOAT; + IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier); + copy_v4_v4((float *)IDP_Array(property), value->value); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + ui_data->base.rna_subtype = PROP_COLOR; + ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__); + ui_data->default_array_len = 4; + for (const int i : IndexRange(4)) { + ui_data->default_array[i] = double(value->value[i]); } return property; } @@ -390,6 +421,8 @@ static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDP return property.type == IDP_INT; case SOCK_VECTOR: return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 3; + case SOCK_RGBA: + return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 4; case SOCK_BOOLEAN: return property.type == IDP_INT; case SOCK_STRING: @@ -410,28 +443,42 @@ static void init_socket_cpp_value_from_property(const IDProperty &property, { switch (socket_value_type) { case SOCK_FLOAT: { + float value = 0.0f; if (property.type == IDP_FLOAT) { - *(float *)r_value = IDP_Float(&property); + value = IDP_Float(&property); } else if (property.type == IDP_DOUBLE) { - *(float *)r_value = (float)IDP_Double(&property); + value = (float)IDP_Double(&property); } + new (r_value) blender::fn::Field<float>(blender::fn::make_constant_field(value)); break; } case SOCK_INT: { - *(int *)r_value = IDP_Int(&property); + int value = IDP_Int(&property); + new (r_value) blender::fn::Field<int>(blender::fn::make_constant_field(value)); break; } case SOCK_VECTOR: { - copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); + float3 value; + copy_v3_v3(value, (const float *)IDP_Array(&property)); + new (r_value) blender::fn::Field<float3>(blender::fn::make_constant_field(value)); + break; + } + case SOCK_RGBA: { + blender::ColorGeometry4f value; + copy_v4_v4((float *)value, (const float *)IDP_Array(&property)); + new (r_value) blender::fn::Field<ColorGeometry4f>(blender::fn::make_constant_field(value)); break; } case SOCK_BOOLEAN: { - *(bool *)r_value = IDP_Int(&property) != 0; + bool value = IDP_Int(&property) != 0; + new (r_value) blender::fn::Field<bool>(blender::fn::make_constant_field(value)); break; } case SOCK_STRING: { - new (r_value) std::string(IDP_String(&property)); + std::string value = IDP_String(&property); + new (r_value) + blender::fn::Field<std::string>(blender::fn::make_constant_field(std::move(value))); break; } case SOCK_OBJECT: { @@ -512,6 +559,32 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) new_prop->ui_data = ui_data; } } + + if (socket_type_has_attribute_toggle(*socket)) { + const std::string use_attribute_id = socket->identifier + use_attribute_suffix; + const std::string attribute_name_id = socket->identifier + attribute_name_suffix; + + IDPropertyTemplate idprop = {0}; + IDProperty *use_attribute_prop = IDP_New(IDP_INT, &idprop, use_attribute_id.c_str()); + IDP_AddToGroup(nmd->settings.properties, use_attribute_prop); + + IDProperty *attribute_prop = IDP_New(IDP_STRING, &idprop, attribute_name_id.c_str()); + IDP_AddToGroup(nmd->settings.properties, attribute_prop); + + if (old_properties != nullptr) { + IDProperty *old_prop_use_attribute = IDP_GetPropertyFromGroup(old_properties, + use_attribute_id.c_str()); + if (old_prop_use_attribute != nullptr) { + IDP_CopyPropertyContent(use_attribute_prop, old_prop_use_attribute); + } + + IDProperty *old_attribute_name_prop = IDP_GetPropertyFromGroup(old_properties, + attribute_name_id.c_str()); + if (old_attribute_name_prop != nullptr) { + IDP_CopyPropertyContent(attribute_prop, old_attribute_name_prop); + } + } + } } if (old_properties != nullptr) { @@ -567,8 +640,33 @@ static void initialize_group_input(NodesModifierData &nmd, return; } - init_socket_cpp_value_from_property( - *property, static_cast<eNodeSocketDatatype>(socket.type), r_value); + if (!socket_type_has_attribute_toggle(socket)) { + init_socket_cpp_value_from_property( + *property, static_cast<eNodeSocketDatatype>(socket.type), r_value); + return; + } + + const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup( + nmd.settings.properties, (socket.identifier + use_attribute_suffix).c_str()); + const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup( + nmd.settings.properties, (socket.identifier + attribute_name_suffix).c_str()); + if (property_use_attribute == nullptr || property_attribute_name == nullptr) { + init_socket_cpp_value_from_property( + *property, static_cast<eNodeSocketDatatype>(socket.type), r_value); + return; + } + + const bool use_attribute = IDP_Int(property_use_attribute) != 0; + if (use_attribute) { + const StringRef attribute_name{IDP_String(property_attribute_name)}; + auto attribute_input = std::make_shared<blender::bke::AttributeFieldInput>( + attribute_name, *socket.typeinfo->get_base_cpp_type()); + new (r_value) blender::fn::GField(std::move(attribute_input), 0); + } + else { + init_socket_cpp_value_from_property( + *property, static_cast<eNodeSocketDatatype>(socket.type), r_value); + } } static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain) @@ -878,13 +976,13 @@ static void modifyGeometrySet(ModifierData *md, * the node socket identifier for the property names, since they are unique, but also having * the correct label displayed in the UI. */ static void draw_property_for_socket(uiLayout *layout, + NodesModifierData *nmd, PointerRNA *bmain_ptr, PointerRNA *md_ptr, - const IDProperty *modifier_props, const bNodeSocket &socket) { /* The property should be created in #MOD_nodes_update_interface with the correct type. */ - IDProperty *property = IDP_GetPropertyFromGroup(modifier_props, socket.identifier); + IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier); /* 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. */ @@ -925,8 +1023,38 @@ static void draw_property_for_socket(uiLayout *layout, uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE); break; } - default: - uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + default: { + if (socket_type_has_attribute_toggle(socket) && + USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { + const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + + use_attribute_suffix + "\"]"; + const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + + attribute_name_suffix + "\"]"; + + uiLayout *row = uiLayoutRow(layout, true); + const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; + if (use_attribute) { + uiItemR(row, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); + } + else { + uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); + } + PointerRNA props; + uiItemFullO(row, + "object.geometry_nodes_input_attribute_toggle", + "", + ICON_SPREADSHEET, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + &props); + RNA_string_set(&props, "modifier_name", nmd->modifier.name); + RNA_string_set(&props, "prop_path", rna_path_use_attribute.c_str()); + } + else { + uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + } + } } } @@ -957,7 +1085,7 @@ static void panel_draw(const bContext *C, Panel *panel) RNA_main_pointer_create(bmain, &bmain_ptr); LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { - draw_property_for_socket(layout, &bmain_ptr, ptr, nmd->settings.properties, *socket); + draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, *socket); } } @@ -1041,9 +1169,10 @@ ModifierTypeInfo modifierType_Nodes = { /* srna */ &RNA_NodesModifier, /* type */ eModifierTypeType_Constructive, /* flags */ - static_cast<ModifierTypeFlag>( - eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode | - eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping), + static_cast<ModifierTypeFlag>(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_SupportsEditmode | + eModifierTypeFlag_EnableInEditmode | + eModifierTypeFlag_SupportsMapping), /* icon */ ICON_NODETREE, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 5646e37707c..f67f7f967c9 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -21,6 +21,8 @@ #include "DEG_depsgraph_query.h" +#include "FN_field.hh" +#include "FN_field_cpp_type.hh" #include "FN_generic_value_map.hh" #include "FN_multi_function.hh" @@ -33,6 +35,9 @@ namespace blender::modifiers::geometry_nodes { using fn::CPPType; +using fn::Field; +using fn::FieldCPPType; +using fn::GField; using fn::GValueMap; using nodes::GeoNodeExecParams; using namespace fn::multi_function_types; @@ -858,11 +863,10 @@ class GeometryNodesEvaluator { const MultiFunction &fn, NodeState &node_state) { - MFContextBuilder fn_context; - MFParamsBuilder fn_params{fn, 1}; LinearAllocator<> &allocator = local_allocators_.local(); /* Prepare the inputs for the multi function. */ + Vector<GField> input_fields; for (const int i : node->inputs().index_range()) { const InputSocketRef &socket_ref = node->input(i); if (!socket_ref.is_available()) { @@ -873,24 +877,12 @@ class GeometryNodesEvaluator { BLI_assert(input_state.was_ready_for_execution); SingleInputValue &single_value = *input_state.value.single; BLI_assert(single_value.value != nullptr); - fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value}); - } - /* Prepare the outputs for the multi function. */ - Vector<GMutablePointer> outputs; - for (const int i : node->outputs().index_range()) { - const OutputSocketRef &socket_ref = node->output(i); - if (!socket_ref.is_available()) { - continue; - } - const CPPType &type = *get_socket_cpp_type(socket_ref); - void *buffer = allocator.allocate(type.size(), type.alignment()); - fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1}); - outputs.append({type, buffer}); + input_fields.append(std::move(*(GField *)single_value.value)); } - fn.call(IndexRange(1), fn_params, fn_context); + auto operation = std::make_shared<fn::FieldOperation>(fn, std::move(input_fields)); - /* Forward the computed outputs. */ + /* Forward outputs. */ int output_index = 0; for (const int i : node->outputs().index_range()) { const OutputSocketRef &socket_ref = node->output(i); @@ -899,8 +891,11 @@ class GeometryNodesEvaluator { } OutputState &output_state = node_state.outputs[i]; const DOutputSocket socket{node.context(), &socket_ref}; - GMutablePointer value = outputs[output_index]; - this->forward_output(socket, value); + const CPPType *cpp_type = get_socket_cpp_type(socket_ref); + GField new_field{operation, output_index}; + new_field = fn::make_field_constant_if_possible(std::move(new_field)); + GField &field_to_forward = *allocator.construct<GField>(std::move(new_field)).release(); + this->forward_output(socket, {cpp_type, &field_to_forward}); output_state.has_been_computed = true; output_index++; } @@ -922,7 +917,7 @@ class GeometryNodesEvaluator { OutputState &output_state = node_state.outputs[socket->index()]; output_state.has_been_computed = true; void *buffer = allocator.allocate(type->size(), type->alignment()); - type->copy_construct(type->default_value(), buffer); + this->construct_default_value(*type, buffer); this->forward_output({node.context(), socket}, {*type, buffer}); } } @@ -1389,14 +1384,42 @@ class GeometryNodesEvaluator { return; } + const FieldCPPType *from_field_type = dynamic_cast<const FieldCPPType *>(&from_type); + const FieldCPPType *to_field_type = dynamic_cast<const FieldCPPType *>(&to_type); + + if (from_field_type != nullptr && to_field_type != nullptr) { + const CPPType &from_base_type = from_field_type->field_type(); + const CPPType &to_base_type = to_field_type->field_type(); + if (conversions_.is_convertible(from_base_type, to_base_type)) { + const MultiFunction &fn = *conversions_.get_conversion_multi_function( + MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + const GField &from_field = *(const GField *)from_value; + auto operation = std::make_shared<fn::FieldOperation>(fn, Vector<GField>{from_field}); + new (to_value) GField(std::move(operation), 0); + return; + } + } if (conversions_.is_convertible(from_type, to_type)) { /* Do the conversion if possible. */ conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value); } else { /* Cannot convert, use default value instead. */ - to_type.copy_construct(to_type.default_value(), to_value); + this->construct_default_value(to_type, to_value); + } + } + + void construct_default_value(const CPPType &type, void *r_value) + { + if (const FieldCPPType *field_cpp_type = dynamic_cast<const FieldCPPType *>(&type)) { + const CPPType &base_type = field_cpp_type->field_type(); + auto constant_fn = std::make_unique<fn::CustomMF_GenericConstant>( + base_type, base_type.default_value(), false); + auto operation = std::make_shared<fn::FieldOperation>(std::move(constant_fn)); + new (r_value) GField(std::move(operation), 0); + return; } + type.copy_construct(type.default_value(), r_value); } NodeState &get_node_state(const DNode node) diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 1dbdcf87d63..db2eedf9c02 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -450,7 +450,7 @@ static void normalEditModifier_do_directional(NormalEditModifierData *enmd, if (do_polynors_fix && polygons_check_flip(mloop, nos, &mesh->ldata, mpoly, polynors, num_polys)) { - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } BKE_mesh_normals_loop_custom_set(mvert, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 1c502b94bdb..ff1055eff3b 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -317,7 +317,7 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co } } - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } @@ -510,7 +510,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = doOcean(md, ctx, mesh); if (result != mesh) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } return result; diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 49b5dabe72d..4fffa7c93f3 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -545,7 +545,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_SAFE_FREE(vert_part_index); MEM_SAFE_FREE(vert_part_value); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index df3db894f4e..fef1f76c051 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -220,7 +220,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) BKE_mesh_copy_parameters_for_eval(result, mesh); BKE_mesh_calc_edges(result, true, false); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 0819b314e32..f24f6951690 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -1135,12 +1135,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * ob_axis != NULL ? mtx_tx[3] : NULL, ltmd->merge_dist); if (result != result_prev) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } } if ((ltmd->flag & MOD_SCREW_NORMAL_CALC) == 0) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } return result; diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 543cee18868..7d90935f678 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -1960,7 +1960,7 @@ static Mesh *base_skin(Mesh *origmesh, SkinModifierData *smd, eSkinErrorFlag *r_ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, origmesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); skin_set_orig_indices(result); diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 00fa6e24a64..8f9aa86e561 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -988,7 +988,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex /* must recalculate normals with vgroups since they can displace unevenly T26888. */ if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || do_rim || dvert) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } else if (do_shell) { uint i; diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index 5b4716a1a43..f654b69841e 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -1955,7 +1955,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } } - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); /* Make edges. */ { diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index ef633494c7b..52d5f3e97ef 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -107,7 +107,7 @@ Mesh *triangulate_mesh(Mesh *mesh, me->flag |= ME_EDGEDRAW | ME_EDGERENDER; } - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 5b97d0eb259..d57e92b4b35 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -216,7 +216,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, * we really need vertexCos here. */ else if (vertexCos) { BKE_mesh_vert_coords_apply(mesh, vertexCos); - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; } if (use_orco) { diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index b1fa2a7d912..503297d5985 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -1979,8 +1979,7 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, BLI_assert(loop_cur == result_nloops); /* is this needed? */ - /* recalculate normals */ - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); weld_mesh_context_free(&weld_mesh); } diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index e188a61e975..706960182cf 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -109,7 +109,7 @@ static Mesh *WireframeModifier_do(WireframeModifierData *wmd, Object *ob, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 8680fcee49a..b741461f820 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -104,6 +104,7 @@ set(SRC composite/nodes/node_composite_outputFile.c composite/nodes/node_composite_pixelate.c composite/nodes/node_composite_planetrackdeform.c + composite/nodes/node_composite_posterize.c composite/nodes/node_composite_premulkey.c composite/nodes/node_composite_rgb.c composite/nodes/node_composite_rotate.c @@ -140,7 +141,11 @@ set(SRC function/nodes/node_fn_random_float.cc function/node_function_util.cc + geometry/nodes/legacy/node_geo_material_assign.cc + geometry/nodes/legacy/node_geo_select_by_material.cc + geometry/nodes/node_geo_align_rotation_to_vector.cc + geometry/nodes/node_geo_attribute_capture.cc geometry/nodes/node_geo_attribute_clamp.cc geometry/nodes/node_geo_attribute_color_ramp.cc geometry/nodes/node_geo_attribute_combine_xyz.cc @@ -165,6 +170,7 @@ set(SRC geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc geometry/nodes/node_geo_curve_endpoints.cc + geometry/nodes/node_geo_curve_fill.cc geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_primitive_bezier_segment.cc geometry/nodes/node_geo_curve_primitive_circle.cc @@ -185,10 +191,14 @@ set(SRC geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc + geometry/nodes/node_geo_input_normal.cc + geometry/nodes/node_geo_input_position.cc + geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc geometry/nodes/node_geo_material_assign.cc geometry/nodes/node_geo_material_replace.cc + geometry/nodes/node_geo_material_selection.cc geometry/nodes/node_geo_mesh_primitive_circle.cc geometry/nodes/node_geo_mesh_primitive_cone.cc geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -208,8 +218,8 @@ set(SRC geometry/nodes/node_geo_point_translate.cc geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_raycast.cc - geometry/nodes/node_geo_select_by_material.cc geometry/nodes/node_geo_separate_components.cc + geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc @@ -285,16 +295,16 @@ set(SRC shader/nodes/node_shader_tex_checker.c shader/nodes/node_shader_tex_coord.c shader/nodes/node_shader_tex_environment.c - shader/nodes/node_shader_tex_gradient.c + shader/nodes/node_shader_tex_gradient.cc shader/nodes/node_shader_tex_image.c shader/nodes/node_shader_tex_magic.c - shader/nodes/node_shader_tex_musgrave.c - shader/nodes/node_shader_tex_noise.c + shader/nodes/node_shader_tex_musgrave.cc + shader/nodes/node_shader_tex_noise.cc shader/nodes/node_shader_tex_pointdensity.c shader/nodes/node_shader_tex_sky.c - shader/nodes/node_shader_tex_voronoi.c + shader/nodes/node_shader_tex_voronoi.cc shader/nodes/node_shader_tex_wave.c - shader/nodes/node_shader_tex_white_noise.c + shader/nodes/node_shader_tex_white_noise.cc shader/nodes/node_shader_uvAlongStroke.c shader/nodes/node_shader_uvmap.c shader/nodes/node_shader_valToRgb.cc @@ -346,6 +356,8 @@ set(SRC intern/node_exec.cc intern/node_geometry_exec.cc intern/node_multi_function.cc + intern/node_declaration.cc + intern/node_socket_declarations.cc intern/node_socket.cc intern/node_tree_ref.cc intern/node_util.c @@ -366,6 +378,8 @@ set(SRC NOD_geometry_nodes_eval_log.hh NOD_math_functions.hh NOD_multi_function.hh + NOD_node_declaration.hh + NOD_socket_declarations.hh NOD_node_tree_ref.hh NOD_shader.h NOD_socket.h diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 258e4c961c9..2cbbd31c97a 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -80,6 +80,7 @@ void register_node_type_cmp_despeckle(void); void register_node_type_cmp_defocus(void); void register_node_type_cmp_denoise(void); void register_node_type_cmp_antialiasing(void); +void register_node_type_cmp_posterize(void); void register_node_type_cmp_valtorgb(void); void register_node_type_cmp_rgbtobw(void); diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 856d787c8d0..a713da45f0b 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,6 +29,9 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); +void register_node_type_geo_legacy_material_assign(void); +void register_node_type_geo_legacy_select_by_material(void); + void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_clamp(void); void register_node_type_geo_attribute_color_ramp(void); @@ -37,6 +40,7 @@ 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_capture(void); void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); @@ -52,6 +56,7 @@ void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_convex_hull(void); void register_node_type_geo_curve_endpoints(void); +void register_node_type_geo_curve_fill(void); void register_node_type_geo_curve_length(void); void register_node_type_geo_curve_primitive_bezier_segment(void); void register_node_type_geo_curve_primitive_circle(void); @@ -70,11 +75,15 @@ void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_edge_split(void); +void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); +void register_node_type_geo_input_normal(void); +void register_node_type_geo_input_position(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_assign(void); void register_node_type_geo_material_replace(void); +void register_node_type_geo_material_selection(void); void register_node_type_geo_mesh_primitive_circle(void); void register_node_type_geo_mesh_primitive_cone(void); void register_node_type_geo_mesh_primitive_cube(void); @@ -96,8 +105,8 @@ void register_node_type_geo_points_to_volume(void); void register_node_type_geo_raycast(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_handle_type(void); -void register_node_type_geo_select_by_material(void); void register_node_type_geo_separate_components(void); +void register_node_type_geo_set_position(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index d6a23051c0b..dbb5f8b240d 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -16,7 +16,8 @@ #pragma once -#include "FN_generic_value_map.hh" +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" #include "BKE_attribute_access.hh" #include "BKE_geometry_set.hh" @@ -32,17 +33,24 @@ struct ModifierData; namespace blender::nodes { +using bke::AttributeIDRef; using bke::geometry_set_realize_instances; +using bke::GeometryComponentFieldContext; using bke::OutputAttribute; using bke::OutputAttribute_Typed; using bke::ReadAttributeLookup; +using bke::StrongAnonymousAttributeID; +using bke::WeakAnonymousAttributeID; using bke::WriteAttributeLookup; using fn::CPPType; +using fn::Field; +using fn::FieldInput; +using fn::FieldOperation; +using fn::GField; 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; @@ -121,6 +129,14 @@ class GeoNodeExecParams { { } + template<typename T> + static inline constexpr bool is_stored_as_field_v = std::is_same_v<T, float> || + std::is_same_v<T, int> || + std::is_same_v<T, bool> || + std::is_same_v<T, ColorGeometry4f> || + std::is_same_v<T, float3> || + std::is_same_v<T, std::string>; + /** * Get the input value for the input socket with the given identifier. * @@ -142,11 +158,17 @@ class GeoNodeExecParams { */ template<typename T> T extract_input(StringRef identifier) { + if constexpr (is_stored_as_field_v<T>) { + Field<T> field = this->extract_input<Field<T>>(identifier); + return fn::evaluate_constant_field(field); + } + else { #ifdef DEBUG - this->check_input_access(identifier, &CPPType::get<T>()); + this->check_input_access(identifier, &CPPType::get<T>()); #endif - GMutablePointer gvalue = this->extract_input(identifier); - return gvalue.relocate_out<T>(); + GMutablePointer gvalue = this->extract_input(identifier); + return gvalue.relocate_out<T>(); + } } /** @@ -159,7 +181,13 @@ class GeoNodeExecParams { Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier); Vector<T> values; for (GMutablePointer gvalue : gvalues) { - values.append(gvalue.relocate_out<T>()); + if constexpr (is_stored_as_field_v<T>) { + const Field<T> field = gvalue.relocate_out<Field<T>>(); + values.append(fn::evaluate_constant_field(field)); + } + else { + values.append(gvalue.relocate_out<T>()); + } } return values; } @@ -167,14 +195,20 @@ class GeoNodeExecParams { /** * Get the input value for the input socket with the given identifier. */ - template<typename T> const T &get_input(StringRef identifier) const + template<typename T> const T get_input(StringRef identifier) const { + if constexpr (is_stored_as_field_v<T>) { + const Field<T> &field = this->get_input<Field<T>>(identifier); + return fn::evaluate_constant_field(field); + } + else { #ifdef DEBUG - this->check_input_access(identifier, &CPPType::get<T>()); + this->check_input_access(identifier, &CPPType::get<T>()); #endif - GPointer gvalue = provider_->get_input(identifier); - BLI_assert(gvalue.is_type<T>()); - return *(const T *)gvalue.get(); + GPointer gvalue = provider_->get_input(identifier); + BLI_assert(gvalue.is_type<T>()); + return *(const T *)gvalue.get(); + } } /** @@ -183,13 +217,19 @@ 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>>(); + if constexpr (is_stored_as_field_v<StoredT>) { + this->set_output<Field<StoredT>>(identifier, + fn::make_constant_field<StoredT>(std::forward<T>(value))); + } + else { + const CPPType &type = CPPType::get<StoredT>(); #ifdef DEBUG - this->check_output_access(identifier, type); + this->check_output_access(identifier, type); #endif - GMutablePointer gvalue = provider_->alloc_output_value(type); - new (gvalue.get()) StoredT(std::forward<T>(value)); - provider_->set_output(identifier, gvalue); + GMutablePointer gvalue = provider_->alloc_output_value(type); + new (gvalue.get()) StoredT(std::forward<T>(value)); + provider_->set_output(identifier, gvalue); + } } /** diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh index 2f4b104fb4c..58816544dc1 100644 --- a/source/blender/nodes/NOD_multi_function.hh +++ b/source/blender/nodes/NOD_multi_function.hh @@ -114,7 +114,7 @@ inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) template<typename T, typename... Args> inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) { - const T &fn = resource_scope_.construct<T>(__func__, std::forward<Args>(args)...); + const T &fn = resource_scope_.construct<T>(std::forward<Args>(args)...); this->set_matching_fn(&fn); } diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh new file mode 100644 index 00000000000..d64b76ccbb9 --- /dev/null +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -0,0 +1,205 @@ +/* + * 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 <type_traits> + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +namespace blender::nodes { + +class NodeDeclarationBuilder; + +/** + * Describes a single input or output socket. This is subclassed for different socket types. + */ +class SocketDeclaration { + protected: + std::string name_; + std::string identifier_; + bool hide_label_ = false; + bool hide_value_ = false; + bool is_multi_input_ = false; + + friend NodeDeclarationBuilder; + template<typename SocketDecl> friend class SocketDeclarationBuilder; + + public: + virtual ~SocketDeclaration() = default; + + virtual bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const = 0; + virtual bool matches(const bNodeSocket &socket) const = 0; + virtual bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const; + + StringRefNull name() const; + StringRefNull identifier() const; + + protected: + void set_common_flags(bNodeSocket &socket) const; + bool matches_common_data(const bNodeSocket &socket) const; +}; + +class BaseSocketDeclarationBuilder { + public: + virtual ~BaseSocketDeclarationBuilder() = default; +}; + +/** + * Wraps a #SocketDeclaration and provides methods to set it up correctly. + * This is separate from #SocketDeclaration, because it allows separating the API used by nodes to + * declare themselves from how the declaration is stored internally. + */ +template<typename SocketDecl> +class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { + protected: + using Self = typename SocketDecl::Builder; + static_assert(std::is_base_of_v<SocketDeclaration, SocketDecl>); + SocketDecl *decl_; + + friend class NodeDeclarationBuilder; + + public: + Self &hide_label(bool value = true) + { + decl_->hide_label_ = value; + return *(Self *)this; + } + + Self &hide_value(bool value = true) + { + decl_->hide_value_ = value; + return *(Self *)this; + } + + Self &multi_input(bool value = true) + { + decl_->is_multi_input_ = value; + return *(Self *)this; + } +}; + +using SocketDeclarationPtr = std::unique_ptr<SocketDeclaration>; + +class NodeDeclaration { + private: + Vector<SocketDeclarationPtr> inputs_; + Vector<SocketDeclarationPtr> outputs_; + + friend NodeDeclarationBuilder; + + public: + void build(bNodeTree &ntree, bNode &node) const; + bool matches(const bNode &node) const; + + Span<SocketDeclarationPtr> inputs() const; + Span<SocketDeclarationPtr> outputs() const; + + MEM_CXX_CLASS_ALLOC_FUNCS("NodeDeclaration") +}; + +class NodeDeclarationBuilder { + private: + NodeDeclaration &declaration_; + Vector<std::unique_ptr<BaseSocketDeclarationBuilder>> builders_; + + public: + NodeDeclarationBuilder(NodeDeclaration &declaration); + + template<typename DeclType> + typename DeclType::Builder &add_input(StringRef name, StringRef identifier = ""); + template<typename DeclType> + typename DeclType::Builder &add_output(StringRef name, StringRef identifier = ""); + + private: + template<typename DeclType> + typename DeclType::Builder &add_socket(StringRef name, + StringRef identifier, + Vector<SocketDeclarationPtr> &r_decls); +}; + +/* -------------------------------------------------------------------- + * SocketDeclaration inline methods. + */ + +inline StringRefNull SocketDeclaration::name() const +{ + return name_; +} + +inline StringRefNull SocketDeclaration::identifier() const +{ + return identifier_; +} + +/* -------------------------------------------------------------------- + * NodeDeclarationBuilder inline methods. + */ + +inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration) + : declaration_(declaration) +{ +} + +template<typename DeclType> +inline typename DeclType::Builder &NodeDeclarationBuilder::add_input(StringRef name, + StringRef identifier) +{ + return this->add_socket<DeclType>(name, identifier, declaration_.inputs_); +} + +template<typename DeclType> +inline typename DeclType::Builder &NodeDeclarationBuilder::add_output(StringRef name, + StringRef identifier) +{ + return this->add_socket<DeclType>(name, identifier, declaration_.outputs_); +} + +template<typename DeclType> +inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket( + StringRef name, StringRef identifier, Vector<SocketDeclarationPtr> &r_decls) +{ + static_assert(std::is_base_of_v<SocketDeclaration, DeclType>); + using Builder = typename DeclType::Builder; + std::unique_ptr<DeclType> socket_decl = std::make_unique<DeclType>(); + std::unique_ptr<Builder> socket_decl_builder = std::make_unique<Builder>(); + socket_decl_builder->decl_ = &*socket_decl; + socket_decl->name_ = name; + socket_decl->identifier_ = identifier.is_empty() ? name : identifier; + r_decls.append(std::move(socket_decl)); + Builder &socket_decl_builder_ref = *socket_decl_builder; + builders_.append(std::move(socket_decl_builder)); + return socket_decl_builder_ref; +} + +/* -------------------------------------------------------------------- + * NodeDeclaration inline methods. + */ + +inline Span<SocketDeclarationPtr> NodeDeclaration::inputs() const +{ + return inputs_; +} + +inline Span<SocketDeclarationPtr> NodeDeclaration::outputs() const +{ + return outputs_; +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_socket.h b/source/blender/nodes/NOD_socket.h index 6bcfda70d04..4f8e114c34a 100644 --- a/source/blender/nodes/NOD_socket.h +++ b/source/blender/nodes/NOD_socket.h @@ -43,7 +43,7 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree, struct bNodeSocketTemplate *stemp, eNodeSocketInOut in_out); -void node_verify_socket_templates(struct bNodeTree *ntree, struct bNode *node); +void node_verify_sockets(struct bNodeTree *ntree, struct bNode *node, bool do_id_user); void node_socket_init_default_value(struct bNodeSocket *sock); void node_socket_copy_default_value(struct bNodeSocket *to, const struct bNodeSocket *from); diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh new file mode 100644 index 00000000000..3d0cfdb5d5d --- /dev/null +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -0,0 +1,279 @@ +/* + * 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 "NOD_node_declaration.hh" + +#include "RNA_types.h" + +#include "BLI_color.hh" +#include "BLI_float3.hh" + +namespace blender::nodes::decl { + +class FloatBuilder; + +class Float : public SocketDeclaration { + private: + float default_value_ = 0.0f; + float soft_min_value_ = -FLT_MAX; + float soft_max_value_ = FLT_MAX; + PropertySubType subtype_ = PROP_NONE; + + friend FloatBuilder; + + public: + using Builder = FloatBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; +}; + +class FloatBuilder : public SocketDeclarationBuilder<Float> { + public: + FloatBuilder &min(const float value) + { + decl_->soft_min_value_ = value; + return *this; + } + + FloatBuilder &max(const float value) + { + decl_->soft_max_value_ = value; + return *this; + } + + FloatBuilder &default_value(const float value) + { + decl_->default_value_ = value; + return *this; + } + + FloatBuilder &subtype(PropertySubType subtype) + { + decl_->subtype_ = subtype; + return *this; + } +}; + +class IntBuilder; + +class Int : public SocketDeclaration { + private: + int default_value_ = 0; + int soft_min_value_ = INT32_MIN; + int soft_max_value_ = INT32_MAX; + PropertySubType subtype_ = PROP_NONE; + + friend IntBuilder; + + public: + using Builder = IntBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; +}; + +class IntBuilder : public SocketDeclarationBuilder<Int> { + public: + IntBuilder &min(const int value) + { + decl_->soft_min_value_ = value; + return *this; + } + + IntBuilder &max(const int value) + { + decl_->soft_max_value_ = value; + return *this; + } + + IntBuilder &default_value(const int value) + { + decl_->default_value_ = value; + return *this; + } + + IntBuilder &subtype(PropertySubType subtype) + { + decl_->subtype_ = subtype; + return *this; + } +}; + +class VectorBuilder; + +class Vector : public SocketDeclaration { + private: + float3 default_value_ = {0, 0, 0}; + float soft_min_value_ = -FLT_MAX; + float soft_max_value_ = FLT_MAX; + PropertySubType subtype_ = PROP_NONE; + + friend VectorBuilder; + + public: + using Builder = VectorBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; +}; + +class VectorBuilder : public SocketDeclarationBuilder<Vector> { + public: + VectorBuilder &default_value(const float3 value) + { + decl_->default_value_ = value; + return *this; + } + + VectorBuilder &subtype(PropertySubType subtype) + { + decl_->subtype_ = subtype; + return *this; + } + + VectorBuilder &min(const float min) + { + decl_->soft_min_value_ = min; + return *this; + } + + VectorBuilder &max(const float max) + { + decl_->soft_max_value_ = max; + return *this; + } +}; + +class BoolBuilder; + +class Bool : public SocketDeclaration { + private: + bool default_value_ = false; + friend BoolBuilder; + + public: + using Builder = BoolBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; +}; + +class BoolBuilder : public SocketDeclarationBuilder<Bool> { + public: + BoolBuilder &default_value(const bool value) + { + decl_->default_value_ = value; + return *this; + } +}; + +class ColorBuilder; + +class Color : public SocketDeclaration { + private: + ColorGeometry4f default_value_; + + friend ColorBuilder; + + public: + using Builder = ColorBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; +}; + +class ColorBuilder : public SocketDeclarationBuilder<Color> { + public: + ColorBuilder &default_value(const ColorGeometry4f value) + { + decl_->default_value_ = value; + return *this; + } +}; + +class String : public SocketDeclaration { + public: + using Builder = SocketDeclarationBuilder<String>; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; +}; + +class IDSocketDeclaration : public SocketDeclaration { + private: + const char *idname_; + + public: + IDSocketDeclaration(const char *idname) : idname_(idname) + { + } + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; +}; + +class Object : public IDSocketDeclaration { + public: + using Builder = SocketDeclarationBuilder<Object>; + + Object() : IDSocketDeclaration("NodeSocketObject") + { + } +}; + +class Material : public IDSocketDeclaration { + public: + using Builder = SocketDeclarationBuilder<Material>; + + Material() : IDSocketDeclaration("NodeSocketMaterial") + { + } +}; + +class Collection : public IDSocketDeclaration { + public: + using Builder = SocketDeclarationBuilder<Collection>; + + Collection() : IDSocketDeclaration("NodeSocketCollection") + { + } +}; + +class Texture : public IDSocketDeclaration { + public: + using Builder = SocketDeclarationBuilder<Texture>; + + Texture() : IDSocketDeclaration("NodeSocketTexture") + { + } +}; + +class Geometry : public SocketDeclaration { + public: + using Builder = SocketDeclarationBuilder<Geometry>; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; +}; + +} // namespace blender::nodes::decl diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 4da8648173d..51d59821d3c 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -225,6 +225,7 @@ DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE_LEGACY, def_cmp_cryptomatte_legacy, DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOISE", Denoise, "Denoise", "" ) DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" ) DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" ) +DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" ) DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) @@ -268,30 +269,50 @@ DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_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_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_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_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_LECAGY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "LEGACY_ATTRIBUTE_COLOR_RAMP", LegacyAttributeColorRamp, "Attribute Color Ramp", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "LEGACY_ATTRIBUTE_COMBINE_XYZ", LegacyAttributeCombineXYZ, "Attribute Combine XYZ", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "LEGACY_ATTRIBUTE_COMPARE", LegacyAttributeCompare, "Attribute Compare", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "LEGACY_ATTRIBUTE_CONVERT", LegacyAttributeConvert, "Attribute Convert", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "LEGACY_ATTRIBUTE_CURVE_MAP", LegacyAttributeCurveMap, "Attribute Curve Map", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_FILL, def_geo_attribute_fill, "LEGACY_ATTRIBUTE_FILL", LegacyAttributeFill, "Attribute Fill", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "LEGACY_ATTRIBUTE_MAP_RANGE", LegacyAttributeMapRange, "Attribute Map Range", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MATH, def_geo_attribute_math, "LEGACY_ATTRIBUTE_MATH", LegacyAttributeMath, "Attribute Math", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MIX, def_geo_attribute_mix, "LEGACY_ATTRIBUTE_MIX", LegacyAttributeMix, "Attribute Mix", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "LEGACY_ATTRIBUTE_RANDOMIZE", LegacyAttributeRandomize, "Attribute Randomize", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRIBUTE_SAMPLE_TEXTURE", LegacyAttributeSampleTexture, "Attribute Sample Texture", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "LEGACY_ATTRIBUTE_VECTOR_MATH", LegacyAttributeVectorMath, "Attribute Vector Math", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_MESH_TO_CURVE, 0, "LEGACY_MESH_TO_CURVE", LegacyMeshToCurve, "Mesh to Curve", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_DISTRIBUTE, def_geo_point_distribute, "LEGACY_POINT_DISTRIBUTE", LegacyPointDistribute, "Point Distribute", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_INSTANCE, def_geo_point_instance, "LEGACY_POINT_INSTANCE", LegacyPointInstance, "Point Instance", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_ROTATE, def_geo_point_rotate, "LEGACY_POINT_ROTATE", LegacyRotatePoints, "Point Rotate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_POINT_SCALE", LegacyPointScale, "Point Scale", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") + +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") 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_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "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_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") +DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") @@ -301,21 +322,19 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "CURVE_SELECT_HANDLES", CurveSelectHandles, "Select by Handle Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") -DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") +DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") +DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") +DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") +DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") 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", "") @@ -325,18 +344,9 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") -DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") 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_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") -DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") +DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "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", "") diff --git a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c index cc42781ee70..6f68b187775 100644 --- a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c +++ b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c @@ -40,7 +40,7 @@ static bNodeSocketTemplate cmp_node_doubleedgemask_out[] = { void register_node_type_cmp_doubleedgemask(void) { - static bNodeType ntype; // allocate a node type data structure + static bNodeType ntype; /* Allocate a node type data structure. */ cmp_node_type_base(&ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_MATTE, 0); node_type_socket_templates(&ntype, cmp_node_doubleedgemask_in, cmp_node_doubleedgemask_out); diff --git a/source/blender/io/usd/intern/usd_reader_instance.h b/source/blender/nodes/composite/nodes/node_composite_posterize.c index efc1c69a7dd..5093e581cdc 100644 --- a/source/blender/io/usd/intern/usd_reader_instance.h +++ b/source/blender/nodes/composite/nodes/node_composite_posterize.c @@ -16,32 +16,31 @@ * The Original Code is Copyright (C) 2021 Blender Foundation. * All rights reserved. */ -#pragma once -#include "usd_reader_xform.h" - -#include <pxr/usd/usdGeom/xform.h> - -struct Collection; - -namespace blender::io::usd { - -/* Wraps the UsdGeomXform schema. Creates a Blender Empty object. */ - -class USDInstanceReader : public USDXformReader { +/** \file + * \ingroup cmpnodes + */ - public: - USDInstanceReader(const pxr::UsdPrim &prim, - const USDImportParams &import_params, - const ImportSettings &settings); +#include "node_composite_util.h" - bool valid() const override; +/* **************** Posterize ******************** */ - void create_object(Main *bmain, double motionSampleTime) override; +static bNodeSocketTemplate cmp_node_posterize_in[] = { + {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + {SOCK_FLOAT, N_("Steps"), 8.0f, 8.0f, 8.0f, 8.0f, 2.0f, 1024.0f, PROP_NONE}, + {-1, ""}, +}; +static bNodeSocketTemplate cmp_node_posterize_out[] = { + {SOCK_RGBA, N_("Image")}, + {-1, ""}, +}; - void set_instance_collection(Collection *coll); +void register_node_type_cmp_posterize(void) +{ + static bNodeType ntype; - pxr::SdfPath proto_path() const; -}; + cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR, 0); + node_type_socket_templates(&ntype, cmp_node_posterize_in, cmp_node_posterize_out); -} // namespace blender::io::usd + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh index 96a8f29c3e9..46b485298e3 100644 --- a/source/blender/nodes/function/node_function_util.hh +++ b/source/blender/nodes/function/node_function_util.hh @@ -31,6 +31,7 @@ #include "NOD_function.h" #include "NOD_multi_function.hh" +#include "NOD_socket_declarations.hh" #include "node_util.h" diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 58e7d82beea..b71ee092de6 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -24,17 +24,17 @@ #include "node_function_util.hh" -static bNodeSocketTemplate fn_node_boolean_math_in[] = { - {SOCK_BOOLEAN, N_("Boolean")}, - {SOCK_BOOLEAN, N_("Boolean")}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate fn_node_boolean_math_out[] = { - {SOCK_BOOLEAN, N_("Boolean")}, - {-1, ""}, +static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Bool>("Boolean", "Boolean"); + b.add_input<decl::Bool>("Boolean", "Boolean_001"); + b.add_output<decl::Bool>("Boolean"); }; +} // namespace blender::nodes + static void fn_node_boolean_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -91,7 +91,7 @@ void register_node_type_fn_boolean_math() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out); + ntype.declare = blender::nodes::fn_node_boolean_math_declare; node_type_label(&ntype, node_boolean_math_label); node_type_update(&ntype, node_boolean_math_update); ntype.build_multi_function = fn_node_boolean_math_build_multi_function; diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 918dd24e520..4f4830afabc 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -26,18 +26,18 @@ #include "node_function_util.hh" -static bNodeSocketTemplate fn_node_float_compare_in[] = { - {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("Epsilon"), 0.001f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate fn_node_float_compare_out[] = { - {SOCK_BOOLEAN, N_("Result")}, - {-1, ""}, +static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("A").min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>("B").min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f); + b.add_output<decl::Bool>("Result"); }; +} // namespace blender::nodes + static void geo_node_float_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -110,7 +110,7 @@ void register_node_type_fn_float_compare() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out); + ntype.declare = blender::nodes::fn_node_float_compare_declare; node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); ntype.build_multi_function = fn_node_float_compare_build_multi_function; diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 40b8f27f895..e59c78d2c04 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -25,16 +25,16 @@ #include "node_function_util.hh" -static bNodeSocketTemplate fn_node_float_to_int_in[] = { - {SOCK_FLOAT, N_("Float"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate fn_node_float_to_int_out[] = { - {SOCK_INT, N_("Integer")}, - {-1, ""}, +static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("Float"); + b.add_output<decl::Int>("Integer"); }; +} // namespace blender::nodes + static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "rounding_mode", 0, "", ICON_NONE); @@ -88,7 +88,7 @@ void register_node_type_fn_float_to_int() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out); + ntype.declare = blender::nodes::fn_node_float_to_int_declare; node_type_label(&ntype, node_float_to_int_label); ntype.build_multi_function = fn_node_float_to_int_build_multi_function; ntype.draw_buttons = fn_node_float_to_int_layout; diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 560ace57aba..4a8e898fb9b 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -19,11 +19,15 @@ #include "UI_interface.h" #include "UI_resources.h" -static bNodeSocketTemplate fn_node_input_string_out[] = { - {SOCK_STRING, N_("String")}, - {-1, ""}, +namespace blender::nodes { + +static void fn_node_input_string_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::String>("String"); }; +} // namespace blender::nodes + static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "string", 0, "", ICON_NONE); @@ -75,7 +79,7 @@ void register_node_type_fn_input_string() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_INPUT_STRING, "String", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, fn_node_input_string_out); + ntype.declare = blender::nodes::fn_node_input_string_declare; node_type_init(&ntype, fn_node_input_string_init); node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); ntype.build_multi_function = fn_node_input_string_build_multi_function; diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 244c045de9a..9548df7b423 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -21,11 +21,15 @@ #include "UI_interface.h" #include "UI_resources.h" -static bNodeSocketTemplate fn_node_input_vector_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, +namespace blender::nodes { + +static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>("Vector"); }; +} // namespace blender::nodes + static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col = uiLayoutColumn(layout, true); @@ -52,7 +56,7 @@ void register_node_type_fn_input_vector() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_INPUT_VECTOR, "Vector", 0, 0); - node_type_socket_templates(&ntype, nullptr, fn_node_input_vector_out); + ntype.declare = blender::nodes::fn_node_input_vector_declare; node_type_init(&ntype, fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index 47ec9adf6bd..1bd39aacdca 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -18,18 +18,18 @@ #include "BLI_hash.h" -static bNodeSocketTemplate fn_node_random_float_in[] = { - {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate fn_node_random_float_out[] = { - {SOCK_FLOAT, N_("Value")}, - {-1, ""}, +static void fn_node_random_float_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("Min").min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Int>("Seed").min(-10000).max(10000); + b.add_output<decl::Float>("Value"); }; +} // namespace blender::nodes + class RandomFloatFunction : public blender::fn::MultiFunction { public: RandomFloatFunction() @@ -79,7 +79,7 @@ void register_node_type_fn_random_float() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); - node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); + ntype.declare = blender::nodes::fn_node_random_float_declare; ntype.build_multi_function = fn_node_random_float_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 956b7b8a005..015ac0de002 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -31,6 +31,7 @@ #include "NOD_geometry.h" #include "NOD_geometry_exec.hh" +#include "NOD_socket_declarations.hh" #include "node_util.h" @@ -54,13 +55,20 @@ void transform_mesh(Mesh *mesh, const float3 rotation, const float3 scale); +Mesh *create_line_mesh(const float3 start, const float3 delta, const int count); + +Mesh *create_grid_mesh(const int verts_x, + const int verts_y, + const float size_x, + const float size_y); + Mesh *create_cylinder_or_cone_mesh(const float radius_top, const float radius_bottom, const float depth, const int verts_num, const GeometryNodeMeshCircleFillType fill_type); -Mesh *create_cube_mesh(const float size); +Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); /** * Copies the point domain attributes from `in_component` that are in the mask to `out_component`. @@ -76,7 +84,7 @@ struct CurveToPointsResults { MutableSpan<float> radii; MutableSpan<float> tilts; - Map<std::string, GMutableSpan> point_attributes; + Map<AttributeIDRef, GMutableSpan> point_attributes; MutableSpan<float3> tangents; MutableSpan<float3> normals; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc new file mode 100644 index 00000000000..7d3481c1067 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc @@ -0,0 +1,95 @@ +/* + * 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" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_material.h" + +namespace blender::nodes { + +static void geo_node_legacy_material_assign_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Material>("Material").hide_label(true); + b.add_input<decl::String>("Selection"); + b.add_output<decl::Geometry>("Geometry"); +} + +static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material) +{ + int new_material_index = -1; + for (const int i : IndexRange(mesh.totcol)) { + Material *other_material = mesh.mat[i]; + if (other_material == material) { + new_material_index = i; + break; + } + } + if (new_material_index == -1) { + /* Append a new material index. */ + new_material_index = mesh.totcol; + BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); + } + + mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); + for (const int i : IndexRange(mesh.totpoly)) { + if (face_mask[i]) { + MPoly &poly = mesh.mpoly[i]; + poly.mat_nr = new_material_index; + } + } +} + +static void geo_node_legacy_material_assign_exec(GeoNodeExecParams params) +{ + Material *material = params.extract_input<Material *>("Material"); + const std::string mask_name = params.extract_input<std::string>("Selection"); + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + Mesh *mesh = mesh_component.get_for_write(); + if (mesh != nullptr) { + GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>( + mask_name, ATTR_DOMAIN_FACE, true); + assign_material_to_faces(*mesh, face_mask, material); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_material_assign() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_legacy_material_assign_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_material_assign_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc index ee114741a77..eabdd2bcd5a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc @@ -26,29 +26,16 @@ #include "BKE_material.h" -static bNodeSocketTemplate geo_node_select_by_material_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_MATERIAL, - N_("Material"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - PROP_NONE, - SOCK_HIDE_LABEL}, - {SOCK_STRING, N_("Selection")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_select_by_material_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_legacy_select_by_material_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Material>("Material").hide_label(); + b.add_input<decl::String>("Selection"); + b.add_output<decl::Geometry>("Geometry"); +} + static void select_mesh_by_material(const Mesh &mesh, const Material *material, const MutableSpan<bool> r_selection) @@ -67,7 +54,7 @@ static void select_mesh_by_material(const Mesh &mesh, }); } -static void geo_node_select_by_material_exec(GeoNodeExecParams params) +static void geo_node_legacy_select_by_material_exec(GeoNodeExecParams params) { Material *material = params.extract_input<Material *>("Material"); const std::string selection_name = params.extract_input<std::string>("Selection"); @@ -93,14 +80,13 @@ static void geo_node_select_by_material_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_select_by_material() +void register_node_type_geo_legacy_select_by_material() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_select_by_material_in, geo_node_select_by_material_out); - ntype.geometry_node_execute = blender::nodes::geo_node_select_by_material_exec; + &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_legacy_select_by_material_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_select_by_material_exec; nodeRegisterType(&ntype); } 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 9b6824fdb5c..d0bb906e8af 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 @@ -22,19 +22,23 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_align_rotation_to_vector_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Factor")}, - {SOCK_FLOAT, N_("Factor"), 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR}, - {SOCK_STRING, N_("Vector")}, - {SOCK_VECTOR, N_("Vector"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX, PROP_ANGLE}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_align_rotation_to_vector_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_align_rotation_to_vector_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Factor"); + b.add_input<decl::Float>("Factor", "Factor_001") + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::String>("Vector"); + b.add_input<decl::Vector>("Vector", "Vector_001") + .default_value({0.0, 0.0, 1.0}) + .subtype(PROP_ANGLE); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, bContext *UNUSED(C), @@ -49,8 +53,6 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, uiItemR(col, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE); } -namespace blender::nodes { - static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) @@ -224,19 +226,18 @@ void register_node_type_geo_align_rotation_to_vector() static bNodeType ntype; geo_node_type_base(&ntype, - GEO_NODE_ALIGN_ROTATION_TO_VECTOR, + GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, "Align Rotation to Vector", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_align_rotation_to_vector_in, geo_node_align_rotation_to_vector_out); node_type_init(&ntype, blender::nodes::geo_node_align_rotation_to_vector_init); node_type_update(&ntype, blender::nodes::geo_node_align_rotation_to_vector_update); node_type_storage(&ntype, "NodeGeometryAlignRotationToVector", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_align_rotation_to_vector_declare; ntype.geometry_node_execute = blender::nodes::geo_node_align_rotation_to_vector_exec; - ntype.draw_buttons = geo_node_align_rotation_to_vector_layout; + ntype.draw_buttons = blender::nodes::geo_node_align_rotation_to_vector_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc new file mode 100644 index 00000000000..1fa71d3f57d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -0,0 +1,210 @@ +/* + * 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 "UI_interface.h" +#include "UI_resources.h" + +#include "BKE_attribute_math.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Vector>("Value"); + b.add_input<decl::Float>("Value", "Value_001"); + b.add_input<decl::Color>("Value", "Value_002"); + b.add_input<decl::Bool>("Value", "Value_003"); + b.add_input<decl::Int>("Value", "Value_004"); + + b.add_output<decl::Geometry>("Geometry"); + b.add_output<decl::Vector>("Attribute"); + b.add_output<decl::Float>("Attribute", "Attribute_001"); + b.add_output<decl::Color>("Attribute", "Attribute_002"); + b.add_output<decl::Bool>("Attribute", "Attribute_003"); + b.add_output<decl::Int>("Attribute", "Attribute_004"); +} + +static void geo_node_attribute_capture_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void geo_node_attribute_capture_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryAttributeCapture *data = (NodeGeometryAttributeCapture *)MEM_callocN( + sizeof(NodeGeometryAttributeCapture), __func__); + data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_POINT; + + node->storage = data; +} + +static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *) + node->storage; + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + + bNodeSocket *socket_value_attribute_name = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_value_vector = socket_value_attribute_name->next; + bNodeSocket *socket_value_float = socket_value_vector->next; + bNodeSocket *socket_value_color4f = socket_value_float->next; + bNodeSocket *socket_value_boolean = socket_value_color4f->next; + bNodeSocket *socket_value_int32 = socket_value_boolean->next; + + nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_value_attribute_name = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_value_vector = out_socket_value_attribute_name->next; + bNodeSocket *out_socket_value_float = out_socket_value_vector->next; + bNodeSocket *out_socket_value_color4f = out_socket_value_float->next; + bNodeSocket *out_socket_value_boolean = out_socket_value_color4f->next; + bNodeSocket *out_socket_value_int32 = out_socket_value_boolean->next; + + nodeSetSocketAvailability(out_socket_value_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(out_socket_value_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(out_socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(out_socket_value_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(out_socket_value_int32, data_type == CD_PROP_INT32); +} + +static void try_capture_field_on_geometry(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const GField &field) +{ + GeometryComponentFieldContext field_context{component, domain}; + const int domain_size = component.attribute_domain_size(domain); + const IndexMask mask{IndexMask(domain_size)}; + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type()); + OutputAttribute output_attribute = component.attribute_try_get_for_output_only( + attribute_id, domain, data_type); + + fn::FieldEvaluator evaluator{field_context, &mask}; + evaluator.add_with_destination(field, output_attribute.varray()); + evaluator.evaluate(); + + output_attribute.save(); +} + +static void geo_node_attribute_capture_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + const bNode &node = params.node(); + const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *) + node.storage; + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); + + GField field; + switch (data_type) { + case CD_PROP_FLOAT: + field = params.get_input<Field<float>>("Value_001"); + break; + case CD_PROP_FLOAT3: + field = params.get_input<Field<float3>>("Value"); + break; + case CD_PROP_COLOR: + field = params.get_input<Field<ColorGeometry4f>>("Value_002"); + break; + case CD_PROP_BOOL: + field = params.get_input<Field<bool>>("Value_003"); + break; + case CD_PROP_INT32: + field = params.get_input<Field<int>>("Value_004"); + break; + default: + break; + } + + WeakAnonymousAttributeID anonymous_id{"Attribute Capture"}; + const CPPType &type = field.cpp_type(); + + static const Array<GeometryComponentType> types = { + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + GeometryComponent &component = geometry_set.get_component_for_write(type); + try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + } + } + + GField output_field{ + std::make_shared<bke::AnonymousAttributeFieldInput>(std::move(anonymous_id), type)}; + + switch (data_type) { + case CD_PROP_FLOAT: { + params.set_output("Attribute_001", Field<float>(output_field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Attribute", Field<float3>(output_field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Attribute_002", Field<ColorGeometry4f>(output_field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Attribute_003", Field<bool>(output_field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Attribute_004", Field<int>(output_field)); + break; + } + default: + break; + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_capture() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_CAPTURE, "Attribute Capture", NODE_CLASS_ATTRIBUTE, 0); + node_type_storage(&ntype, + "NodeGeometryAttributeCapture", + node_free_standard_storage, + node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::geo_node_attribute_capture_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_capture_update); + ntype.declare = blender::nodes::geo_node_attribute_capture_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_capture_exec; + ntype.draw_buttons = blender::nodes::geo_node_attribute_capture_layout; + nodeRegisterType(&ntype); +} 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 71643df1cb6..97070184609 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -20,25 +20,23 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_clamp_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Attribute")}, - {SOCK_STRING, N_("Result")}, - {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -100000, 100000}, - {SOCK_INT, N_("Max"), 100.0f, 0.0f, 0.0f, 0.0f, -100000, 100000}, - {SOCK_RGBA, N_("Min"), 0.5, 0.5, 0.5, 1.0}, - {SOCK_RGBA, N_("Max"), 0.5, 0.5, 0.5, 1.0}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_clamp_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_clamp_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Attribute"); + b.add_input<decl::String>("Result"); + b.add_input<decl::Vector>("Min"); + b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>("Min", "Min_001"); + b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f); + b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000); + b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000); + b.add_input<decl::Color>("Min", "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::Color>("Max", "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -78,8 +76,6 @@ static void geo_node_attribute_clamp_update(bNodeTree *UNUSED(ntree), bNode *nod nodeSetSocketAvailability(sock_max_color, data_type == CD_PROP_COLOR); } -namespace blender::nodes { - template<typename T> T clamp_value(const T val, const T min, const T max); template<> inline float clamp_value(const float val, const float min, const float max) @@ -272,12 +268,13 @@ void register_node_type_geo_attribute_clamp() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates(&ntype, geo_node_attribute_clamp_in, geo_node_attribute_clamp_out); - node_type_init(&ntype, geo_node_attribute_clamp_init); - node_type_update(&ntype, geo_node_attribute_clamp_update); + geo_node_type_base( + &ntype, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); + node_type_init(&ntype, blender::nodes::geo_node_attribute_clamp_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_clamp_update); + ntype.declare = blender::nodes::geo_node_attribute_clamp_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_clamp_exec; - ntype.draw_buttons = geo_node_attribute_clamp_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_clamp_layout; node_type_storage( &ntype, "NodeAttributeClamp", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); 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 c5740395dfb..aa054af3acd 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 @@ -23,17 +23,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_color_ramp_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Attribute")}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_color_ramp_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_color_ramp_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Attribute"); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_color_ramp_layout(uiLayout *layout, bContext *UNUSED(C), @@ -42,8 +40,6 @@ static void geo_node_attribute_color_ramp_layout(uiLayout *layout, uiTemplateColorRamp(layout, ptr, "color_ramp", false); } -namespace blender::nodes { - static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN( @@ -129,15 +125,17 @@ void register_node_type_geo_attribute_color_ramp() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_color_ramp_in, geo_node_attribute_color_ramp_out); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, + "Attribute Color Ramp", + NODE_CLASS_ATTRIBUTE, + 0); node_type_storage( &ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage); node_type_init(&ntype, blender::nodes::geo_node_attribute_color_ramp_init); node_type_size_preset(&ntype, NODE_SIZE_LARGE); + ntype.declare = blender::nodes::geo_node_attribute_color_ramp_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_color_ramp_exec; - ntype.draw_buttons = geo_node_attribute_color_ramp_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_color_ramp_layout; nodeRegisterType(&ntype); } 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 d8c52d16f41..569d5a824ca 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 @@ -19,22 +19,20 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_combine_xyz_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("X")}, - {SOCK_FLOAT, N_("X"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("Y")}, - {SOCK_FLOAT, N_("Y"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("Z")}, - {SOCK_FLOAT, N_("Z"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_combine_xyz_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_combine_xyz_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("X"); + b.add_input<decl::Float>("X", "X_001"); + b.add_input<decl::String>("Y"); + b.add_input<decl::Float>("Y", "Y_001"); + b.add_input<decl::String>("Z"); + b.add_input<decl::Float>("Z", "Z_001"); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_combine_xyz_layout(uiLayout *layout, bContext *UNUSED(C), @@ -48,8 +46,6 @@ static void geo_node_attribute_combine_xyz_layout(uiLayout *layout, uiItemR(col, ptr, "input_type_z", 0, IFACE_("Z"), ICON_NONE); } -namespace blender::nodes { - static void geo_node_attribute_combine_xyz_init(bNodeTree *UNUSED(tree), bNode *node) { NodeAttributeCombineXYZ *data = (NodeAttributeCombineXYZ *)MEM_callocN( @@ -140,15 +136,18 @@ void register_node_type_geo_attribute_combine_xyz() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, "Attribute Combine XYZ", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_combine_xyz_in, geo_node_attribute_combine_xyz_out); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, + "Attribute Combine XYZ", + NODE_CLASS_ATTRIBUTE, + 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_combine_xyz_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_combine_xyz_update); node_type_storage( &ntype, "NodeAttributeCombineXYZ", node_free_standard_storage, node_copy_standard_storage); + + ntype.declare = blender::nodes::geo_node_attribute_combine_xyz_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_combine_xyz_exec; - ntype.draw_buttons = geo_node_attribute_combine_xyz_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_combine_xyz_layout; nodeRegisterType(&ntype); } 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 57ac68b4cd4..0b9708dae14 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -21,25 +21,23 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_compare_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("A")}, - {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("A"), 0.5, 0.5, 0.5, 1.0}, - {SOCK_STRING, N_("B")}, - {SOCK_FLOAT, N_("B"), 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_("B"), 0.5, 0.5, 0.5, 1.0}, - {SOCK_FLOAT, N_("Threshold"), 0.01f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_compare_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_compare_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("A"); + b.add_input<decl::Float>("A", "A_001"); + b.add_input<decl::Vector>("A", "A_002"); + b.add_input<decl::Color>("A", "A_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input<decl::String>("B"); + b.add_input<decl::Float>("B", "B_001"); + b.add_input<decl::Vector>("B", "B_002"); + b.add_input<decl::Color>("B", "B_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input<decl::Float>("Threshold").default_value(0.01f).min(0.0f); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_compare_layout(uiLayout *layout, bContext *UNUSED(C), @@ -67,8 +65,6 @@ static bool operation_tests_equality(const NodeAttributeCompare &node_storage) return ELEM(node_storage.operation, NODE_FLOAT_COMPARE_EQUAL, NODE_FLOAT_COMPARE_NOT_EQUAL); } -namespace blender::nodes { - static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node->storage; @@ -352,14 +348,13 @@ void register_node_type_geo_attribute_compare() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_compare_in, geo_node_attribute_compare_out); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE, 0); + ntype.declare = blender::nodes::geo_node_attribute_compare_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_compare_exec; - ntype.draw_buttons = geo_node_attribute_compare_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_compare_layout; node_type_update(&ntype, blender::nodes::geo_node_attribute_compare_update); node_type_storage( &ntype, "NodeAttributeCompare", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, geo_node_attribute_compare_init); + node_type_init(&ntype, blender::nodes::geo_node_attribute_compare_init); nodeRegisterType(&ntype); } 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 60b5b91db19..a2382aa9d25 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc @@ -19,17 +19,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_convert_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Attribute")}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_convert_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_convert_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Attribute"); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_convert_layout(uiLayout *layout, bContext *UNUSED(C), @@ -51,8 +49,6 @@ static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node node->storage = data; } -namespace blender::nodes { - static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component, const StringRef source_name, const StringRef result_name) @@ -186,12 +182,11 @@ void register_node_type_geo_attribute_convert() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_convert_in, geo_node_attribute_convert_out); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0); + ntype.declare = blender::nodes::geo_node_attribute_convert_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_convert_exec; - ntype.draw_buttons = geo_node_attribute_convert_layout; - node_type_init(&ntype, geo_node_attribute_convert_init); + ntype.draw_buttons = blender::nodes::geo_node_attribute_convert_layout; + node_type_init(&ntype, blender::nodes::geo_node_attribute_convert_init); node_type_storage( &ntype, "NodeAttributeConvert", node_free_standard_storage, node_copy_standard_storage); 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 index 3b951bda95e..b9621b4ae92 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc @@ -24,17 +24,15 @@ #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, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_curve_map_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Attribute"); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_curve_map_layout(uiLayout *layout, bContext *UNUSED(C), @@ -101,8 +99,6 @@ static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode } } -namespace blender::nodes { - static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef input_name, StringRef result_name) @@ -215,17 +211,16 @@ 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); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0); + node_type_update(&ntype, blender::nodes::geo_node_attribute_curve_map_update); + node_type_init(&ntype, blender::nodes::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); + blender::nodes::geo_node_attribute_curve_map_free_storage, + blender::nodes::geo_node_attribute_curve_map_copy_storage); + ntype.declare = blender::nodes::geo_node_attribute_curve_map_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec; - ntype.draw_buttons = geo_node_attribute_curve_map_layout; + ntype.draw_buttons = blender::nodes::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 389abe3b2aa..3c50ae5c837 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -19,21 +19,19 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_fill_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Attribute")}, - {SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("Value"), 0, 0, 0, 0, -10000000.0f, 10000000.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_fill_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_fill_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Attribute"); + b.add_input<decl::Vector>("Value", "Value"); + b.add_input<decl::Float>("Value", "Value_001"); + b.add_input<decl::Color>("Value", "Value_002"); + b.add_input<decl::Bool>("Value", "Value_003"); + b.add_input<decl::Int>("Value", "Value_004"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -66,8 +64,6 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32); } -namespace blender::nodes { - static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name) { /* Use the domain of the result attribute if it already exists. */ @@ -156,11 +152,12 @@ void register_node_type_geo_attribute_fill() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates(&ntype, geo_node_attribute_fill_in, geo_node_attribute_fill_out); - node_type_init(&ntype, geo_node_attribute_fill_init); - node_type_update(&ntype, geo_node_attribute_fill_update); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0); + node_type_init(&ntype, blender::nodes::geo_node_attribute_fill_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_fill_update); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_fill_exec; - ntype.draw_buttons = geo_node_attribute_fill_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_fill_layout; + ntype.declare = blender::nodes::geo_node_attribute_fill_declare; nodeRegisterType(&ntype); } 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 00f38fb0c6b..0ea3bbe1e45 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 @@ -22,28 +22,26 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_map_range_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Attribute")}, - {SOCK_STRING, N_("Result")}, - {SOCK_FLOAT, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("From Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("To Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("From Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("To Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_BOOLEAN, N_("Clamp")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_map_range_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_map_range_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Attribute"); + b.add_input<decl::String>("Result"); + b.add_input<decl::Float>("From Min"); + b.add_input<decl::Float>("From Max").default_value(1.0f); + b.add_input<decl::Float>("To Min"); + b.add_input<decl::Float>("To Max").default_value(1.0f); + b.add_input<decl::Float>("Steps").default_value(4.0f); + b.add_input<decl::Vector>("From Min", "From Min_001"); + b.add_input<decl::Vector>("From Max", "From Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>("To Min", "To Min_001"); + b.add_input<decl::Vector>("To Max", "To Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>("Steps", "Steps_001").default_value({4.0f, 4.0f, 4.0f}); + b.add_input<decl::Bool>("Clamp"); + b.add_output<decl::Geometry>("Geometry"); +} static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -101,8 +99,6 @@ static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED); } -namespace blender::nodes { - static float map_linear(const float value, const float min_from, const float max_from, @@ -424,14 +420,13 @@ void register_node_type_geo_attribute_map_range() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_map_range_in, geo_node_attribute_map_range_out); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_map_range_exec; - node_type_init(&ntype, geo_node_attribute_map_range_init); - node_type_update(&ntype, geo_node_attribute_map_range_update); + node_type_init(&ntype, blender::nodes::geo_node_attribute_map_range_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_map_range_update); node_type_storage( &ntype, "NodeAttributeMapRange", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = fn_attribute_map_range_layout; + ntype.declare = blender::nodes::geo_node_attribute_map_range_declare; + ntype.draw_buttons = blender::nodes::fn_attribute_map_range_layout; nodeRegisterType(&ntype); } 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 368c4025eb7..efa09215b45 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -25,22 +25,20 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_math_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("A")}, - {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("B")}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("C")}, - {SOCK_FLOAT, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_math_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_math_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("A"); + b.add_input<decl::Float>("A", "A_001"); + b.add_input<decl::String>("B"); + b.add_input<decl::Float>("B", "B_001"); + b.add_input<decl::String>("C"); + b.add_input<decl::Float>("C", "C_001"); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} static bool operation_use_input_c(const NodeMathOperation operation) { @@ -132,8 +130,6 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -namespace blender::nodes { - static void geo_node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) { NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; @@ -305,13 +301,14 @@ void register_node_type_geo_attribute_math() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates(&ntype, geo_node_attribute_math_in, geo_node_attribute_math_out); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE, 0); + ntype.declare = blender::nodes::geo_node_attribute_math_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec; - ntype.draw_buttons = geo_node_attribute_math_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_math_layout; node_type_label(&ntype, blender::nodes::geo_node_math_label); node_type_update(&ntype, blender::nodes::geo_node_attribute_math_update); - node_type_init(&ntype, geo_node_attribute_math_init); + node_type_init(&ntype, blender::nodes::geo_node_attribute_math_init); node_type_storage( &ntype, "NodeAttributeMath", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); 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 931b7758a57..74e05cb997d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -25,26 +25,28 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_mix_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Factor")}, - {SOCK_FLOAT, N_("Factor"), 0.5, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR}, - {SOCK_STRING, N_("A")}, - {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("A"), 0.5, 0.5, 0.5, 1.0}, - {SOCK_STRING, N_("B")}, - {SOCK_FLOAT, N_("B"), 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_("B"), 0.5, 0.5, 0.5, 1.0}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_mix_attribute_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +static void geo_node_mix_attribute_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Factor"); + b.add_input<decl::Float>("Factor", "Factor_001") + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::String>("A"); + b.add_input<decl::Float>("A", "A_001"); + b.add_input<decl::Vector>("A", "A_002"); + b.add_input<decl::Color>("A", "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::String>("B"); + b.add_input<decl::Float>("B", "B_001"); + b.add_input<decl::Vector>("B", "B_002"); + b.add_input<decl::Color>("B", "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -57,8 +59,6 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE); } -namespace blender::nodes { - static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix), @@ -243,12 +243,12 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params) void register_node_type_geo_attribute_mix() { static bNodeType ntype; - - geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates(&ntype, geo_node_attribute_mix_in, geo_node_mix_attribute_out); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_mix_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_mix_update); - ntype.draw_buttons = geo_node_attribute_mix_layout; + ntype.declare = blender::nodes::geo_node_mix_attribute_declare; + ntype.draw_buttons = blender::nodes::geo_node_attribute_mix_layout; node_type_storage( &ntype, "NodeAttributeMix", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_mix_exec; 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 bfcde288cfb..0cf411343cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc @@ -26,18 +26,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_proximity_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_GEOMETRY, N_("Target")}, - {SOCK_STRING, N_("Distance")}, - {SOCK_STRING, N_("Position")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_proximity_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_proximity_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>("Target"); + b.add_input<decl::String>("Distance"); + b.add_input<decl::String>("Position"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_proximity_layout(uiLayout *layout, bContext *UNUSED(C), @@ -55,8 +53,6 @@ static void geo_attribute_proximity_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = node_storage; } -namespace blender::nodes { - static void calculate_mesh_proximity(const VArray<float3> &positions, const Mesh &mesh, const GeometryNodeAttributeProximityTargetType type, @@ -241,15 +237,15 @@ void register_node_type_geo_attribute_proximity() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_proximity_in, geo_node_attribute_proximity_out); - node_type_init(&ntype, geo_attribute_proximity_init); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE, 0); + node_type_init(&ntype, blender::nodes::geo_attribute_proximity_init); node_type_storage(&ntype, "NodeGeometryAttributeProximity", node_free_standard_storage, node_copy_standard_storage); + + ntype.declare = blender::nodes::geo_node_attribute_proximity_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_proximity_exec; - ntype.draw_buttons = geo_node_attribute_proximity_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_proximity_layout; nodeRegisterType(&ntype); } 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 15d419a003a..60b9910399c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -23,23 +23,21 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_randomize_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Attribute")}, - {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -100000, 100000}, - {SOCK_INT, N_("Max"), 100.0f, 0.0f, 0.0f, 0.0f, -100000, 100000}, - {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_randomize_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Attribute"); + b.add_input<decl::Vector>("Min"); + b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>("Min", "Min_001"); + b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f); + b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000); + b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000); + b.add_input<decl::Int>("Seed").min(-10000).max(10000); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_random_layout(uiLayout *layout, bContext *UNUSED(C), @@ -78,8 +76,6 @@ static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32); } -namespace blender::nodes { - template<typename T> T random_value_in_range(const uint32_t id, const uint32_t seed, const T min, const T max); @@ -335,13 +331,13 @@ void register_node_type_geo_attribute_randomize() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_randomize_in, geo_node_attribute_randomize_out); - node_type_init(&ntype, geo_node_attribute_randomize_init); - node_type_update(&ntype, geo_node_attribute_randomize_update); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); + node_type_init(&ntype, blender::nodes::geo_node_attribute_randomize_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_randomize_update); + + ntype.declare = blender::nodes::geo_node_attribute_randomize_declare; ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec; - ntype.draw_buttons = geo_node_attribute_random_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_random_layout; node_type_storage( &ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); 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 e4f3230ebb9..21a9a338857 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -16,28 +16,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_remove_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, - N_("Attribute"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - -1.0f, - 1.0f, - PROP_NONE, - SOCK_MULTI_INPUT}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_remove_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Attribute").multi_input(); + b.add_output<decl::Geometry>("Geometry"); +} + static void remove_attribute(GeometryComponent &component, GeoNodeExecParams ¶ms, Span<std::string> attribute_names) @@ -85,7 +72,7 @@ void register_node_type_geo_attribute_remove() geo_node_type_base( &ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates(&ntype, geo_node_attribute_remove_in, geo_node_attribute_remove_out); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec; + ntype.declare = blender::nodes::geo_node_attribute_remove_declare; nodeRegisterType(&ntype); } 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 5f02061da97..52f97475941 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 @@ -28,21 +28,17 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_TEXTURE, N_("Texture"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL}, - {SOCK_STRING, N_("Mapping")}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_attribute_sample_texture_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Texture>("Texture").hide_label(); + b.add_input<decl::String>("Mapping"); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} + static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef result_name, const StringRef map_name) @@ -130,13 +126,12 @@ void register_node_type_geo_sample_texture() static bNodeType ntype; geo_node_type_base(&ntype, - GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, + GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, "Attribute Sample Texture", NODE_CLASS_ATTRIBUTE, 0); node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_socket_templates( - &ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out); + ntype.declare = blender::nodes::geo_node_attribute_sample_texture_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec; 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 137a72bb707..de0090406c6 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 @@ -19,20 +19,18 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_separate_xyz_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Vector")}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("Result X")}, - {SOCK_STRING, N_("Result Y")}, - {SOCK_STRING, N_("Result Z")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_separate_xyz_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_separate_xyz_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Vector"); + b.add_input<decl::Vector>("Vector", "Vector_001"); + b.add_input<decl::String>("Result X"); + b.add_input<decl::String>("Result Y"); + b.add_input<decl::String>("Result Z"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_separate_xyz_layout(uiLayout *layout, bContext *UNUSED(C), @@ -43,8 +41,6 @@ static void geo_node_attribute_separate_xyz_layout(uiLayout *layout, uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE); } -namespace blender::nodes { - static void geo_node_attribute_separate_xyz_init(bNodeTree *UNUSED(tree), bNode *node) { NodeAttributeSeparateXYZ *data = (NodeAttributeSeparateXYZ *)MEM_callocN( @@ -161,15 +157,17 @@ void register_node_type_geo_attribute_separate_xyz() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, "Attribute Separate XYZ", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_separate_xyz_in, geo_node_attribute_separate_xyz_out); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, + "Attribute Separate XYZ", + NODE_CLASS_ATTRIBUTE, + 0); + ntype.declare = blender::nodes::geo_node_attribute_separate_xyz_declare; node_type_init(&ntype, blender::nodes::geo_node_attribute_separate_xyz_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_separate_xyz_update); node_type_storage( &ntype, "NodeAttributeSeparateXYZ", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_separate_xyz_exec; - ntype.draw_buttons = geo_node_attribute_separate_xyz_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_separate_xyz_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc index ab2136f4e8d..874350cd714 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -29,18 +29,16 @@ #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, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_transfer_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>("Source Geometry"); + b.add_input<decl::String>("Source"); + b.add_input<decl::String>("Destination"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_transfer_layout(uiLayout *layout, bContext *UNUSED(C), @@ -52,8 +50,6 @@ static void geo_node_attribute_transfer_layout(uiLayout *layout, 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( @@ -520,15 +516,14 @@ 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); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0); 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.declare = blender::nodes::geo_node_attribute_transfer_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec; - ntype.draw_buttons = geo_node_attribute_transfer_layout; + ntype.draw_buttons = blender::nodes::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 be70ebdebfe..59903050f88 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 @@ -26,24 +26,22 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_vector_math_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("A")}, - {SOCK_VECTOR, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("B")}, - {SOCK_VECTOR, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("C")}, - {SOCK_VECTOR, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_vector_math_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_vector_math_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("A"); + b.add_input<decl::Vector>("A", "A_001"); + b.add_input<decl::String>("B"); + b.add_input<decl::Vector>("B", "B_001"); + b.add_input<decl::Float>("B", "B_002"); + b.add_input<decl::String>("C"); + b.add_input<decl::Vector>("C", "C_001"); + b.add_input<decl::Float>("C", "C_002"); + b.add_input<decl::String>("Result"); + b.add_output<decl::Geometry>("Geometry"); +} static bool operation_use_input_b(const NodeVectorMathOperation operation) { @@ -154,8 +152,6 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op return CD_PROP_FLOAT3; } -namespace blender::nodes { - static void geo_node_vector_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, @@ -559,16 +555,19 @@ void register_node_type_geo_attribute_vector_math() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_VECTOR_MATH, "Attribute Vector Math", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_vector_math_in, geo_node_attribute_vector_math_out); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, + "Attribute Vector Math", + NODE_CLASS_ATTRIBUTE, + 0); + ntype.declare = blender::nodes::geo_node_attribute_vector_math_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec; - ntype.draw_buttons = geo_node_attribute_vector_math_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_vector_math_layout; node_type_label(&ntype, blender::nodes::geo_node_vector_math_label); node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_math_update); - node_type_init(&ntype, geo_node_attribute_vector_math_init); + node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_math_init); node_type_storage( &ntype, "NodeAttributeVectorMath", node_free_standard_storage, node_copy_standard_storage); + nodeRegisterType(&ntype); } 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 index da753dfc11b..adaa4de3029 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc @@ -21,27 +21,26 @@ #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, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Vector"); + b.add_input<decl::Vector>("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value(); + b.add_input<decl::String>("Center"); + b.add_input<decl::Vector>("Center", "Center_001").subtype(PROP_XYZ); + b.add_input<decl::String>("Axis"); + b.add_input<decl::Vector>("Axis", "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); + b.add_input<decl::String>("Angle"); + b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE); + b.add_input<decl::String>("Rotation"); + b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER); + b.add_input<decl::Bool>("Invert"); + b.add_input<decl::String>("Result"); + + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, bContext *UNUSED(C), @@ -71,8 +70,6 @@ static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, } } -namespace blender::nodes { - static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node) { const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage; @@ -339,14 +336,13 @@ void register_node_type_geo_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; + ntype.draw_buttons = blender::nodes::geo_node_attribute_vector_rotate_layout; + ntype.declare = blender::nodes::geo_node_attribute_vector_rotate_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index d8029ea1eeb..2a1c43a89fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -23,27 +23,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_boolean_in[] = { - {SOCK_GEOMETRY, N_("Geometry 1")}, - {SOCK_GEOMETRY, - N_("Geometry 2"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - PROP_NONE, - SOCK_MULTI_INPUT}, - {SOCK_BOOLEAN, N_("Self Intersection")}, - {SOCK_BOOLEAN, N_("Hole Tolerant")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_boolean_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_boolean_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry 1"); + b.add_input<decl::Geometry>("Geometry 2").multi_input(); + b.add_input<decl::Bool>("Self Intersection"); + b.add_input<decl::Bool>("Hole Tolerant"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -77,8 +66,6 @@ static void geo_node_boolean_init(bNodeTree *UNUSED(tree), bNode *node) node->custom1 = GEO_NODE_BOOLEAN_DIFFERENCE; } -namespace blender::nodes { - static void geo_node_boolean_exec(GeoNodeExecParams params) { GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1; @@ -138,10 +125,10 @@ void register_node_type_geo_boolean() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out); - ntype.draw_buttons = geo_node_boolean_layout; - ntype.updatefunc = geo_node_boolean_update; - node_type_init(&ntype, geo_node_boolean_init); + ntype.declare = blender::nodes::geo_node_boolean_declare; + ntype.draw_buttons = blender::nodes::geo_node_boolean_layout; + ntype.updatefunc = blender::nodes::geo_node_boolean_update; + node_type_init(&ntype, blender::nodes::geo_node_boolean_init); ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec; 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 83d3558a7cd..fdc6b12095c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -19,20 +19,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_bounding_box_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_bounding_box_out[] = { - {SOCK_GEOMETRY, N_("Bounding Box")}, - {SOCK_VECTOR, N_("Min")}, - {SOCK_VECTOR, N_("Max")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>("Bounding Box"); + b.add_output<decl::Vector>("Min"); + b.add_output<decl::Vector>("Max"); +} + using bke::GeometryInstanceGroup; static void compute_min_max_from_position_and_transform(const GeometryComponent &component, @@ -156,8 +152,8 @@ static void geo_node_bounding_box_exec(GeoNodeExecParams params) else { const float3 scale = max - min; const float3 center = min + scale / 2.0f; - Mesh *mesh = create_cube_mesh(1.0f); - transform_mesh(mesh, center, float3(0), scale); + Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); + transform_mesh(mesh, center, float3(0), float3(1)); params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh)); params.set_output("Min", min); params.set_output("Max", max); @@ -171,7 +167,7 @@ void register_node_type_geo_bounding_box() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_BOUNDING_BOX, "Bounding Box", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_bounding_box_in, geo_node_bounding_box_out); + ntype.declare = blender::nodes::geo_node_bounding_box_declare; ntype.geometry_node_execute = blender::nodes::geo_node_bounding_box_exec; nodeRegisterType(&ntype); } 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 b2dc4661e9c..f4c295b06fb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -23,32 +23,19 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_collection_info_in[] = { - {SOCK_COLLECTION, - N_("Collection"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - PROP_NONE, - SOCK_HIDE_LABEL}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_collection_info_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_collection_info_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Collection>("Collection").hide_label(); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -namespace blender::nodes { - static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN( @@ -98,13 +85,13 @@ void register_node_type_geo_collection_info() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_COLLECTION_INFO, "Collection Info", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, geo_node_collection_info_in, geo_node_collection_info_out); + ntype.declare = blender::nodes::geo_node_collection_info_declare; node_type_init(&ntype, blender::nodes::geo_node_collection_info_node_init); node_type_storage(&ntype, "NodeGeometryCollectionInfo", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_collection_info_exec; - ntype.draw_buttons = geo_node_collection_info_layout; + ntype.draw_buttons = blender::nodes::geo_node_collection_info_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 91e08d7777b..f1e10e3d276 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -28,18 +28,14 @@ # include "RBI_hull_api.h" #endif -static bNodeSocketTemplate geo_node_convex_hull_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_convex_hull_out[] = { - {SOCK_GEOMETRY, N_("Convex Hull")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_convex_hull_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>("Convex Hull"); +} + using bke::GeometryInstanceGroup; #ifdef WITH_BULLET @@ -317,7 +313,7 @@ void register_node_type_geo_convex_hull() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CONVEX_HULL, "Convex Hull", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_convex_hull_in, geo_node_convex_hull_out); + ntype.declare = blender::nodes::geo_node_convex_hull_declare; ntype.geometry_node_execute = blender::nodes::geo_node_convex_hull_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc index 1f878259f30..7853c5aa04a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc @@ -25,19 +25,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_endpoints_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_endpoints_out[] = { - {SOCK_GEOMETRY, N_("Start Points")}, - {SOCK_GEOMETRY, N_("End Points")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_curve_endpoints_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>("Start Points"); + b.add_output<decl::Geometry>("End Points"); +} + /** * Evaluate splines in parallel to speed up the rest of the node's execution. */ @@ -60,25 +56,26 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, Span<int> offsets, PointCloudComponent &points) { - curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (meta_data.domain != ATTR_DOMAIN_CURVE) { - return true; - } - GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( - name, ATTR_DOMAIN_CURVE, meta_data.data_type); - - OutputAttribute result_attribute = points.attribute_try_get_for_output_only( - name, ATTR_DOMAIN_POINT, meta_data.data_type); - GMutableSpan result = result_attribute.as_span(); - - /* Only copy the attributes of splines in the offsets. */ - for (const int i : offsets.index_range()) { - spline_attribute->get(offsets[i], result[i]); - } - - result_attribute.save(); - return true; - }); + curve_component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain != ATTR_DOMAIN_CURVE) { + return true; + } + GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); + + OutputAttribute result_attribute = points.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); + GMutableSpan result = result_attribute.as_span(); + + /* Only copy the attributes of splines in the offsets. */ + for (const int i : offsets.index_range()) { + spline_attribute->get(offsets[i], result[i]); + } + + result_attribute.save(); + return true; + }); } /** @@ -128,20 +125,20 @@ static void copy_endpoint_attributes(Span<SplinePtr> splines, /* Copy the point attribute data over. */ for (const auto &item : start_data.point_attributes.items()) { - const StringRef name = item.key; + const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; - BLI_assert(spline.attributes.get_for_read(name)); - GSpan spline_span = *spline.attributes.get_for_read(name); + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]); } for (const auto &item : end_data.point_attributes.items()) { - const StringRef name = item.key; + const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; - BLI_assert(spline.attributes.get_for_read(name)); - GSpan spline_span = *spline.attributes.get_for_read(name); + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); blender::fn::GVArray_For_GSpan(spline_span).get(spline.size() - 1, point_span[i]); } } @@ -216,7 +213,7 @@ void register_node_type_geo_curve_endpoints() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_curve_endpoints_in, geo_node_curve_endpoints_out); + ntype.declare = blender::nodes::geo_node_curve_endpoints_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoints_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc new file mode 100644 index 00000000000..d8f40b0a0df --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -0,0 +1,175 @@ +/* + * 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_delaunay_2d.h" +#include "BLI_double2.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_spline.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Curve"); + b.add_output<decl::Geometry>("Mesh"); +} + +static void geo_node_curve_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_fill_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurveFill *data = (NodeGeometryCurveFill *)MEM_callocN(sizeof(NodeGeometryCurveFill), + __func__); + + data->mode = GEO_NODE_CURVE_FILL_MODE_TRIANGULATED; + node->storage = data; +} + +static blender::meshintersect::CDT_result<double> do_cdt(const CurveEval &curve, + const CDT_output_type output_type) +{ + Span<SplinePtr> splines = curve.splines(); + blender::meshintersect::CDT_input<double> input; + input.need_ids = false; + Array<int> offsets = curve.evaluated_point_offsets(); + input.vert.reinitialize(offsets.last()); + input.face.reinitialize(splines.size()); + + for (const int i_spline : splines.index_range()) { + const SplinePtr &spline = splines[i_spline]; + const int vert_offset = offsets[i_spline]; + + Span<float3> positions = spline->evaluated_positions(); + for (const int i : positions.index_range()) { + input.vert[vert_offset + i] = double2(positions[i].x, positions[i].y); + } + + input.face[i_spline].resize(spline->evaluated_edges_size()); + MutableSpan<int> face_verts = input.face[i_spline]; + for (const int i : IndexRange(spline->evaluated_edges_size())) { + face_verts[i] = vert_offset + i; + } + } + blender::meshintersect::CDT_result<double> result = delaunay_2d_calc(input, output_type); + return result; +} + +/* Converts the CDT result into a Mesh. */ +static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &result) +{ + int vert_len = result.vert.size(); + int edge_len = result.edge.size(); + int poly_len = result.face.size(); + int loop_len = 0; + + for (const Vector<int> &face : result.face) { + loop_len += face.size(); + } + + Mesh *mesh = BKE_mesh_new_nomain(vert_len, edge_len, 0, loop_len, poly_len); + 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}; + + for (const int i : IndexRange(result.vert.size())) { + copy_v3_v3(verts[i].co, float3((float)result.vert[i].x, (float)result.vert[i].y, 0.0f)); + } + for (const int i : IndexRange(result.edge.size())) { + edges[i].v1 = result.edge[i].first; + edges[i].v2 = result.edge[i].second; + edges[i].flag = ME_EDGEDRAW | ME_EDGERENDER; + } + int i_loop = 0; + for (const int i : IndexRange(result.face.size())) { + polys[i].loopstart = i_loop; + polys[i].totloop = result.face[i].size(); + for (const int j : result.face[i].index_range()) { + loops[i_loop].v = result.face[i][j]; + i_loop++; + } + } + + /* The delaunay triangulation doesn't seem to return all of the necessary edges, even in + * triangulation mode. */ + BKE_mesh_calc_edges(mesh, true, false); + return mesh; +} + +static Mesh *curve_fill_calculate(GeoNodeExecParams ¶ms, const CurveComponent &component) +{ + const CurveEval &curve = *component.get_for_read(); + if (curve.splines().size() == 0) { + return nullptr; + } + + const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage; + const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode; + + const CDT_output_type output_type = (mode == GEO_NODE_CURVE_FILL_MODE_NGONS) ? + CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES : + CDT_INSIDE_WITH_HOLES; + + const blender::meshintersect::CDT_result<double> results = do_cdt(curve, output_type); + return cdt_to_mesh(results); +} + +static void geo_node_curve_fill_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Mesh", GeometrySet()); + return; + } + + Mesh *mesh = curve_fill_calculate(params, + *geometry_set.get_component_for_read<CurveComponent>()); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_fill() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_FILL, "Curve Fill", NODE_CLASS_GEOMETRY, 0); + + node_type_init(&ntype, blender::nodes::geo_node_curve_fill_init); + node_type_storage( + &ntype, "NodeGeometryCurveFill", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_curve_fill_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_fill_exec; + ntype.draw_buttons = blender::nodes::geo_node_curve_fill_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index 306085e3b75..8fe054633a1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -17,18 +17,14 @@ #include "BKE_spline.hh" #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_length_in[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_length_out[] = { - {SOCK_FLOAT, N_("Length")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_curve_length_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Curve"); + b.add_output<decl::Float>("Length"); +} + static void geo_node_curve_length_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); @@ -52,7 +48,7 @@ void register_node_type_geo_curve_length() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_curve_length_in, geo_node_curve_length_out); + ntype.declare = blender::nodes::geo_node_curve_length_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index 78b5b109419..313473e3442 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -21,27 +21,20 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_primitive_bezier_segment_in[] = { - {SOCK_INT, N_("Resolution"), 16.0f, 0.0f, 0.0f, 0.0f, 1, 256, PROP_UNSIGNED}, - {SOCK_VECTOR, N_("Start"), -1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, - N_("Start Handle"), - -0.5f, - 0.5f, - 0.0f, - 0.0f, - -FLT_MAX, - FLT_MAX, - PROP_TRANSLATION}, - {SOCK_VECTOR, N_("End Handle"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("End"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Resolution").default_value(16).min(1).max(256).subtype(PROP_UNSIGNED); + b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("Start Handle") + .default_value({-0.5f, 0.5f, 0.0f}) + .subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("End Handle").subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>("Curve"); +} -static bNodeSocketTemplate geo_node_curve_primitive_bezier_segment_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; static void geo_node_curve_primitive_bezier_segment_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -49,8 +42,6 @@ static void geo_node_curve_primitive_bezier_segment_layout(uiLayout *layout, uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -namespace blender::nodes { - static void geo_node_curve_primitive_bezier_segment_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurvePrimitiveBezierSegment *data = (NodeGeometryCurvePrimitiveBezierSegment *) @@ -135,15 +126,13 @@ void register_node_type_geo_curve_primitive_bezier_segment() static bNodeType ntype; geo_node_type_base( &ntype, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, "Bezier Segment", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, - geo_node_curve_primitive_bezier_segment_in, - geo_node_curve_primitive_bezier_segment_out); node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_bezier_segment_init); node_type_storage(&ntype, "NodeGeometryCurvePrimitiveBezierSegment", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = geo_node_curve_primitive_bezier_segment_layout; + ntype.declare = blender::nodes::geo_node_curve_primitive_bezier_segment_declare; + ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_bezier_segment_layout; ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_bezier_segment_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index ae947b7aeed..f5eb83ea4fd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -21,20 +21,20 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_primitive_circle_in[] = { - {SOCK_INT, N_("Resolution"), 32.0f, 0.0f, 0.0f, 0.0f, 3.0f, 512.0f}, - {SOCK_VECTOR, N_("Point 1"), -1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Point 2"), 0.0f, 1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Point 3"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_primitive_circle_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {SOCK_VECTOR, N_("Center")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Resolution").default_value(32).min(3).max(512); + b.add_input<decl::Vector>("Point 1") + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("Point 2").default_value({0.0f, 1.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("Point 3").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Curve"); + b.add_output<decl::Vector>("Center"); +} static void geo_node_curve_primitive_circle_layout(uiLayout *layout, bContext *UNUSED(C), @@ -43,8 +43,6 @@ static void geo_node_curve_primitive_circle_layout(uiLayout *layout, uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -namespace blender::nodes { - static void geo_node_curve_primitive_circle_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurvePrimitiveCircle *data = (NodeGeometryCurvePrimitiveCircle *)MEM_callocN( @@ -209,8 +207,6 @@ void register_node_type_geo_curve_primitive_circle() static bNodeType ntype; geo_node_type_base( &ntype, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, "Curve Circle", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_curve_primitive_circle_in, geo_node_curve_primitive_circle_out); node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_circle_init); node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_circle_update); @@ -218,8 +214,8 @@ void register_node_type_geo_curve_primitive_circle() "NodeGeometryCurvePrimitiveCircle", node_free_standard_storage, node_copy_standard_storage); - + ntype.declare = blender::nodes::geo_node_curve_primitive_circle_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_circle_exec; - ntype.draw_buttons = geo_node_curve_primitive_circle_layout; + ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_circle_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index eed3a998ef6..a3d2ada612f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -21,18 +21,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_primitive_line_in[] = { - {SOCK_VECTOR, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("End"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Direction"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("Length"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_primitive_line_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>("Start").subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("End").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("Direction").default_value({0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>("Length").default_value(1.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Curve"); +} static void geo_node_curve_primitive_line_layout(uiLayout *layout, bContext *UNUSED(C), @@ -41,8 +39,6 @@ static void geo_node_curve_primitive_line_layout(uiLayout *layout, uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -namespace blender::nodes { - static void geo_node_curve_primitive_line_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurvePrimitiveLine *data = (NodeGeometryCurvePrimitiveLine *)MEM_callocN( @@ -132,15 +128,14 @@ void register_node_type_geo_curve_primitive_line() { static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_LINE, "Curve Line", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_curve_primitive_line_in, geo_node_curve_primitive_line_out); node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_line_init); node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_line_update); node_type_storage(&ntype, "NodeGeometryCurvePrimitiveLine", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_curve_primitive_line_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_line_exec; - ntype.draw_buttons = geo_node_curve_primitive_line_layout; + ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_line_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index 10211cb2287..a54fd971ac4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -17,21 +17,17 @@ #include "BKE_spline.hh" #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_primitive_quadratic_bezier_in[] = { - {SOCK_INT, N_("Resolution"), 16.0f, 0.0f, 0.0f, 0.0f, 3, 256, PROP_UNSIGNED}, - {SOCK_VECTOR, N_("Start"), -1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Middle"), 0.0f, 2.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("End"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_primitive_quadratic_bezier_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Resolution").default_value(16).min(3).max(256).subtype(PROP_UNSIGNED); + b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("Middle").default_value({0.0f, 2.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>("Curve"); +} + static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, const float3 p2, const float3 p3, @@ -74,9 +70,7 @@ void register_node_type_geo_curve_primitive_quadratic_bezier() "Quadratic Bezier", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, - geo_node_curve_primitive_quadratic_bezier_in, - geo_node_curve_primitive_quadratic_bezier_out); + ntype.declare = blender::nodes::geo_node_curve_primitive_quadratic_bezier_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_quadratic_bezier_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 5f3f159c305..07ddaa8f61e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -19,25 +19,23 @@ #include "UI_resources.h" #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_primitive_quadrilateral_in[] = { - {SOCK_FLOAT, N_("Width"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Height"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Bottom Width"), 4.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Top Width"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Offset"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Bottom Height"), 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Top Height"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {SOCK_VECTOR, N_("Point 1"), -1.0f, 1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {SOCK_VECTOR, N_("Point 2"), 1.0f, 1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {SOCK_VECTOR, N_("Point 3"), 1.0f, -1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {SOCK_VECTOR, N_("Point 4"), -1.0f, -1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_primitive_quadrilateral_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Height").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Bottom Width").default_value(4.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Top Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Offset").default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Bottom Height").default_value(3.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Top Height").default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Vector>("Point 1").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); + b.add_input<decl::Vector>("Point 2").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); + b.add_input<decl::Vector>("Point 3").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); + b.add_input<decl::Vector>("Point 4").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Curve"); +} static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout, bContext *UNUSED(C), @@ -54,8 +52,6 @@ static void geo_node_curve_primitive_quadrilateral_init(bNodeTree *UNUSED(tree), node->storage = data; } -namespace blender::nodes { - static void geo_node_curve_primitive_quadrilateral_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryCurvePrimitiveQuad &node_storage = *(NodeGeometryCurvePrimitiveQuad *)node->storage; @@ -110,10 +106,10 @@ static void create_rectangle_curve(MutableSpan<float3> positions, const float height, const float width) { - positions[0] = float3(width / 2.0f, -height / 2.0f, 0.0f); - positions[1] = float3(-width / 2.0f, -height / 2.0f, 0.0f); - positions[2] = float3(-width / 2.0f, height / 2.0f, 0.0f); - positions[3] = float3(width / 2.0f, height / 2.0f, 0.0f); + positions[0] = float3(width / 2.0f, height / 2.0f, 0.0f); + positions[1] = float3(-width / 2.0f, height / 2.0f, 0.0f); + positions[2] = float3(-width / 2.0f, -height / 2.0f, 0.0f); + positions[3] = float3(width / 2.0f, -height / 2.0f, 0.0f); } static void create_points_curve(MutableSpan<float3> positions, @@ -133,10 +129,10 @@ static void create_parallelogram_curve(MutableSpan<float3> positions, const float width, const float offset) { - positions[0] = float3(width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); - positions[1] = float3(-width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); - positions[2] = float3(-width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); - positions[3] = float3(width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); + positions[0] = float3(width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); + positions[1] = float3(-width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); + positions[2] = float3(-width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); + positions[3] = float3(width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); } static void create_trapezoid_curve(MutableSpan<float3> positions, const float bottom, @@ -144,10 +140,10 @@ static void create_trapezoid_curve(MutableSpan<float3> positions, const float offset, const float height) { - positions[0] = float3(bottom / 2.0f, -height / 2.0f, 0.0f); - positions[1] = float3(-bottom / 2.0f, -height / 2.0f, 0.0f); - positions[2] = float3(-top / 2.0f + offset, height / 2.0f, 0.0f); - positions[3] = float3(top / 2.0f + offset, height / 2.0f, 0.0f); + positions[0] = float3(top / 2.0f + offset, height / 2.0f, 0.0f); + positions[1] = float3(-top / 2.0f + offset, height / 2.0f, 0.0f); + positions[2] = float3(-bottom / 2.0f, -height / 2.0f, 0.0f); + positions[3] = float3(bottom / 2.0f, -height / 2.0f, 0.0f); } static void create_kite_curve(MutableSpan<float3> positions, @@ -155,10 +151,10 @@ static void create_kite_curve(MutableSpan<float3> positions, const float bottom_height, const float top_height) { - positions[0] = float3(-width / 2.0f, 0, 0); - positions[1] = float3(0, top_height, 0); - positions[2] = float3(width / 2, 0, 0); - positions[3] = float3(0, -bottom_height, 0); + positions[0] = float3(0, -bottom_height, 0); + positions[1] = float3(width / 2, 0, 0); + positions[2] = float3(0, top_height, 0); + positions[3] = float3(-width / 2.0f, 0, 0); } static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params) @@ -226,13 +222,11 @@ void register_node_type_geo_curve_primitive_quadrilateral() static bNodeType ntype; geo_node_type_base( &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, "Quadrilateral", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, - geo_node_curve_primitive_quadrilateral_in, - geo_node_curve_primitive_quadrilateral_out); + ntype.declare = blender::nodes::geo_node_curve_primitive_quadrilateral_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_quadrilateral_exec; - ntype.draw_buttons = geo_node_curve_primitive_quadrilateral_layout; + ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_quadrilateral_layout; node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_quadrilateral_update); - node_type_init(&ntype, geo_node_curve_primitive_quadrilateral_init); + node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_quadrilateral_init); node_type_storage(&ntype, "NodeGeometryCurvePrimitiveQuad", node_free_standard_storage, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 8e25fb7f403..0803d43e5c3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -18,23 +18,19 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_primitive_spiral_in[] = { - {SOCK_INT, N_("Resolution"), 32.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1024.0f, PROP_UNSIGNED}, - {SOCK_FLOAT, N_("Rotations"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_FLOAT}, - {SOCK_FLOAT, N_("Start Radius"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("End Radius"), 2.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Height"), 2.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, - {SOCK_BOOLEAN, N_("Reverse")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_primitive_spiral_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Resolution").default_value(32).min(1).max(1024).subtype(PROP_UNSIGNED); + b.add_input<decl::Float>("Rotations").default_value(2.0f).min(0.0f); + b.add_input<decl::Float>("Start Radius").default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("End Radius").default_value(2.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Height").default_value(2.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Bool>("Reverse"); + b.add_output<decl::Geometry>("Curve"); +} + static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, const int resolution, const float start_radius, @@ -45,7 +41,7 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - const int totalpoints = resolution * rotations; + const int totalpoints = std::max(int(resolution * rotations), 1); const float delta_radius = (end_radius - start_radius) / (float)totalpoints; float radius = start_radius; const float delta_height = height / (float)totalpoints; @@ -100,8 +96,7 @@ void register_node_type_geo_curve_primitive_spiral() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, "Spiral", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_curve_primitive_spiral_in, geo_node_curve_primitive_spiral_out); + ntype.declare = blender::nodes::geo_node_curve_primitive_spiral_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_spiral_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 2e2bb247e2b..6261146562d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -18,21 +18,17 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_primitive_star_in[] = { - {SOCK_INT, N_("Points"), 8.0f, 0.0f, 0.0f, 0.0f, 3, 256, PROP_UNSIGNED}, - {SOCK_FLOAT, N_("Inner Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Outer Radius"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Twist"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_primitive_star_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Points").default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); + b.add_input<decl::Float>("Inner Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Outer Radius").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Twist").subtype(PROP_ANGLE); + b.add_output<decl::Geometry>("Curve"); +} + static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, const float outer_radius, const float twist, @@ -74,8 +70,7 @@ void register_node_type_geo_curve_primitive_star() { static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_STAR, "Star", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_curve_primitive_star_in, geo_node_curve_primitive_star_out); + ntype.declare = blender::nodes::geo_node_curve_primitive_star_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_star_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index ad0c453c2af..e89d500fe57 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -30,21 +30,19 @@ using blender::fn::GVArray_For_GSpan; 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, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_curve_resample_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000); + b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Geometry"); +} 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); + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) @@ -68,88 +66,101 @@ static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); } -namespace blender::nodes { - struct SampleModeParam { GeometryNodeCurveSampleMode mode; std::optional<float> length; std::optional<int> count; }; -static SplinePtr resample_spline(const Spline &input_spline, const int count) +static SplinePtr resample_spline(const Spline &src, 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 || count == 1) { - output_spline->add_point(input_spline.positions().first(), - input_spline.tilts().first(), - input_spline.radii().first()); - output_spline->attributes.reallocate(1); - input_spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = input_spline.attributes.get_for_read(name); - BLI_assert(src); - if (!output_spline->attributes.create(name, meta_data.data_type)) { - BLI_assert_unreachable(); - return false; - } - std::optional<GMutableSpan> dst = output_spline->attributes.get_for_write(name); - if (!dst) { - BLI_assert_unreachable(); - return false; + std::unique_ptr<PolySpline> dst = std::make_unique<PolySpline>(); + Spline::copy_base_settings(src, *dst); + + if (src.evaluated_edges_size() < 1 || count == 1) { + dst->add_point(src.positions().first(), src.tilts().first(), src.radii().first()); + dst->attributes.reallocate(1); + src.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id); + if (dst->attributes.create(attribute_id, meta_data.data_type)) { + std::optional<GMutableSpan> dst_attribute = dst->attributes.get_for_write( + attribute_id); + if (dst_attribute) { + src_attribute->type().copy_assign(src_attribute->data(), dst_attribute->data()); + return true; + } } - src->type().copy_assign(src->data(), dst->data()); - return true; + BLI_assert_unreachable(); + return false; }, ATTR_DOMAIN_POINT); - return output_spline; + return dst; } - output_spline->resize(count); + dst->resize(count); - Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count); + Array<float> uniform_samples = src.sample_uniform_index_factors(count); - input_spline.sample_with_index_factors<float3>( - input_spline.evaluated_positions(), uniform_samples, output_spline->positions()); + src.sample_with_index_factors<float3>( + src.evaluated_positions(), uniform_samples, dst->positions()); - input_spline.sample_with_index_factors<float>( - input_spline.interpolate_to_evaluated(input_spline.radii()), - uniform_samples, - output_spline->radii()); + src.sample_with_index_factors<float>( + src.interpolate_to_evaluated(src.radii()), uniform_samples, dst->radii()); - input_spline.sample_with_index_factors<float>( - input_spline.interpolate_to_evaluated(input_spline.tilts()), - uniform_samples, - output_spline->tilts()); + src.sample_with_index_factors<float>( + src.interpolate_to_evaluated(src.tilts()), uniform_samples, dst->tilts()); - output_spline->attributes.reallocate(count); - input_spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(name); - BLI_assert(input_attribute); - if (!output_spline->attributes.create(name, meta_data.data_type)) { - BLI_assert_unreachable(); - return false; - } - std::optional<GMutableSpan> output_attribute = output_spline->attributes.get_for_write( - name); - if (!output_attribute) { - BLI_assert_unreachable(); - return false; + src.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> input_attribute = src.attributes.get_for_read(attribute_id); + if (dst->attributes.create(attribute_id, meta_data.data_type)) { + std::optional<GMutableSpan> output_attribute = dst->attributes.get_for_write( + attribute_id); + if (output_attribute) { + src.sample_with_index_factors(*src.interpolate_to_evaluated(*input_attribute), + uniform_samples, + *output_attribute); + return true; + } } - input_spline.sample_with_index_factors( - *input_spline.interpolate_to_evaluated(*input_attribute), - uniform_samples, - *output_attribute); + BLI_assert_unreachable(); + return false; + }, + ATTR_DOMAIN_POINT); + + return dst; +} + +static SplinePtr resample_spline_evaluated(const Spline &src) +{ + std::unique_ptr<PolySpline> dst = std::make_unique<PolySpline>(); + Spline::copy_base_settings(src, *dst); + dst->resize(src.evaluated_points_size()); + + dst->positions().copy_from(src.evaluated_positions()); + dst->positions().copy_from(src.evaluated_positions()); + src.interpolate_to_evaluated(src.radii())->materialize(dst->radii()); + src.interpolate_to_evaluated(src.tilts())->materialize(dst->tilts()); + + src.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id); + if (dst->attributes.create(attribute_id, meta_data.data_type)) { + std::optional<GMutableSpan> dst_attribute = dst->attributes.get_for_write(attribute_id); + if (dst_attribute) { + src.interpolate_to_evaluated(*src_attribute)->materialize(dst_attribute->data()); + return true; + } + } + BLI_assert_unreachable(); return true; }, ATTR_DOMAIN_POINT); - return output_spline; + return dst; } static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, @@ -173,11 +184,18 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { const float length = input_splines[i]->length(); - const int count = std::max(int(length / *mode_param.length), 1); + const int count = std::max(int(length / *mode_param.length) + 1, 1); output_splines[i] = resample_spline(*input_splines[i], count); } }); } + else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_EVALUATED) { + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + output_splines[i] = resample_spline_evaluated(*input_splines[i]); + } + }); + } output_curve->attributes = input_curve.attributes; @@ -226,12 +244,12 @@ 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; + ntype.declare = blender::nodes::geo_node_curve_resample_declare; + ntype.draw_buttons = blender::nodes::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); + node_type_init(&ntype, blender::nodes::geo_node_curve_resample_init); + node_type_update(&ntype, blender::nodes::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_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index e92d22a6064..32bcbe2c608 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -20,42 +20,13 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_reverse_in[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {SOCK_STRING, N_("Selection")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_reverse_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; - namespace blender::nodes { -/** - * Reverse the data in a MutableSpan object. - */ -template<typename T> static void reverse_data(MutableSpan<T> r_data) -{ - const int size = r_data.size(); - for (const int i : IndexRange(size / 2)) { - std::swap(r_data[size - 1 - i], r_data[i]); - } -} - -/** - * Reverse and Swap the data between 2 MutableSpans. - */ -template<typename T> static void reverse_data(MutableSpan<T> left, MutableSpan<T> right) +static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) { - BLI_assert(left.size() == right.size()); - const int size = left.size(); - - for (const int i : IndexRange(size / 2 + size % 2)) { - std::swap(left[i], right[size - 1 - i]); - std::swap(right[i], left[size - 1 - i]); - } + b.add_input<decl::Geometry>("Curve"); + b.add_input<decl::String>("Selection"); + b.add_output<decl::Geometry>("Curve"); } static void geo_node_curve_reverse_exec(GeoNodeExecParams params) @@ -78,42 +49,9 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - if (!selection[i]) { - continue; - } - - reverse_data<float3>(splines[i]->positions()); - reverse_data<float>(splines[i]->radii()); - reverse_data<float>(splines[i]->tilts()); - - splines[i]->attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional<blender::fn::GMutableSpan> output_attribute = - splines[i]->attributes.get_for_write(name); - if (!output_attribute) { - BLI_assert_unreachable(); - return false; - } - attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { - using T = decltype(dummy); - reverse_data(output_attribute->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - /* Deal with extra info on derived types. */ - if (BezierSpline *spline = dynamic_cast<BezierSpline *>(splines[i].get())) { - reverse_data<BezierSpline::HandleType>(spline->handle_types_left()); - reverse_data<BezierSpline::HandleType>(spline->handle_types_right()); - reverse_data<float3>(spline->handle_positions_left(), spline->handle_positions_right()); + if (selection[i]) { + splines[i]->reverse(); } - else if (NURBSpline *spline = dynamic_cast<NURBSpline *>(splines[i].get())) { - reverse_data<float>(spline->weights()); - } - /* Nothing to do for poly splines. */ - - splines[i]->mark_cache_invalid(); } }); @@ -125,8 +63,9 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) void register_node_type_geo_curve_reverse() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_curve_reverse_in, geo_node_curve_reverse_out); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_reverse_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc index fb21c05b0c0..dfcae2e65b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc @@ -23,16 +23,14 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_select_by_handle_type_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Selection")}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_select_by_handle_type_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +static void geo_node_select_by_handle_type_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Selection"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout, bContext *UNUSED(C), @@ -68,8 +66,6 @@ static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCu return BezierSpline::HandleType::Auto; } -namespace blender::nodes { - static void select_curve_by_handle_type(const CurveEval &curve, const BezierSpline::HandleType type, const GeometryNodeCurveHandleMode mode, @@ -131,17 +127,19 @@ void register_node_type_geo_select_by_handle_type() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_select_by_handle_type_in, geo_node_select_by_handle_type_out); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, + "Select by Handle Type", + NODE_CLASS_GEOMETRY, + 0); + ntype.declare = blender::nodes::geo_node_select_by_handle_type_declare; ntype.geometry_node_execute = blender::nodes::geo_node_select_by_handle_type_exec; - node_type_init(&ntype, geo_node_curve_select_by_handle_type_init); + node_type_init(&ntype, blender::nodes::geo_node_curve_select_by_handle_type_init); node_type_storage(&ntype, "NodeGeometryCurveSelectHandles", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = geo_node_curve_select_by_handle_type_layout; + ntype.draw_buttons = blender::nodes::geo_node_curve_select_by_handle_type_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 72bd8ab188d..31c13134f79 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -21,16 +21,14 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_set_handles_in[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {SOCK_STRING, N_("Selection")}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_curve_set_handles_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; +static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Curve"); + b.add_input<decl::String>("Selection"); + b.add_output<decl::Geometry>("Curve"); +} static void geo_node_curve_set_handles_layout(uiLayout *layout, bContext *UNUSED(C), @@ -40,8 +38,6 @@ static void geo_node_curve_set_handles_layout(uiLayout *layout, uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); } -namespace blender::nodes { - static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN( @@ -134,16 +130,15 @@ void register_node_type_geo_curve_set_handles() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_curve_set_handles_in, geo_node_curve_set_handles_out); + &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre; ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec; node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init); node_type_storage(&ntype, "NodeGeometryCurveSetHandles", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = geo_node_curve_set_handles_layout; + ntype.draw_buttons = blender::nodes::geo_node_curve_set_handles_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index fe3f42625ae..0ef107fd8a4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -23,16 +23,14 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_spline_type_in[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {SOCK_STRING, N_("Selection")}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_curve_spline_type_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; +static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Curve"); + b.add_input<decl::String>("Selection"); + b.add_output<decl::Geometry>("Curve"); +} static void geo_node_curve_spline_type_layout(uiLayout *layout, bContext *UNUSED(C), @@ -41,8 +39,6 @@ static void geo_node_curve_spline_type_layout(uiLayout *layout, uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); } -namespace blender::nodes { - static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( @@ -78,14 +74,14 @@ template<typename CopyFn> static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn) { input_spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = input_spline.attributes.get_for_read(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id); BLI_assert(src); - if (!output_spline.attributes.create(name, meta_data.data_type)) { + if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) { BLI_assert_unreachable(); return false; } - std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(name); + std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id); if (!dst) { BLI_assert_unreachable(); return false; @@ -292,16 +288,15 @@ void register_node_type_geo_curve_spline_type() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_curve_spline_type_in, geo_node_curve_spline_type_out); + &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_spline_type_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec; node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init); node_type_storage(&ntype, "NodeGeometryCurveSplineType", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = geo_node_curve_spline_type_layout; + ntype.draw_buttons = blender::nodes::geo_node_curve_spline_type_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 7908c26e2dc..0522f2b8981 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -29,17 +29,15 @@ using blender::fn::GVArray_For_GSpan; using blender::fn::GVArray_For_Span; using blender::fn::GVArray_Typed; -static bNodeSocketTemplate geo_node_curve_subdivide_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Cuts")}, - {SOCK_INT, N_("Cuts"), 1, 0, 0, 0, 0, 1000}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_curve_subdivide_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Cuts"); + b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -57,8 +55,6 @@ static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -namespace blender::nodes { - static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage; @@ -287,16 +283,16 @@ static void subdivide_dynamic_attributes(const Spline &src_spline, { const bool is_cyclic = src_spline.is_cyclic(); src_spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = src_spline.attributes.get_for_read(name); + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id); BLI_assert(src); - if (!dst_spline.attributes.create(name, meta_data.data_type)) { + if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) { /* Since the source spline of the same type had the attribute, adding it should work. */ BLI_assert_unreachable(); } - std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(name); + std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(attribute_id); BLI_assert(dst); attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) { @@ -381,14 +377,15 @@ void register_node_type_geo_curve_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_curve_subdivide_in, geo_node_curve_subdivide_out); - ntype.draw_buttons = geo_node_curve_subdivide_layout; + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; + ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout; node_type_storage(&ntype, "NodeGeometryCurveSubdivide", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, geo_node_curve_subdivide_init); + node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init); node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update); ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_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 index ae5ad4e350b..f46440fd949 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -30,18 +30,28 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {SOCK_GEOMETRY, N_("Profile Curve")}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = { - {SOCK_GEOMETRY, N_("Mesh")}, - {-1, ""}, -}; +static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>("Profile Curve"); + b.add_output<decl::Geometry>("Mesh"); +} -namespace blender::nodes { +/** Information about the creation of one curve spline and profile spline combination. */ +struct ResultInfo { + const Spline &spline; + const Spline &profile; + int vert_offset; + int edge_offset; + int loop_offset; + int poly_offset; + int spline_vert_len; + int spline_edge_len; + int profile_vert_len; + int profile_edge_len; +}; static void vert_extrude_to_mesh_data(const Spline &spline, const float3 profile_vert, @@ -79,44 +89,33 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges) } } -static void spline_extrude_to_mesh_data(const Spline &spline, - const Spline &profile_spline, - const int vert_offset, - const int edge_offset, - const int loop_offset, - const int poly_offset, +static void spline_extrude_to_mesh_data(const ResultInfo &info, MutableSpan<MVert> r_verts, MutableSpan<MEdge> r_edges, MutableSpan<MLoop> r_loops, MutableSpan<MPoly> r_polys) { - 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) { + const Spline &spline = info.spline; + const Spline &profile = info.profile; + if (info.profile_vert_len == 1) { vert_extrude_to_mesh_data(spline, - profile_spline.evaluated_positions()[0], + profile.evaluated_positions()[0], r_verts, r_edges, - vert_offset, - edge_offset); + info.vert_offset, + info.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)) { - const int profile_edge_offset = spline_edges_start + i_profile * spline_edge_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 spline_edges_start = info.edge_offset; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + const int profile_edge_offset = spline_edges_start + i_profile * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int i_next_ring = (i_ring == info.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_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; MEdge &edge = r_edges[profile_edge_offset + i_ring]; edge.v1 = ring_vert_offset + i_profile; @@ -126,13 +125,14 @@ static void spline_extrude_to_mesh_data(const Spline &spline, } /* Add the edges running along each profile ring. */ - const int profile_edges_start = spline_edges_start + profile_vert_len * spline_edge_len; - for (const int i_ring : IndexRange(spline_vert_len)) { - const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + const int profile_edges_start = spline_edges_start + + info.profile_vert_len * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; - const int ring_edge_offset = profile_edges_start + i_ring * profile_edge_len; - 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 ring_edge_offset = profile_edges_start + i_ring * info.profile_edge_len; + for (const int i_profile : IndexRange(info.profile_edge_len)) { + const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; MEdge &edge = r_edges[ring_edge_offset + i_profile]; edge.v1 = ring_vert_offset + i_profile; @@ -142,24 +142,25 @@ static void spline_extrude_to_mesh_data(const Spline &spline, } /* 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; + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int i_next_ring = (i_ring == info.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_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + const int next_ring_vert_offset = info.vert_offset + info.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; + const int ring_edge_start = profile_edges_start + info.profile_edge_len * i_ring; + const int next_ring_edge_offset = profile_edges_start + info.profile_edge_len * i_next_ring; - const int ring_poly_offset = poly_offset + i_ring * profile_edge_len; - const int ring_loop_offset = loop_offset + i_ring * profile_edge_len * 4; + const int ring_poly_offset = info.poly_offset + i_ring * info.profile_edge_len; + const int ring_loop_offset = info.loop_offset + i_ring * info.profile_edge_len * 4; - for (const int i_profile : IndexRange(profile_edge_len)) { + for (const int i_profile : IndexRange(info.profile_edge_len)) { const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4; - const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; + const int i_next_profile = (i_profile == info.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; + const int spline_edge_start = spline_edges_start + info.spline_edge_len * i_profile; + const int next_spline_edge_start = spline_edges_start + + info.spline_edge_len * i_next_profile; MPoly &poly = r_polys[ring_poly_offset + i_profile]; poly.loopstart = ring_segment_loop_offset; @@ -168,16 +169,16 @@ static void spline_extrude_to_mesh_data(const Spline &spline, MLoop &loop_a = r_loops[ring_segment_loop_offset]; loop_a.v = ring_vert_offset + i_profile; - loop_a.e = spline_edge_start + i_ring; + loop_a.e = ring_edge_start + i_profile; MLoop &loop_b = r_loops[ring_segment_loop_offset + 1]; - loop_b.v = next_ring_vert_offset + i_profile; - loop_b.e = next_ring_edge_offset + i_profile; + loop_b.v = ring_vert_offset + i_next_profile; + loop_b.e = next_spline_edge_start + i_ring; MLoop &loop_c = r_loops[ring_segment_loop_offset + 2]; loop_c.v = next_ring_vert_offset + i_next_profile; - loop_c.e = next_spline_edge_start + i_ring; + loop_c.e = next_ring_edge_offset + i_profile; MLoop &loop_d = r_loops[ring_segment_loop_offset + 3]; - loop_d.v = ring_vert_offset + i_next_profile; - loop_d.e = ring_edge_start + i_profile; + loop_d.v = next_ring_vert_offset + i_profile; + loop_d.e = spline_edge_start + i_ring; } } @@ -185,29 +186,30 @@ static void spline_extrude_to_mesh_data(const Spline &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(); + Span<float3> profile_positions = profile.evaluated_positions(); GVArray_Typed<float> radii = spline.interpolate_to_evaluated(spline.radii()); - for (const int i_ring : IndexRange(spline_vert_len)) { + for (const int i_ring : IndexRange(info.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]); - const int ring_vert_start = vert_offset + i_ring * profile_vert_len; - for (const int i_profile : IndexRange(profile_vert_len)) { + const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + for (const int i_profile : IndexRange(info.profile_vert_len)) { MVert &vert = r_verts[ring_vert_start + i_profile]; 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); + if (profile.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile); 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)); + mark_edges_sharp( + r_edges.slice(spline_edges_start + info.spline_edge_len * control_point_offsets[i], + info.spline_edge_len)); } } } @@ -276,6 +278,372 @@ static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<Spl return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)}; } +static AttributeDomain get_result_attribute_domain(const MeshComponent &component, + const AttributeIDRef &attribute_id) +{ + /* Only use a different domain if it is builtin and must only exist on one domain. */ + if (!component.attribute_is_builtin(attribute_id)) { + return ATTR_DOMAIN_POINT; + } + + std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(attribute_id); + if (!meta_data) { + /* This function has to return something in this case, but it shouldn't be used, + * so return an output that will assert later if the code attempts to handle it. */ + return ATTR_DOMAIN_AUTO; + } + + return meta_data->domain; +} + +/** + * The data stored in the attribute and its domain from #OutputAttribute, to avoid calling + * `as_span()` for every single profile and curve spline combination, and for readability. + */ +struct ResultAttributeData { + GMutableSpan data; + AttributeDomain domain; +}; + +static std::optional<ResultAttributeData> create_attribute_and_get_span( + MeshComponent &component, + const AttributeIDRef &attribute_id, + AttributeMetaData meta_data, + Vector<OutputAttribute> &r_attributes) +{ + const AttributeDomain domain = get_result_attribute_domain(component, attribute_id); + OutputAttribute attribute = component.attribute_try_get_for_output_only( + attribute_id, domain, meta_data.data_type); + if (!attribute) { + return std::nullopt; + } + + GMutableSpan span = attribute.as_span(); + r_attributes.append(std::move(attribute)); + return std::make_optional<ResultAttributeData>({span, domain}); +} + +/** + * Store the references to the attribute data from the curve and profile inputs. Here we rely on + * the invariants of the storage of curve attributes, that the order will be consistent between + * splines, and all splines will have the same attributes. + */ +struct ResultAttributes { + /** + * Result attributes on the mesh corresponding to each attribute on the curve input, in the same + * order. The data is optional only in case the attribute does not exist on the mesh for some + * reason, like "shade_smooth" when the result has no faces. + */ + Vector<std::optional<ResultAttributeData>> curve_point_attributes; + Vector<std::optional<ResultAttributeData>> curve_spline_attributes; + + /** + * Result attributes corresponding the attributes on the profile input, in the same order. The + * attributes are optional in case the attribute names correspond to a namse used by the curve + * input, in which case the curve input attributes take precedence. + */ + Vector<std::optional<ResultAttributeData>> profile_point_attributes; + Vector<std::optional<ResultAttributeData>> profile_spline_attributes; + + /** + * Because some builtin attributes are not stored contiguously, and the curve inputs might have + * attributes with those names, it's necessary to keep OutputAttributes around to give access to + * the result data in a contiguous array. + */ + Vector<OutputAttribute> attributes; +}; +static ResultAttributes create_result_attributes(const CurveEval &curve, + const CurveEval &profile, + Mesh &mesh) +{ + MeshComponent mesh_component; + mesh_component.replace(&mesh, GeometryOwnershipType::Editable); + Set<AttributeIDRef> curve_attributes; + + /* In order to prefer attributes on the main curve input when there are name collisions, first + * check the attributes on the curve, then add attributes on the profile that are not also on the + * main curve input. */ + ResultAttributes result; + curve.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + curve_attributes.add_new(id); + result.curve_point_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + return true; + }, + ATTR_DOMAIN_POINT); + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + curve_attributes.add_new(id); + result.curve_spline_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + return true; + }, + ATTR_DOMAIN_CURVE); + profile.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (curve_attributes.contains(id)) { + result.profile_point_attributes.append({}); + } + else { + result.profile_point_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + } + return true; + }, + ATTR_DOMAIN_POINT); + profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (curve_attributes.contains(id)) { + result.profile_spline_attributes.append({}); + } + else { + result.profile_spline_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + } + return true; + }, + ATTR_DOMAIN_CURVE); + + return result; +} + +template<typename T> +static void copy_curve_point_data_to_mesh_verts(const Span<T> src, + const ResultInfo &info, + MutableSpan<T> dst) +{ + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + dst.slice(ring_vert_start, info.profile_vert_len).fill(src[i_ring]); + } +} + +template<typename T> +static void copy_curve_point_data_to_mesh_edges(const Span<T> src, + const ResultInfo &info, + MutableSpan<T> dst) +{ + const int edges_start = info.edge_offset + info.profile_vert_len * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_edge_start = edges_start + info.profile_edge_len * i_ring; + dst.slice(ring_edge_start, info.profile_edge_len).fill(src[i_ring]); + } +} + +template<typename T> +static void copy_curve_point_data_to_mesh_faces(const Span<T> src, + const ResultInfo &info, + MutableSpan<T> dst) +{ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int ring_face_start = info.poly_offset + info.profile_edge_len * i_ring; + dst.slice(ring_face_start, info.profile_edge_len).fill(src[i_ring]); + } +} + +static void copy_curve_point_attribute_to_mesh(const GSpan src, + const ResultInfo &info, + ResultAttributeData &dst) +{ + GVArrayPtr interpolated_gvarray = info.spline.interpolate_to_evaluated(src); + GSpan interpolated = interpolated_gvarray->get_internal_span(); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst.domain) { + case ATTR_DOMAIN_POINT: + copy_curve_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>()); + break; + case ATTR_DOMAIN_EDGE: + copy_curve_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>()); + break; + case ATTR_DOMAIN_FACE: + copy_curve_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>()); + break; + case ATTR_DOMAIN_CORNER: + /* Unsupported for now, since there are no builtin attributes to convert into. */ + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +template<typename T> +static void copy_profile_point_data_to_mesh_verts(const Span<T> src, + const ResultInfo &info, + MutableSpan<T> dst) +{ + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int profile_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + dst[profile_vert_start + i_profile] = src[i_profile]; + } + } +} + +template<typename T> +static void copy_profile_point_data_to_mesh_edges(const Span<T> src, + const ResultInfo &info, + MutableSpan<T> dst) +{ + for (const int i_profile : IndexRange(info.profile_vert_len)) { + const int profile_edge_offset = info.edge_offset + i_profile * info.spline_edge_len; + dst.slice(profile_edge_offset, info.spline_edge_len).fill(src[i_profile]); + } +} + +template<typename T> +static void copy_profile_point_data_to_mesh_faces(const Span<T> src, + const ResultInfo &info, + MutableSpan<T> dst) +{ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int profile_face_start = info.poly_offset + i_ring * info.profile_edge_len; + for (const int i_profile : IndexRange(info.profile_edge_len)) { + dst[profile_face_start + i_profile] = src[i_profile]; + } + } +} + +static void copy_profile_point_attribute_to_mesh(const GSpan src, + const ResultInfo &info, + ResultAttributeData &dst) +{ + GVArrayPtr interpolated_gvarray = info.profile.interpolate_to_evaluated(src); + GSpan interpolated = interpolated_gvarray->get_internal_span(); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst.domain) { + case ATTR_DOMAIN_POINT: + copy_profile_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>()); + break; + case ATTR_DOMAIN_EDGE: + copy_profile_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>()); + break; + case ATTR_DOMAIN_FACE: + copy_profile_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>()); + break; + case ATTR_DOMAIN_CORNER: + /* Unsupported for now, since there are no builtin attributes to convert into. */ + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +static void copy_point_domain_attributes_to_mesh(const ResultInfo &info, + ResultAttributes &attributes) +{ + if (!attributes.curve_point_attributes.is_empty()) { + int i = 0; + info.spline.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.curve_point_attributes[i]) { + copy_curve_point_attribute_to_mesh(*info.spline.attributes.get_for_read(id), + info, + *attributes.curve_point_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } + if (!attributes.profile_point_attributes.is_empty()) { + int i = 0; + info.profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.profile_point_attributes[i]) { + copy_profile_point_attribute_to_mesh(*info.profile.attributes.get_for_read(id), + info, + *attributes.profile_point_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } +} + +template<typename T> +static void copy_spline_data_to_mesh(Span<T> src, Span<int> offsets, MutableSpan<T> dst) +{ + for (const int i : IndexRange(src.size())) { + dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); + } +} + +/** + * Since the offsets for each combination of curve and profile spline are stored for every mesh + * domain, and this just needs to fill the chunks corresponding to each combination, we can use + * the same function for all mesh domains. + */ +static void copy_spline_attribute_to_mesh(const GSpan src, + const ResultOffsets &offsets, + ResultAttributeData &dst_attribute) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst_attribute.domain) { + case ATTR_DOMAIN_POINT: + copy_spline_data_to_mesh(src.typed<T>(), offsets.vert, dst_attribute.data.typed<T>()); + break; + case ATTR_DOMAIN_EDGE: + copy_spline_data_to_mesh(src.typed<T>(), offsets.edge, dst_attribute.data.typed<T>()); + break; + case ATTR_DOMAIN_FACE: + copy_spline_data_to_mesh(src.typed<T>(), offsets.poly, dst_attribute.data.typed<T>()); + break; + case ATTR_DOMAIN_CORNER: + copy_spline_data_to_mesh(src.typed<T>(), offsets.loop, dst_attribute.data.typed<T>()); + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve, + const CurveEval &profile, + const ResultOffsets &offsets, + ResultAttributes &attributes) +{ + if (!attributes.curve_spline_attributes.is_empty()) { + int i = 0; + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.curve_spline_attributes[i]) { + copy_spline_attribute_to_mesh(*curve.attributes.get_for_read(id), + offsets, + *attributes.curve_spline_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_CURVE); + } + if (!attributes.profile_spline_attributes.is_empty()) { + int i = 0; + profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.profile_spline_attributes[i]) { + copy_spline_attribute_to_mesh(*profile.attributes.get_for_read(id), + offsets, + *attributes.profile_spline_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_CURVE); + } +} + /** * \note Normal calculation is by far the slowest part of calculations relating to the result mesh. * Although it would be a sensible decision to use the better topology information available while @@ -298,30 +666,52 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr BKE_id_material_eval_ensure_default_slot(&mesh->id); mesh->flag |= ME_AUTOSMOOTH; mesh->smoothresh = DEG2RADF(180.0f); - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); + + ResultAttributes attributes = create_result_attributes(curve, profile, *mesh); threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) { for (const int i_spline : curves_range) { + const Spline &spline = *curves[i_spline]; + if (spline.evaluated_points_size() == 0) { + continue; + } const int spline_start_index = i_spline * profiles.size(); threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) { for (const int i_profile : profiles_range) { + const Spline &profile = *profiles[i_profile]; const int i_mesh = spline_start_index + i_profile; - spline_extrude_to_mesh_data(*curves[i_spline], - *profiles[i_profile], - offsets.vert[i_mesh], - offsets.edge[i_mesh], - offsets.loop[i_mesh], - offsets.poly[i_mesh], + ResultInfo info{ + spline, + profile, + offsets.vert[i_mesh], + offsets.edge[i_mesh], + offsets.loop[i_mesh], + offsets.poly[i_mesh], + spline.evaluated_points_size(), + spline.evaluated_edges_size(), + profile.evaluated_points_size(), + profile.evaluated_edges_size(), + }; + + spline_extrude_to_mesh_data(info, {mesh->mvert, mesh->totvert}, {mesh->medge, mesh->totedge}, {mesh->mloop, mesh->totloop}, {mesh->mpoly, mesh->totpoly}); + + copy_point_domain_attributes_to_mesh(info, attributes); } }); } }); + copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes); + + for (OutputAttribute &output_attribute : attributes.attributes) { + output_attribute.save(); + } + return mesh; } @@ -374,7 +764,7 @@ 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.declare = blender::nodes::geo_node_curve_to_mesh_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index bb8f560f92d..74740ba244f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -26,17 +26,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_curve_to_points_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_INT, N_("Count"), 10, 0, 0, 0, 2, 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_to_points_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Int>("Count").default_value(10).min(2).max(100000); + b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -64,8 +62,6 @@ static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *nod nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); } -namespace blender::nodes { - /** * Evaluate splines in parallel to speed up the rest of the node's execution. */ @@ -105,7 +101,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - offset += splines[i]->length() / resolution; + offset += splines[i]->length() / resolution + 1; } offsets.last() = offset; return offsets; @@ -119,21 +115,21 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, } static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, - const StringRef name, + const AttributeIDRef &attribute_id, const CustomDataType data_type) { - points.attribute_try_create(name, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); - WriteAttributeLookup attribute = points.attribute_try_get_for_write(name); + points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); + WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); BLI_assert(attribute); return attribute.varray->get_internal_span(); } template<typename T> static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points, - const StringRef name) + const AttributeIDRef &attribute_id) { GMutableSpan attribute = create_attribute_and_retrieve_span( - points, name, bke::cpp_type_to_custom_data_type(CPPType::get<T>())); + points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>())); return attribute.typed<T>(); } @@ -151,9 +147,10 @@ CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponen /* Because of the invariants of the curve component, we use the attributes of the * first spline as a representative for the attribute meta data all splines. */ curve.splines().first()->attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { attributes.point_attributes.add_new( - name, create_attribute_and_retrieve_span(points, name, meta_data.data_type)); + attribute_id, + create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type)); return true; }, ATTR_DOMAIN_POINT); @@ -183,12 +180,12 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines, spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size)); spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size)); - for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) { - const StringRef name = item.key; + for (const Map<AttributeIDRef, GMutableSpan>::Item &item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; - BLI_assert(spline.attributes.get_for_read(name)); - GSpan spline_span = *spline.attributes.get_for_read(name); + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); spline.interpolate_to_evaluated(spline_span) ->materialize(point_span.slice(offset, size).data()); @@ -226,12 +223,12 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines, uniform_samples, data.tilts.slice(offset, size)); - for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) { - const StringRef name = item.key; + for (const Map<AttributeIDRef, GMutableSpan>::Item &item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; - BLI_assert(spline.attributes.get_for_read(name)); - GSpan spline_span = *spline.attributes.get_for_read(name); + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span), uniform_samples, @@ -261,31 +258,32 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, Span<int> offsets, PointCloudComponent &points) { - curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (meta_data.domain != ATTR_DOMAIN_CURVE) { - return true; - } - GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( - name, ATTR_DOMAIN_CURVE, meta_data.data_type); - const CPPType &type = spline_attribute->type(); - - OutputAttribute result_attribute = points.attribute_try_get_for_output_only( - name, ATTR_DOMAIN_POINT, meta_data.data_type); - GMutableSpan result = result_attribute.as_span(); - - for (const int i : IndexRange(spline_attribute->size())) { - const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - if (size != 0) { - BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); - spline_attribute->get(i, buffer); - type.fill_assign_n(buffer, result[offset], size); - } - } - - result_attribute.save(); - return true; - }); + curve_component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain != ATTR_DOMAIN_CURVE) { + return true; + } + GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); + const CPPType &type = spline_attribute->type(); + + OutputAttribute result_attribute = points.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); + GMutableSpan result = result_attribute.as_span(); + + for (const int i : IndexRange(spline_attribute->size())) { + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + if (size != 0) { + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + spline_attribute->get(i, buffer); + type.fill_assign_n(buffer, result[offset], size); + } + } + + result_attribute.save(); + return true; + }); } void curve_create_default_rotation_attribute(Span<float3> tangents, @@ -361,13 +359,13 @@ void register_node_type_geo_curve_to_points() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_curve_to_points_in, geo_node_curve_to_points_out); + ntype.declare = blender::nodes::geo_node_curve_to_points_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec; - ntype.draw_buttons = geo_node_curve_to_points_layout; + ntype.draw_buttons = blender::nodes::geo_node_curve_to_points_layout; node_type_storage( &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, geo_node_curve_to_points_init); - node_type_update(&ntype, geo_node_curve_to_points_update); + node_type_init(&ntype, blender::nodes::geo_node_curve_to_points_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_to_points_update); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index f9415c6d27b..4ad311d63c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -24,19 +24,17 @@ using blender::attribute_math::mix2; -static bNodeSocketTemplate geo_node_curve_trim_in[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("End"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 10000.0f, PROP_DISTANCE}, - {SOCK_FLOAT, N_("End"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_DISTANCE}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_curve_trim_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; +static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Curve"); + b.add_input<decl::Float>("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>("End").min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("End", "End_001").min(0.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Curve"); +} static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -69,8 +67,6 @@ static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node) nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); } -namespace blender::nodes { - struct TrimLocation { /* Control point index at the start side of the trim location. */ int left_index; @@ -162,8 +158,8 @@ static void trim_poly_spline(Spline &spline, linear_trim_data<float>(start, end, spline.tilts()); spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> src = spline.attributes.get_for_write(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id); BLI_assert(src); attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { using T = decltype(dummy); @@ -197,14 +193,14 @@ static PolySpline trim_nurbs_spline(const Spline &spline, /* Copy generic attribute data. */ spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = spline.attributes.get_for_read(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id); BLI_assert(src); - if (!new_spline.attributes.create(name, meta_data.data_type)) { + if (!new_spline.attributes.create(attribute_id, meta_data.data_type)) { BLI_assert_unreachable(); return false; } - std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(name); + std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id); BLI_assert(dst); attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { @@ -253,8 +249,8 @@ static void trim_bezier_spline(Spline &spline, linear_trim_data<float>(start, end, bezier_spline.radii()); linear_trim_data<float>(start, end, bezier_spline.tilts()); spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> src = spline.attributes.get_for_write(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id); BLI_assert(src); attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { using T = decltype(dummy); @@ -399,12 +395,12 @@ void register_node_type_geo_curve_trim() { static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_curve_trim_in, geo_node_curve_trim_out); ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec; - ntype.draw_buttons = geo_node_curve_trim_layout; + ntype.draw_buttons = blender::nodes::geo_node_curve_trim_layout; + ntype.declare = blender::nodes::geo_node_curve_trim_declare; node_type_storage( &ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, geo_node_curve_trim_init); - node_type_update(&ntype, geo_node_curve_trim_update); + node_type_init(&ntype, blender::nodes::geo_node_curve_trim_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_trim_update); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 8c697275f88..1e2f652cd78 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -43,20 +43,16 @@ extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, blender::Span<int> masked_poly_indices, blender::Span<int> new_loop_starts); -static bNodeSocketTemplate geo_node_delete_geometry_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Selection")}, - {SOCK_BOOLEAN, N_("Invert")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_delete_geometry_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Selection"); + b.add_input<decl::Bool>("Invert"); + b.add_output<decl::Geometry>("Geometry"); +} + template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask) { for (const int i_out : mask.index_range()) { @@ -97,16 +93,17 @@ static void copy_dynamic_attributes(const CustomDataAttributes &src, const IndexMask mask) { src.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional<GSpan> src_attribute = src.get_for_read(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src_attribute = src.get_for_read(attribute_id); BLI_assert(src_attribute); - if (!dst.create(name, meta_data.data_type)) { - /* Since the source spline of the same type had the attribute, adding it should work. */ + if (!dst.create(attribute_id, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. + */ BLI_assert_unreachable(); } - std::optional<GMutableSpan> new_attribute = dst.get_for_write(name); + std::optional<GMutableSpan> new_attribute = dst.get_for_write(attribute_id); BLI_assert(new_attribute); attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { @@ -304,8 +301,8 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, } /** - * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the edge - * are kept along with the edge. + * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the + * edge are kept along with the edge. */ static void compute_selected_vertices_and_edges_from_edge_selection( const Mesh &mesh, @@ -562,7 +559,7 @@ static Mesh *delete_mesh_selection(const Mesh &mesh_in, mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts); BKE_mesh_calc_edges_loose(result); /* Tag to recalculate normals later. */ - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } @@ -671,8 +668,10 @@ void register_node_type_geo_delete_geometry() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + + ntype.declare = blender::nodes::geo_node_delete_geometry_declare; ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 740b828d503..867fecea251 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -22,29 +22,21 @@ extern "C" { Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd); } -static bNodeSocketTemplate geo_node_edge_split_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_BOOLEAN, N_("Edge Angle"), true}, - {SOCK_FLOAT, - N_("Angle"), - DEG2RADF(30.0f), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - DEG2RADF(180.0f), - PROP_ANGLE}, - {SOCK_BOOLEAN, N_("Sharp Edges")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_edge_split_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Bool>("Edge Angle").default_value(true); + b.add_input<decl::Float>("Angle") + .default_value(DEG2RADF(30.0f)) + .min(0.0f) + .max(DEG2RADF(180.0f)) + .subtype(PROP_ANGLE); + b.add_input<decl::Bool>("Sharp Edges"); + b.add_output<decl::Geometry>("Geometry"); +} + static void geo_node_edge_split_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -91,7 +83,7 @@ void register_node_type_geo_edge_split() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_edge_split_in, geo_node_edge_split_out); ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec; + ntype.declare = blender::nodes::geo_node_edge_split_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc new file mode 100644 index 00000000000..c52ff3d448e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -0,0 +1,60 @@ +/* + * 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" + +namespace blender::nodes { + +static void geo_node_input_index_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>("Index"); +} + +class IndexFieldInput final : public fn::FieldInput { + public: + IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index") + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &UNUSED(context), + IndexMask mask, + ResourceScope &scope) const final + { + /* TODO: Investigate a similar method to IndexRange::as_span() */ + auto index_func = [](int i) { return i; }; + return &scope.construct< + fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>( + mask.min_array_size(), mask.min_array_size(), index_func); + } +}; + +static void geo_node_input_index_exec(GeoNodeExecParams params) +{ + Field<int> index_field{std::make_shared<IndexFieldInput>()}; + params.set_output("Index", std::move(index_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_index() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_INDEX, "Index", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_index_exec; + ntype.declare = blender::nodes::geo_node_input_index_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc index 6bad71a62a2..8e805bd1359 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc @@ -19,18 +19,18 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_input_material_out[] = { - {SOCK_MATERIAL, N_("Material")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_input_material_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Material>("Material"); +} static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "material", 0, "", ICON_NONE); } -namespace blender::nodes { - static void geo_node_input_material_exec(GeoNodeExecParams params) { Material *material = (Material *)params.node().id; @@ -44,8 +44,8 @@ void register_node_type_geo_input_material() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, geo_node_input_material_out); - ntype.draw_buttons = geo_node_input_material_layout; + ntype.draw_buttons = blender::nodes::geo_node_input_material_layout; + ntype.declare = blender::nodes::geo_node_input_material_declare; ntype.geometry_node_execute = blender::nodes::geo_node_input_material_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc new file mode 100644 index 00000000000..07818f2a3ad --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -0,0 +1,211 @@ +/* + * 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 "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_normal_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>("Normal"); +} + +static GVArrayPtr mesh_face_normals(const Mesh &mesh, + const Span<MVert> verts, + const Span<MPoly> polys, + const Span<MLoop> loops, + const IndexMask mask) +{ + /* Use existing normals to avoid unnecessarily recalculating them, if possible. */ + if (!(mesh.runtime.cd_dirty_poly & CD_MASK_NORMAL) && + CustomData_has_layer(&mesh.pdata, CD_NORMAL)) { + const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL); + + return std::make_unique<fn::GVArray_For_Span<float3>>( + Span<float3>((const float3 *)data, polys.size())); + } + + auto normal_fn = [verts, polys, loops](const int i) -> float3 { + float3 normal; + const MPoly &poly = polys[i]; + BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), normal); + return normal; + }; + + return std::make_unique< + fn::GVArray_For_EmbeddedVArray<float3, VArray_For_Func<float3, decltype(normal_fn)>>>( + mask.min_array_size(), mask.min_array_size(), normal_fn); +} + +static GVArrayPtr mesh_vertex_normals(const Mesh &mesh, + const Span<MVert> verts, + const Span<MPoly> polys, + const Span<MLoop> loops, + const IndexMask mask) +{ + /* Use existing normals to avoid unnecessarily recalculating them, if possible. */ + if (!(mesh.runtime.cd_dirty_vert & CD_MASK_NORMAL) && + CustomData_has_layer(&mesh.vdata, CD_NORMAL)) { + const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL); + + return std::make_unique<fn::GVArray_For_Span<float3>>( + Span<float3>((const float3 *)data, mesh.totvert)); + } + + /* If the normals are dirty, they must be recalculated for the output of this node's field + * source. Ideally vertex normals could be calculated lazily on a const mesh, but that's not + * possible at the moment, so we take ownership of the results. Sadly we must also create a copy + * of MVert to use the mesh normals API. This can be improved by adding mutex-protected lazy + * calculation of normals on meshes. + * + * Use mask.min_array_size() to avoid calculating a final chunk of data if possible. */ + Array<MVert> temp_verts(verts); + Array<float3> normals(verts.size()); /* Use full size for accumulation from faces. */ + BKE_mesh_calc_normals_poly_and_vertex(temp_verts.data(), + mask.min_array_size(), + loops.data(), + loops.size(), + polys.data(), + polys.size(), + nullptr, + (float(*)[3])normals.data()); + + return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals)); +} + +static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_component, + const Mesh &mesh, + const IndexMask mask, + const AttributeDomain domain, + ResourceScope &scope) +{ + Span<MVert> verts{mesh.mvert, mesh.totvert}; + Span<MEdge> edges{mesh.medge, mesh.totedge}; + Span<MPoly> polys{mesh.mpoly, mesh.totpoly}; + Span<MLoop> loops{mesh.mloop, mesh.totloop}; + + switch (domain) { + case ATTR_DOMAIN_FACE: { + return scope.add_value(mesh_face_normals(mesh, verts, polys, loops, mask)).get(); + } + case ATTR_DOMAIN_POINT: { + return scope.add_value(mesh_vertex_normals(mesh, verts, polys, loops, mask)).get(); + } + case ATTR_DOMAIN_EDGE: { + /* In this case, start with vertex normals and convert to the edge domain, since the + * conversion from edges to vertices is very simple. Use the full mask since the edges + * might use the vertex normal from any index. */ + GVArrayPtr vert_normals = mesh_vertex_normals( + mesh, verts, polys, loops, IndexRange(verts.size())); + Span<float3> vert_normals_span = vert_normals->get_internal_span().typed<float3>(); + Array<float3> edge_normals(mask.min_array_size()); + + /* Use "manual" domain interpolation instead of the GeometryComponent API to avoid + * calculating unnecessary values and to allow normalizing the result much more simply. */ + for (const int i : mask) { + const MEdge &edge = edges[i]; + edge_normals[i] = float3::interpolate( + vert_normals_span[edge.v1], vert_normals_span[edge.v2], 0.5f) + .normalized(); + } + + return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float3>>>( + std::move(edge_normals)); + } + case ATTR_DOMAIN_CORNER: { + /* The normals on corners are just the mesh's face normals, so start with the face normal + * array and copy the face normal for each of its corners. */ + GVArrayPtr face_normals = mesh_face_normals( + mesh, verts, polys, loops, IndexRange(polys.size())); + + /* In this case using the mesh component's generic domain interpolation is fine, the data + * will still be normalized, since the face normal is just copied to every corner. */ + GVArrayPtr loop_normals = mesh_component.attribute_try_adapt_domain( + std::move(face_normals), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); + return scope.add_value(std::move(loop_normals)).get(); + } + default: + return nullptr; + } +} + +class NormalFieldInput final : public fn::FieldInput { + public: + NormalFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Normal") + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast<const GeometryComponentFieldContext *>(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return nullptr; + } + + return construct_mesh_normals_gvarray(mesh_component, *mesh, mask, domain, scope); + } + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + /* TODO: Add curve normals support. */ + return nullptr; + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 669605641; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const NormalFieldInput *>(&other) != nullptr; + } +}; + +static void geo_node_input_normal_exec(GeoNodeExecParams params) +{ + Field<float3> normal_field{std::make_shared<NormalFieldInput>()}; + params.set_output("Normal", std::move(normal_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_normal() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_NORMAL, "Normal", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_normal_exec; + ntype.declare = blender::nodes::geo_node_input_normal_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc new file mode 100644 index 00000000000..c6365bf6809 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -0,0 +1,43 @@ +/* + * 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" + +namespace blender::nodes { + +static void geo_node_input_position_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>("Position"); +} + +static void geo_node_input_position_exec(GeoNodeExecParams params) +{ + Field<float3> position_field{ + std::make_shared<bke::AttributeFieldInput>("position", CPPType::get<float3>())}; + params.set_output("Position", std::move(position_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_position() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_POSITION, "Position", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_position_exec; + ntype.declare = blender::nodes::geo_node_input_position_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc index ec875b9f983..f8a1c764f61 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc @@ -18,13 +18,13 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_is_viewport_out[] = { - {SOCK_BOOLEAN, N_("Is Viewport")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_is_viewport_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Bool>("Is Viewport"); +} + static void geo_node_is_viewport_exec(GeoNodeExecParams params) { const Depsgraph *depsgraph = params.depsgraph(); @@ -41,7 +41,7 @@ void register_node_type_geo_is_viewport() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_IS_VIEWPORT, "Is Viewport", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, geo_node_is_viewport_out); ntype.geometry_node_execute = blender::nodes::geo_node_is_viewport_exec; + ntype.declare = blender::nodes::geo_node_is_viewport_declare; 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 730cf08feaa..93643298f92 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -29,27 +29,14 @@ using blender::fn::GVArray_For_GSpan; -static bNodeSocketTemplate geo_node_join_geometry_in[] = { - {SOCK_GEOMETRY, - N_("Geometry"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - -1.0f, - 1.0f, - PROP_NONE, - SOCK_MULTI_INPUT}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_join_geometry_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry").multi_input(); + b.add_output<decl::Geometry>("Geometry"); +} + static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent *> src_components) { int totverts = 0; @@ -161,34 +148,35 @@ static Array<const GeometryComponent *> to_base_components(Span<const Component return components; } -static Map<std::string, AttributeMetaData> get_final_attribute_info( +static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info( Span<const GeometryComponent *> components, Span<StringRef> ignored_attributes) { - Map<std::string, AttributeMetaData> info; + Map<AttributeIDRef, AttributeMetaData> info; for (const GeometryComponent *component : components) { - component->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (ignored_attributes.contains(name)) { - return true; - } - info.add_or_modify( - name, - [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, - [&](AttributeMetaData *meta_data_final) { - meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity( - {meta_data_final->data_type, meta_data.data_type}); - meta_data_final->domain = blender::bke::attribute_domain_highest_priority( - {meta_data_final->domain, meta_data.domain}); - }); - return true; - }); + component->attribute_foreach( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { + return true; + } + info.add_or_modify( + attribute_id, + [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, + [&](AttributeMetaData *meta_data_final) { + meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity( + {meta_data_final->data_type, meta_data.data_type}); + meta_data_final->domain = blender::bke::attribute_domain_highest_priority( + {meta_data_final->domain, meta_data.domain}); + }); + return true; + }); } return info; } static void fill_new_attribute(Span<const GeometryComponent *> src_components, - StringRef attribute_name, + const AttributeIDRef &attribute_id, const CustomDataType data_type, const AttributeDomain domain, GMutableSpan dst_span) @@ -203,7 +191,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, continue; } GVArrayPtr read_attribute = component->attribute_get_for_read( - attribute_name, domain, data_type, nullptr); + attribute_id, domain, data_type, nullptr); GVArray_GSpan src_span{*read_attribute}; const void *src_buffer = src_span.data(); @@ -218,20 +206,21 @@ static void join_attributes(Span<const GeometryComponent *> src_components, GeometryComponent &result, Span<StringRef> ignored_attributes = {}) { - const Map<std::string, AttributeMetaData> info = get_final_attribute_info(src_components, - ignored_attributes); + const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components, + ignored_attributes); - for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) { - const StringRef name = item.key; + for (const Map<AttributeIDRef, AttributeMetaData>::Item &item : info.items()) { + const AttributeIDRef attribute_id = item.key; const AttributeMetaData &meta_data = item.value; OutputAttribute write_attribute = result.attribute_try_get_for_output_only( - name, meta_data.domain, meta_data.data_type); + attribute_id, meta_data.domain, meta_data.data_type); if (!write_attribute) { continue; } GMutableSpan dst_span = write_attribute.as_span(); - fill_new_attribute(src_components, name, meta_data.data_type, meta_data.domain, dst_span); + fill_new_attribute( + src_components, attribute_id, meta_data.data_type, meta_data.domain, dst_span); write_attribute.save(); } } @@ -306,7 +295,7 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet * \note This takes advantage of the fact that creating attributes on joined curves never * changes a point attribute into a spline attribute; it is always the other way around. */ -static void ensure_control_point_attribute(const StringRef name, +static void ensure_control_point_attribute(const AttributeIDRef &attribute_id, const CustomDataType data_type, Span<CurveComponent *> src_components, CurveEval &result) @@ -321,7 +310,7 @@ static void ensure_control_point_attribute(const StringRef name, const CurveEval *current_curve = src_components[src_component_index]->get_for_read(); for (SplinePtr &spline : splines) { - std::optional<GSpan> attribute = spline->attributes.get_for_read(name); + std::optional<GSpan> attribute = spline->attributes.get_for_read(attribute_id); if (attribute) { if (attribute->type() != type) { @@ -334,22 +323,22 @@ static void ensure_control_point_attribute(const StringRef name, conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), type) ->materialize(converted_buffer); - spline->attributes.remove(name); - spline->attributes.create_by_move(name, data_type, converted_buffer); + spline->attributes.remove(attribute_id); + spline->attributes.create_by_move(attribute_id, data_type, converted_buffer); } } else { - spline->attributes.create(name, data_type); + spline->attributes.create(attribute_id, data_type); - if (current_curve->attributes.get_for_read(name)) { + if (current_curve->attributes.get_for_read(attribute_id)) { /* In this case the attribute did not exist, but there is a spline domain attribute * we can retrieve a value from, as a spline to point domain conversion. So fill the * new attribute with the value for this spline. */ GVArrayPtr current_curve_attribute = current_curve->attributes.get_for_read( - name, data_type, nullptr); + attribute_id, data_type, nullptr); - BLI_assert(spline->attributes.get_for_read(name)); - std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(name); + BLI_assert(spline->attributes.get_for_read(attribute_id)); + std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(attribute_id); BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); current_curve_attribute->get(spline_index_in_component, buffer); @@ -371,15 +360,15 @@ static void ensure_control_point_attribute(const StringRef name, /** * Fill data for an attribute on the new curve based on all source curves. */ -static void ensure_spline_attribute(const StringRef name, +static void ensure_spline_attribute(const AttributeIDRef &attribute_id, const CustomDataType data_type, Span<CurveComponent *> src_components, CurveEval &result) { const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - result.attributes.create(name, data_type); - GMutableSpan result_attribute = *result.attributes.get_for_write(name); + result.attributes.create(attribute_id, data_type); + GMutableSpan result_attribute = *result.attributes.get_for_write(attribute_id); int offset = 0; for (const CurveComponent *component : src_components) { @@ -388,7 +377,7 @@ static void ensure_spline_attribute(const StringRef name, if (size == 0) { continue; } - GVArrayPtr read_attribute = curve.attributes.get_for_read(name, data_type, nullptr); + GVArrayPtr read_attribute = curve.attributes.get_for_read(attribute_id, data_type, nullptr); GVArray_GSpan src_span{*read_attribute}; const void *src_buffer = src_span.data(); @@ -406,19 +395,19 @@ static void ensure_spline_attribute(const StringRef name, * \warning Splines have been moved out of the source components at this point, so it * is important to only read curve-level data (spline domain attributes) from them. */ -static void join_curve_attributes(const Map<std::string, AttributeMetaData> &info, +static void join_curve_attributes(const Map<AttributeIDRef, AttributeMetaData> &info, Span<CurveComponent *> src_components, CurveEval &result) { - for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) { - const StringRef name = item.key; + for (const Map<AttributeIDRef, AttributeMetaData>::Item &item : info.items()) { + const AttributeIDRef attribute_id = item.key; const AttributeMetaData meta_data = item.value; if (meta_data.domain == ATTR_DOMAIN_CURVE) { - ensure_spline_attribute(name, meta_data.data_type, src_components, result); + ensure_spline_attribute(attribute_id, meta_data.data_type, src_components, result); } else { - ensure_control_point_attribute(name, meta_data.data_type, src_components, result); + ensure_control_point_attribute(attribute_id, meta_data.data_type, src_components, result); } } } @@ -446,7 +435,7 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge } /* Retrieve attribute info before moving the splines out of the input components. */ - const Map<std::string, AttributeMetaData> info = get_final_attribute_info( + const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info( {(const GeometryComponent **)src_components.data(), src_components.size()}, {"position", "radius", "tilt", "cyclic", "resolution"}); @@ -506,7 +495,7 @@ void register_node_type_geo_join_geometry() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_join_geometry_in, geo_node_join_geometry_out); ntype.geometry_node_execute = blender::nodes::geo_node_join_geometry_exec; + ntype.declare = blender::nodes::geo_node_join_geometry_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index 02b2d685bdd..43818947272 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -24,30 +24,17 @@ #include "BKE_material.h" -static bNodeSocketTemplate geo_node_material_assign_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_MATERIAL, - N_("Material"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - PROP_NONE, - SOCK_HIDE_LABEL}, - {SOCK_STRING, N_("Selection")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_material_assign_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { -static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material) +static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Material>("Material").hide_label(); + b.add_input<decl::Bool>("Selection").default_value(true).hide_value(); + b.add_output<decl::Geometry>("Geometry"); +} + +static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material) { int new_material_index = -1; for (const int i : IndexRange(mesh.totcol)) { @@ -64,18 +51,16 @@ static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, } mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); - for (const int i : IndexRange(mesh.totpoly)) { - if (face_mask[i]) { - MPoly &poly = mesh.mpoly[i]; - poly.mat_nr = new_material_index; - } + for (const int i : selection) { + MPoly &poly = mesh.mpoly[i]; + poly.mat_nr = new_material_index; } } static void geo_node_material_assign_exec(GeoNodeExecParams params) { Material *material = params.extract_input<Material *>("Material"); - const std::string mask_name = params.extract_input<std::string>("Selection"); + const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -85,9 +70,15 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params) MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); Mesh *mesh = mesh_component.get_for_write(); if (mesh != nullptr) { - GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>( - mask_name, ATTR_DOMAIN_FACE, true); - assign_material_to_faces(*mesh, face_mask, material); + + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + + fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + assign_material_to_faces(*mesh, selection, material); } } @@ -101,7 +92,7 @@ void register_node_type_geo_material_assign() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_material_assign_in, geo_node_material_assign_out); + ntype.declare = blender::nodes::geo_node_material_assign_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index 40ecab98dea..a9c3bfc6ce0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -24,20 +24,16 @@ #include "BKE_material.h" -static bNodeSocketTemplate geo_node_material_replace_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_MATERIAL, N_("Old")}, - {SOCK_MATERIAL, N_("New")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_material_replace_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_material_replace_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Material>("Old"); + b.add_input<decl::Material>("New"); + b.add_output<decl::Geometry>("Geometry"); +} + static void geo_node_material_replace_exec(GeoNodeExecParams params) { Material *old_material = params.extract_input<Material *>("Old"); @@ -69,7 +65,7 @@ void register_node_type_geo_material_replace() geo_node_type_base( &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_material_replace_in, geo_node_material_replace_out); + ntype.declare = blender::nodes::geo_node_material_replace_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc new file mode 100644 index 00000000000..22c24e34314 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -0,0 +1,131 @@ +/* + * 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" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_task.hh" + +#include "BKE_material.h" + +namespace blender::nodes { + +static void geo_node_material_selection_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Material>("Material").hide_label(true); + b.add_output<decl::Bool>("Selection"); +} + +static void select_mesh_by_material(const Mesh &mesh, + const Material *material, + const IndexMask mask, + const MutableSpan<bool> r_selection) +{ + BLI_assert(mesh.totpoly >= r_selection.size()); + Vector<int> material_indices; + for (const int i : IndexRange(mesh.totcol)) { + if (mesh.mat[i] == material) { + material_indices.append(i); + } + } + threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + const int face_index = mask[i]; + r_selection[i] = material_indices.contains(mesh.mpoly[face_index].mat_nr); + } + }); +} + +class MaterialSelectionFieldInput final : public fn::FieldInput { + Material *material_; + + public: + MaterialSelectionFieldInput(Material *material) + : fn::FieldInput(CPPType::get<bool>(), "Material Selection"), material_(material) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast<const GeometryComponentFieldContext *>(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return nullptr; + } + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return nullptr; + } + + if (domain == ATTR_DOMAIN_FACE) { + Array<bool> selection(mask.min_array_size()); + select_mesh_by_material(*mesh, material_, mask, selection); + return &scope.construct<fn::GVArray_For_ArrayContainer<Array<bool>>>(std::move(selection)); + } + + Array<bool> selection(mesh->totpoly); + select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection); + GVArrayPtr face_selection = std::make_unique<fn::GVArray_For_ArrayContainer<Array<bool>>>( + std::move(selection)); + GVArrayPtr final_selection = mesh_component.attribute_try_adapt_domain( + std::move(face_selection), ATTR_DOMAIN_FACE, domain); + return scope.add_value(std::move(final_selection)).get(); + } + + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 91619626; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const MaterialSelectionFieldInput *>(&other) != nullptr; + } +}; + +static void geo_node_material_selection_exec(GeoNodeExecParams params) +{ + Material *material = params.extract_input<Material *>("Material"); + Field<bool> material_field{std::make_shared<MaterialSelectionFieldInput>(material)}; + params.set_output("Selection", std::move(material_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_material_selection() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_MATERIAL_SELECTION, "Material Selection", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_material_selection_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_material_selection_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 131f9548b40..9c477c639a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -25,16 +25,14 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_primitive_circle_in[] = { - {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, - {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_mesh_primitive_circle_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Vertices").default_value(32).min(3); + b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, bContext *UNUSED(C), @@ -55,8 +53,6 @@ static void geo_node_mesh_primitive_circle_init(bNodeTree *UNUSED(ntree), bNode node->storage = node_storage; } -namespace blender::nodes { - static int circle_vert_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num) { switch (fill_type) { @@ -143,18 +139,14 @@ static Mesh *create_circle_mesh(const float radius, } /* Create outer edges. */ + const short edge_flag = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? + ME_LOOSEEDGE : + (ME_EDGEDRAW | ME_EDGERENDER); /* NGON or TRIANGLE_FAN */ for (const int i : IndexRange(verts_num)) { MEdge &edge = edges[i]; edge.v1 = i; edge.v2 = (i + 1) % verts_num; - } - - /* Set loose edge flags. */ - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[i]; - edge.flag |= ME_LOOSEEDGE; - } + edge.flag = edge_flag; } /* Create triangle fan edges. */ @@ -231,12 +223,11 @@ void register_node_type_geo_mesh_primitive_circle() geo_node_type_base( &ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Mesh Circle", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_mesh_primitive_circle_in, geo_node_mesh_primitive_circle_out); - node_type_init(&ntype, geo_node_mesh_primitive_circle_init); + node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_circle_init); node_type_storage( &ntype, "NodeGeometryMeshCircle", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_circle_exec; - ntype.draw_buttons = geo_node_mesh_primitive_circle_layout; + ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_circle_layout; + ntype.declare = blender::nodes::geo_node_mesh_primitive_circle_declare; nodeRegisterType(&ntype); } 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 b834f5e2fa0..0d58476fc58 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 @@ -25,18 +25,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = { - {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, - {SOCK_FLOAT, N_("Radius Top"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Radius Bottom"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Depth"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_mesh_primitive_cone_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Vertices").default_value(32).min(3); + b.add_input<decl::Float>("Radius Top").min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, bContext *UNUSED(C), @@ -57,8 +55,6 @@ static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *n node->storage = node_storage; } -namespace blender::nodes { - static int vert_total(const GeometryNodeMeshCircleFillType fill_type, const int verts_num, const bool top_is_point, @@ -575,12 +571,11 @@ void register_node_type_geo_mesh_primitive_cone() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_mesh_primitive_cone_in, geo_node_mesh_primitive_cone_out); - node_type_init(&ntype, geo_node_mesh_primitive_cone_init); + node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cone_init); node_type_storage( &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec; - ntype.draw_buttons = geo_node_mesh_primitive_cone_layout; + ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_cone_layout; + ntype.declare = blender::nodes::geo_node_mesh_primitive_cone_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 3a93bc22b7e..af8ce02b3c1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -15,58 +15,473 @@ */ #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" -#include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mesh.h" -#include "bmesh.h" - #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_primitive_cube_in[] = { - {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>("Size").default_value(float3(1)).min(0.0f).subtype(PROP_TRANSLATION); + b.add_input<decl::Int>("Vertices X").default_value(2).min(2).max(1000); + b.add_input<decl::Int>("Vertices Y").default_value(2).min(2).max(1000); + b.add_input<decl::Int>("Vertices Z").default_value(2).min(2).max(1000); + b.add_output<decl::Geometry>("Geometry"); +} + +struct CuboidConfig { + float3 size; + int verts_x; + int verts_y; + int verts_z; + int edges_x; + int edges_y; + int edges_z; + int vertex_count; + int poly_count; + int loop_count; + + CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) + : size(size), + verts_x(verts_x), + verts_y(verts_y), + verts_z(verts_z), + edges_x(verts_x - 1), + edges_y(verts_y - 1), + edges_z(verts_z - 1) + { + BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); + this->vertex_count = this->get_vertex_count(); + this->poly_count = this->get_poly_count(); + this->loop_count = this->poly_count * 4; + } + + private: + int get_vertex_count() + { + const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); + return verts_x * verts_y * verts_z - inner_position_count; + } -static bNodeSocketTemplate geo_node_mesh_primitive_cube_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, + int get_poly_count() + { + return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); + } }; -namespace blender::nodes { +static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) +{ + const float z_bottom = -config.size.z / 2.0f; + const float z_delta = config.size.z / config.edges_z; + + const float x_left = -config.size.x / 2.0f; + const float x_delta = config.size.x / config.edges_x; + + const float y_front = -config.size.y / 2.0f; + const float y_delta = config.size.y / config.edges_y; + + int vert_index = 0; + + /* Though looping over all possible coordinates inside the cube only to skip them may be slow, + * the alternative is similar complexity to below in the poly index calculation. If this loop + * becomes a problem in the future it could be optimized, though only after proper performance + * testing. */ + for (const int z : IndexRange(config.verts_z)) { + for (const int y : IndexRange(config.verts_y)) { + for (const int x : IndexRange(config.verts_x)) { + /* Only plot vertices on the surface of the cuboid. */ + if (ELEM(z, 0, config.edges_z) || ELEM(x, 0, config.edges_x) || + ELEM(y, 0, config.edges_y)) { + + const float x_pos = x_left + x_delta * x; + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + copy_v3_v3(verts[vert_index].co, float3(x_pos, y_pos, z_pos)); + + vert_index++; + } + } + } + } +} -Mesh *create_cube_mesh(const float size) +/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. + * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for + * anti-clockwise. + */ +static void define_quad(MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops, + const int poly_index, + const int loop_index, + const int vert_1, + const int vert_2, + const int vert_3, + const int vert_4) +{ + MPoly &poly = polys[poly_index]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop_1 = loops[loop_index]; + loop_1.v = vert_1; + MLoop &loop_2 = loops[loop_index + 1]; + loop_2.v = vert_2; + MLoop &loop_3 = loops[loop_index + 2]; + loop_3.v = vert_3; + MLoop &loop_4 = loops[loop_index + 3]; + loop_4.v = vert_4; +} + +static void calculate_polys(const CuboidConfig &config, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops) { - const float4x4 transform = float4x4::identity(); - - const BMeshCreateParams bmcp = {true}; - const BMAllocTemplate allocsize = {8, 12, 24, 6}; - BMesh *bm = BM_mesh_create(&allocsize, &bmcp); - BM_data_layer_add_named(bm, &bm->ldata, CD_MLOOPUV, nullptr); - - BMO_op_callf(bm, - BMO_FLAG_DEFAULTS, - "create_cube matrix=%m4 size=%f calc_uvs=%b", - transform.values, - size, - true); - - BMeshToMeshParams params{}; - params.calc_object_remap = false; - Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + int loop_index = 0; + int poly_index = 0; + + /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ + const int xy_cross_section_vert_count = config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2); + + /* Calculate polys for Bottom faces. */ + int vert_1_start = 0; + + for (const int UNUSED(y) : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + const int vert_1 = vert_1_start + x; + const int vert_2 = vert_1_start + config.verts_x + x; + const int vert_3 = vert_2 + 1; + const int vert_4 = vert_1 + 1; + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + vert_1_start += config.verts_x; + } + + /* Calculate polys for Front faces. */ + vert_1_start = 0; + int vert_2_start = config.verts_x * config.verts_y; + + for (const int UNUSED(z) : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_1_start = vert_2_start; + vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); + } + + /* Calculate polys for Top faces. */ + vert_1_start = config.verts_x * config.verts_y + + (config.verts_z - 2) * (config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2)); + vert_2_start = vert_1_start + config.verts_x; + + for (const int UNUSED(y) : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_2_start += config.verts_x; + vert_1_start += config.verts_x; + } + + /* Calculate polys for Back faces. */ + vert_1_start = config.verts_x * config.edges_y; + vert_2_start = vert_1_start + xy_cross_section_vert_count; + + for (const int z : IndexRange(config.edges_z)) { + if (z == (config.edges_z - 1)) { + vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); + } + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_2_start + x, + vert_2_start + x + 1, + vert_1_start + x + 1); + loop_index += 4; + poly_index++; + } + vert_2_start += xy_cross_section_vert_count; + vert_1_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Left faces. */ + vert_1_start = 0; + vert_2_start = config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1; + int vert_2; + int vert_3; + int vert_4; + + if (z == 0 || y == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_1 += config.verts_x - 2; + vert_4 = vert_1 + 2; + } + + if (y == 0 || z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_2 += config.verts_x - 2; + vert_3 = vert_2 + 2; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Right faces. */ + vert_1_start = config.edges_x; + vert_2_start = vert_1_start + config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1 = vert_1_start; + int vert_2 = vert_2_start; + int vert_3 = vert_2_start + 2; + int vert_4 = vert_1 + config.verts_x; + + if (z == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_4 = vert_1 + 2; + } + + if (z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_3 = vert_2 + 2; + } + + if (y == (config.edges_y - 1)) { + vert_3 = vert_2 + config.verts_x; + vert_4 = vert_1 + config.verts_x; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } +} + +static void calculate_uvs(const CuboidConfig &config, Mesh *mesh) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + 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 x_delta = 0.25f / static_cast<float>(config.edges_x); + const float y_delta = 0.25f / static_cast<float>(config.edges_y); + const float z_delta = 0.25f / static_cast<float>(config.edges_z); + + /* Calculate bottom face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); + } + } + + /* Calculate front face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); + } + } + + /* Calculate top face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); + } + } + + /* Calculate back face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); + } + } + + /* Calculate left face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); + } + } + + /* Calculate right face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); + } + } + + uv_attribute.save(); +} + +Mesh *create_cuboid_mesh(const float3 size, + const int verts_x, + const int verts_y, + const int verts_z) +{ + const CuboidConfig config(size, verts_x, verts_y, verts_z); + + Mesh *mesh = BKE_mesh_new_nomain( + config.vertex_count, 0, 0, config.loop_count, config.poly_count); BKE_id_material_eval_ensure_default_slot(&mesh->id); - BM_mesh_bm_to_me(nullptr, bm, mesh, ¶ms); - BM_mesh_free(bm); + + calculate_vertices(config, {mesh->mvert, mesh->totvert}); + + calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); + + calculate_uvs(config, mesh); return mesh; } +static Mesh *create_cube_mesh(const float3 size, + const int verts_x, + const int verts_y, + const int verts_z) +{ + const int dimensions = (verts_x - 1 > 0) + (verts_y - 1 > 0) + (verts_z - 1 > 0); + if (dimensions == 0) { + return create_line_mesh(float3(0), float3(0), 1); + } + if (dimensions == 1) { + float3 start; + float3 delta; + if (verts_x > 1) { + start = {-size.x / 2.0f, 0, 0}; + delta = {size.x / (verts_x - 1), 0, 0}; + } + else if (verts_y > 1) { + start = {0, -size.y / 2.0f, 0}; + delta = {0, size.y / (verts_y - 1), 0}; + } + else { + start = {0, 0, -size.z / 2.0f}; + delta = {0, 0, size.z / (verts_z - 1)}; + } + + return create_line_mesh(start, delta, verts_x * verts_y * verts_z); + } + if (dimensions == 2) { + if (verts_z == 1) { /* XY plane. */ + return create_grid_mesh(verts_x, verts_y, size.x, size.y); + } + if (verts_y == 1) { /* XZ plane. */ + Mesh *mesh = create_grid_mesh(verts_x, verts_z, size.x, size.z); + transform_mesh(mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1)); + return mesh; + } + /* YZ plane. */ + Mesh *mesh = create_grid_mesh(verts_z, verts_y, size.z, size.y); + transform_mesh(mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1)); + return mesh; + } + + return create_cuboid_mesh(size, verts_x, verts_y, verts_z); +} + static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) { - const float size = params.extract_input<float>("Size"); + const float3 size = params.extract_input<float3>("Size"); + const int verts_x = params.extract_input<int>("Vertices X"); + const int verts_y = params.extract_input<int>("Vertices Y"); + const int verts_z = params.extract_input<int>("Vertices Z"); + if (verts_x < 1 || verts_y < 1 || verts_z < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 1")); + params.set_output("Geometry", GeometrySet()); + return; + } + + Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z); - Mesh *mesh = create_cube_mesh(size); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } @@ -77,8 +492,7 @@ void register_node_type_geo_mesh_primitive_cube() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CUBE, "Cube", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_mesh_primitive_cube_in, geo_node_mesh_primitive_cube_out); + ntype.declare = blender::nodes::geo_node_mesh_primitive_cube_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cube_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index b40cb478b03..ed701c921ca 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -25,17 +25,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_in[] = { - {SOCK_INT, N_("Vertices"), 32, 0.0f, 0.0f, 0.0f, 3, 4096}, - {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Depth"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_mesh_primitive_cylinder_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Vertices").default_value(32).min(3).max(4096); + b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, bContext *UNUSED(C), @@ -56,8 +54,6 @@ static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNod node->storage = node_storage; } -namespace blender::nodes { - static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) { const bNode &node = params.node(); @@ -86,14 +82,12 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) void register_node_type_geo_mesh_primitive_cylinder() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_mesh_primitive_cylinder_in, geo_node_mesh_primitive_cylinder_out); - node_type_init(&ntype, geo_node_mesh_primitive_cylinder_init); + node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_init); node_type_storage( &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_mesh_primitive_cylinder_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cylinder_exec; - ntype.draw_buttons = geo_node_mesh_primitive_cylinder_layout; + ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_cylinder_layout; nodeRegisterType(&ntype); } 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 410290c79ee..858ef8648f8 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 @@ -25,21 +25,17 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_primitive_grid_in[] = { - {SOCK_FLOAT, N_("Size X"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Size Y"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_INT, N_("Vertices X"), 3, 0.0f, 0.0f, 0.0f, 2, 1000}, - {SOCK_INT, N_("Vertices Y"), 3, 0.0f, 0.0f, 0.0f, 2, 1000}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_mesh_primitive_grid_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("Size X").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Size Y").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Int>("Vertices X").default_value(3).min(2).max(1000); + b.add_input<decl::Int>("Vertices Y").default_value(3).min(2).max(1000); + b.add_output<decl::Geometry>("Geometry"); +} + static void calculate_uvs( Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y) { @@ -60,12 +56,12 @@ static void calculate_uvs( uv_attribute.save(); } -static Mesh *create_grid_mesh(const int verts_x, - const int verts_y, - const float size_x, - const float size_y) +Mesh *create_grid_mesh(const int verts_x, + const int verts_y, + const float size_x, + const float size_y) { - BLI_assert(verts_x > 1 && verts_y > 1); + BLI_assert(verts_x > 0 && verts_y > 0); const int edges_x = verts_x - 1; const int edges_y = verts_y - 1; Mesh *mesh = BKE_mesh_new_nomain(verts_x * verts_y, @@ -101,6 +97,8 @@ static Mesh *create_grid_mesh(const int verts_x, /* Build the horizontal edges in the X direction. */ const int y_edges_start = 0; + const short edge_flag = (edges_x == 0 || edges_y == 0) ? ME_LOOSEEDGE : + ME_EDGEDRAW | ME_EDGERENDER; int edge_index = 0; for (const int x : IndexRange(verts_x)) { for (const int y : IndexRange(edges_y)) { @@ -108,7 +106,7 @@ static Mesh *create_grid_mesh(const int verts_x, MEdge &edge = edges[edge_index++]; edge.v1 = vert_index; edge.v2 = vert_index + 1; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = edge_flag; } } @@ -120,7 +118,7 @@ static Mesh *create_grid_mesh(const int verts_x, MEdge &edge = edges[edge_index++]; edge.v1 = vert_index; edge.v2 = vert_index + verts_y; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = edge_flag; } } @@ -148,7 +146,9 @@ static Mesh *create_grid_mesh(const int verts_x, } } - calculate_uvs(mesh, verts, loops, size_x, size_y); + if (mesh->totpoly != 0) { + calculate_uvs(mesh, verts, loops, size_x, size_y); + } return mesh; } @@ -159,13 +159,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) const float size_y = params.extract_input<float>("Size Y"); const int verts_x = params.extract_input<int>("Vertices X"); const int verts_y = params.extract_input<int>("Vertices Y"); - if (verts_x < 2 || verts_y < 2) { - if (verts_x < 2) { - params.error_message_add(NodeWarningType::Info, TIP_("Vertices X must be at least 2")); - } - if (verts_y < 2) { - params.error_message_add(NodeWarningType::Info, TIP_("Vertices Y must be at least 2")); - } + if (verts_x < 1 || verts_y < 1) { params.set_output("Geometry", GeometrySet()); return; } @@ -184,8 +178,7 @@ void register_node_type_geo_mesh_primitive_grid() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_GRID, "Grid", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_mesh_primitive_grid_in, geo_node_mesh_primitive_grid_out); + ntype.declare = blender::nodes::geo_node_mesh_primitive_grid_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_grid_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 22195b9e8db..5ea7165ac31 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -24,19 +24,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_in[] = { - {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 1, 7}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Int>("Subdivisions").default_value(1).min(1).max(7); + b.add_output<decl::Geometry>("Geometry"); +} + static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) { const float4x4 transform = float4x4::identity(); @@ -48,7 +44,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) BMO_op_callf(bm, BMO_FLAG_DEFAULTS, - "create_icosphere subdivisions=%i diameter=%f matrix=%m4 calc_uvs=%b", + "create_icosphere subdivisions=%i radius=%f matrix=%m4 calc_uvs=%b", subdivisions, std::abs(radius), transform.values, @@ -81,8 +77,7 @@ void register_node_type_geo_mesh_primitive_ico_sphere() geo_node_type_base( &ntype, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Ico Sphere", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_mesh_primitive_ico_sphere_in, geo_node_mesh_primitive_ico_sphere_out); + ntype.declare = blender::nodes::geo_node_mesh_primitive_ico_sphere_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_ico_sphere_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 2e6d8ca34c8..031223b5ca6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -25,26 +25,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_primitive_line_in[] = { - {SOCK_INT, N_("Count"), 10, 0.0f, 0.0f, 0.0f, 1, 10000}, - {SOCK_FLOAT, N_("Resolution"), 1.0f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE}, - {SOCK_VECTOR, - N_("Start Location"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - -FLT_MAX, - FLT_MAX, - PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_mesh_primitive_line_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Count").default_value(10).min(1).max(10000); + b.add_input<decl::Float>("Resolution").default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); + b.add_input<decl::Vector>("Start Location").subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("Offset").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_mesh_primitive_line_layout(uiLayout *layout, bContext *UNUSED(C), @@ -93,8 +83,6 @@ static void geo_node_mesh_primitive_line_update(bNodeTree *UNUSED(tree), bNode * count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL); } -namespace blender::nodes { - static void fill_edge_data(MutableSpan<MEdge> edges) { for (const int i : edges.index_range()) { @@ -104,7 +92,7 @@ static void fill_edge_data(MutableSpan<MEdge> edges) } } -static Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) +Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) { if (count < 1) { return nullptr; @@ -176,13 +164,12 @@ void register_node_type_geo_mesh_primitive_line() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Mesh Line", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_mesh_primitive_line_in, geo_node_mesh_primitive_line_out); - node_type_init(&ntype, geo_node_mesh_primitive_line_init); - node_type_update(&ntype, geo_node_mesh_primitive_line_update); + ntype.declare = blender::nodes::geo_node_mesh_primitive_line_declare; + node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_line_init); + node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_line_update); node_type_storage( &ntype, "NodeGeometryMeshLine", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_line_exec; - ntype.draw_buttons = geo_node_mesh_primitive_line_layout; + ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_line_layout; nodeRegisterType(&ntype); } 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 affba602234..6fd6cdf5747 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 @@ -25,20 +25,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = { - {SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024}, - {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 2, 1024}, - {SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>("Segments").default_value(32).min(3).max(1024); + b.add_input<decl::Int>("Rings").default_value(16).min(2).max(1024); + b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>("Geometry"); +} + static int sphere_vert_total(const int segments, const int rings) { return segments * (rings - 1) + 2; @@ -313,8 +309,7 @@ void register_node_type_geo_mesh_primitive_uv_sphere() geo_node_type_base( &ntype, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "UV Sphere", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_mesh_primitive_uv_sphere_in, geo_node_mesh_primitive_uv_sphere_out); + ntype.declare = blender::nodes::geo_node_mesh_primitive_uv_shpere_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_uv_sphere_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 165da8ec9f2..9ca74fed9a7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -23,19 +23,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_mesh_subdivide_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_mesh_subdivide_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); + b.add_output<decl::Geometry>("Geometry"); +} + static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -105,7 +101,7 @@ void register_node_type_geo_mesh_subdivide() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_SUBDIVIDE, "Mesh Subdivide", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_mesh_subdivide_in, geo_node_mesh_subdivide_out); + ntype.declare = blender::nodes::geo_node_mesh_subdivide_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_subdivide_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 637003a46c7..11349dc7d42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -27,19 +27,15 @@ using blender::Array; -static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = { - {SOCK_GEOMETRY, N_("Mesh")}, - {SOCK_STRING, N_("Selection")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = { - {SOCK_GEOMETRY, N_("Curve")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_mesh_to_curve_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Mesh"); + b.add_input<decl::String>("Selection"); + b.add_output<decl::Geometry>("Curve"); +} + template<typename T> static void copy_attribute_to_points(const VArray<T> &source_data, Span<int> map, @@ -56,10 +52,10 @@ static void copy_attributes_to_points(CurveEval &curve, Span<Vector<int>> point_to_vert_maps) { MutableSpan<SplinePtr> splines = curve.splines(); - Set<std::string> source_attribute_names = mesh_component.attribute_names(); + Set<AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids(); /* Copy builtin control point attributes. */ - if (source_attribute_names.contains_as("tilt")) { + if (source_attribute_ids.contains("tilt")) { const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>( "tilt", ATTR_DOMAIN_POINT, 0.0f); threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { @@ -68,9 +64,9 @@ static void copy_attributes_to_points(CurveEval &curve, *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); } }); - source_attribute_names.remove_contained_as("tilt"); + source_attribute_ids.remove_contained("tilt"); } - if (source_attribute_names.contains_as("radius")) { + if (source_attribute_ids.contains("radius")) { const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>( "radius", ATTR_DOMAIN_POINT, 1.0f); threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { @@ -79,15 +75,15 @@ static void copy_attributes_to_points(CurveEval &curve, *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); } }); - source_attribute_names.remove_contained_as("radius"); + source_attribute_ids.remove_contained("radius"); } /* Don't copy other builtin control point attributes. */ - source_attribute_names.remove_as("position"); + source_attribute_ids.remove("position"); /* Copy dynamic control point attributes. */ - for (const StringRef name : source_attribute_names) { - const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name, + for (const AttributeIDRef &attribute_id : source_attribute_ids) { + const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id, ATTR_DOMAIN_POINT); /* Some attributes might not exist if they were builtin attribute on domains that don't * have any elements, i.e. a face attribute on the output of the line primitive node. */ @@ -100,8 +96,9 @@ static void copy_attributes_to_points(CurveEval &curve, threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { /* Create attribute on the spline points. */ - splines[i]->attributes.create(name, data_type); - std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(name); + splines[i]->attributes.create(attribute_id, data_type); + std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write( + attribute_id); BLI_assert(spline_attribute); /* Copy attribute based on the map for this spline. */ @@ -309,8 +306,9 @@ void register_node_type_geo_mesh_to_curve() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_mesh_to_curve_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; nodeRegisterType(&ntype); } 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 167812d5656..389acc40f0f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -21,25 +21,22 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_object_info_in[] = { - {SOCK_OBJECT, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_object_info_out[] = { - {SOCK_VECTOR, N_("Location")}, - {SOCK_VECTOR, N_("Rotation")}, - {SOCK_VECTOR, N_("Scale")}, - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_object_info_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Object>("Object").hide_label(); + b.add_output<decl::Vector>("Location"); + b.add_output<decl::Vector>("Rotation"); + b.add_output<decl::Vector>("Scale"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_object_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -namespace blender::nodes { static void geo_node_object_info_exec(GeoNodeExecParams params) { const bNode &bnode = params.node(); @@ -105,11 +102,11 @@ void register_node_type_geo_object_info() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, geo_node_object_info_in, geo_node_object_info_out); node_type_init(&ntype, blender::nodes::geo_node_object_info_node_init); node_type_storage( &ntype, "NodeGeometryObjectInfo", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_object_info_exec; - ntype.draw_buttons = geo_node_object_info_layout; + ntype.draw_buttons = blender::nodes::geo_node_object_info_layout; + ntype.declare = blender::nodes::geo_node_object_info_declare; nodeRegisterType(&ntype); } 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 99930b5ae46..04b4003daed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -39,19 +39,21 @@ using blender::bke::AttributeKind; using blender::bke::GeometryInstanceGroup; -static bNodeSocketTemplate geo_node_point_distribute_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE}, - {SOCK_STRING, N_("Density Attribute")}, - {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_point_distribute_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_point_distribute_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Float>("Distance Min").min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Density Max") + .default_value(1.0f) + .min(0.0f) + .max(100000.0f) + .subtype(PROP_NONE); + b.add_input<decl::String>("Density Attribute"); + b.add_input<decl::Int>("Seed").min(-10000).max(10000); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_point_distribute_layout(uiLayout *layout, bContext *UNUSED(C), @@ -67,8 +69,6 @@ static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node) nodeSetSocketAvailability(sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON)); } -namespace blender::nodes { - /** * Use an arbitrary choice of axes for a usable rotation attribute directly out of this node. */ @@ -277,17 +277,17 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, BLI_NOINLINE static void interpolate_existing_attributes( Span<GeometryInstanceGroup> set_groups, Span<int> instance_start_offsets, - const Map<std::string, AttributeKind> &attributes, + const Map<AttributeIDRef, AttributeKind> &attributes, GeometryComponent &component, Span<Vector<float3>> bary_coords_array, Span<Vector<int>> looptri_indices_array) { - for (Map<std::string, AttributeKind>::Item entry : attributes.items()) { - StringRef attribute_name = entry.key; + for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = 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. */ OutputAttribute attribute_out = component.attribute_try_get_for_output_only( - attribute_name, ATTR_DOMAIN_POINT, output_data_type); + attribute_id, ATTR_DOMAIN_POINT, output_data_type); if (!attribute_out) { continue; } @@ -301,7 +301,7 @@ BLI_NOINLINE static void interpolate_existing_attributes( const Mesh &mesh = *source_component.get_for_read(); std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data( - attribute_name); + attribute_id); if (!attribute_info) { i_instance += set_group.transforms.size(); continue; @@ -309,7 +309,7 @@ BLI_NOINLINE static void interpolate_existing_attributes( const AttributeDomain source_domain = attribute_info->domain; GVArrayPtr source_attribute = source_component.attribute_get_for_read( - attribute_name, source_domain, output_data_type, nullptr); + attribute_id, source_domain, output_data_type, nullptr); if (!source_attribute) { i_instance += set_group.transforms.size(); continue; @@ -406,7 +406,7 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> BLI_NOINLINE static void add_remaining_point_attributes( Span<GeometryInstanceGroup> set_groups, Span<int> instance_start_offsets, - const Map<std::string, AttributeKind> &attributes, + const Map<AttributeIDRef, AttributeKind> &attributes, GeometryComponent &component, Span<Vector<float3>> bary_coords_array, Span<Vector<int>> looptri_indices_array) @@ -629,7 +629,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) PointCloudComponent &point_component = geometry_set_out.get_component_for_write<PointCloudComponent>(); - Map<std::string, AttributeKind> attributes; + Map<AttributeIDRef, AttributeKind> attributes; bke::geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes); add_remaining_point_attributes(set_groups, @@ -649,10 +649,10 @@ void register_node_type_geo_point_distribute() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out); - node_type_update(&ntype, node_point_distribute_update); + &ntype, GEO_NODE_LEGACY_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0); + node_type_update(&ntype, blender::nodes::node_point_distribute_update); + ntype.declare = blender::nodes::geo_node_point_distribute_declare; ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec; - ntype.draw_buttons = geo_node_point_distribute_layout; + ntype.draw_buttons = blender::nodes::geo_node_point_distribute_layout; nodeRegisterType(&ntype); } 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 b119b7b31e9..fb45c22ced4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -24,38 +24,26 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_point_instance_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_OBJECT, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL}, - {SOCK_COLLECTION, - N_("Collection"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - PROP_NONE, - SOCK_HIDE_LABEL}, - {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_point_instance_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Object>("Object").hide_label(); + b.add_input<decl::Collection>("Collection").hide_label(); + b.add_input<decl::Geometry>("Instance Geometry"); + b.add_input<decl::Int>("Seed").min(-10000).max(10000); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "instance_type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "instance_type", 0, "", ICON_NONE); if (RNA_enum_get(ptr, "instance_type") == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) { uiItemR(layout, ptr, "use_whole_collection", 0, nullptr, ICON_NONE); } } -namespace blender::nodes { - static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( @@ -69,7 +57,8 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) { bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); bNodeSocket *collection_socket = object_socket->next; - bNodeSocket *seed_socket = collection_socket->next; + bNodeSocket *instance_geometry_socket = collection_socket->next; + bNodeSocket *seed_socket = instance_geometry_socket->next; NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node->storage; GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node_storage->instance_type; @@ -78,6 +67,8 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION); + nodeSetSocketAvailability(instance_geometry_socket, + type == GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY); nodeSetSocketAvailability( seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection); } @@ -127,6 +118,13 @@ static Vector<InstanceReference> get_instance_references__collection(GeoNodeExec return references; } +static Vector<InstanceReference> get_instance_references__geometry(GeoNodeExecParams ¶ms) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Instance Geometry"); + geometry_set.ensure_owns_direct_data(); + return {std::move(geometry_set)}; +} + static Vector<InstanceReference> get_instance_references(GeoNodeExecParams ¶ms) { const bNode &node = params.node(); @@ -141,6 +139,9 @@ static Vector<InstanceReference> get_instance_references(GeoNodeExecParams ¶ case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: { return get_instance_references__collection(params); } + case GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY: { + return get_instance_references__geometry(params); + } } return {}; } @@ -258,12 +259,13 @@ void register_node_type_geo_point_instance() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_point_instance_init); node_type_storage( &ntype, "NodeGeometryPointInstance", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = geo_node_point_instance_layout; + ntype.declare = blender::nodes::geo_node_point_instance_declare; + ntype.draw_buttons = blender::nodes::geo_node_point_instance_layout; node_type_update(&ntype, blender::nodes::geo_node_point_instance_update); ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec; nodeRegisterType(&ntype); 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 828d3f50551..60c82360007 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc @@ -21,21 +21,19 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_point_rotate_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Axis")}, - {SOCK_VECTOR, N_("Axis"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX, PROP_XYZ}, - {SOCK_STRING, N_("Angle")}, - {SOCK_FLOAT, N_("Angle"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_ANGLE}, - {SOCK_STRING, N_("Rotation")}, - {SOCK_VECTOR, N_("Rotation"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_EULER}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_point_rotate_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_point_rotate_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Axis"); + b.add_input<decl::Vector>("Axis", "Axis_001").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); + b.add_input<decl::String>("Angle"); + b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE); + b.add_input<decl::String>("Rotation"); + b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -57,8 +55,6 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), } } -namespace blender::nodes { - static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN( @@ -223,13 +219,13 @@ void register_node_type_geo_point_rotate() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_point_rotate_in, geo_node_point_rotate_out); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_point_rotate_init); node_type_update(&ntype, blender::nodes::geo_node_point_rotate_update); node_type_storage( &ntype, "NodeGeometryRotatePoints", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_point_rotate_declare; ntype.geometry_node_execute = blender::nodes::geo_node_point_rotate_exec; - ntype.draw_buttons = geo_node_point_rotate_layout; + ntype.draw_buttons = blender::nodes::geo_node_point_rotate_layout; nodeRegisterType(&ntype); } 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 265ec9fb776..99adce149e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc @@ -21,18 +21,18 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_point_scale_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Factor")}, - {SOCK_VECTOR, N_("Factor"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ}, - {SOCK_FLOAT, N_("Factor"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_point_scale_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_point_scale_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Factor"); + b.add_input<decl::Vector>("Factor", "Factor_001") + .default_value({1.0f, 1.0f, 1.0f}) + .subtype(PROP_XYZ); + b.add_input<decl::Float>("Factor", "Factor_002").default_value(1.0f).min(0.0f); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -41,8 +41,6 @@ static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE); } -namespace blender::nodes { - static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN( @@ -128,13 +126,14 @@ void register_node_type_geo_point_scale() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_point_scale_in, geo_node_point_scale_out); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY, 0); + + ntype.declare = blender::nodes::geo_node_point_scale_declare; node_type_init(&ntype, blender::nodes::geo_node_point_scale_init); node_type_update(&ntype, blender::nodes::geo_node_point_scale_update); node_type_storage( &ntype, "NodeGeometryPointScale", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_point_scale_exec; - ntype.draw_buttons = geo_node_point_scale_layout; + ntype.draw_buttons = blender::nodes::geo_node_point_scale_layout; nodeRegisterType(&ntype); } 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 fc04d1e275f..48b6676c1dd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -23,20 +23,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_point_instance_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Mask")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_point_instance_out[] = { - {SOCK_GEOMETRY, N_("Geometry 1")}, - {SOCK_GEOMETRY, N_("Geometry 2")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Mask"); + b.add_output<decl::Geometry>("Geometry 1"); + b.add_output<decl::Geometry>("Geometry 2"); +} + template<typename T> static void copy_data_based_on_mask(Span<T> data, Span<bool> masks, @@ -57,8 +53,8 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, Span<bool> masks, const bool invert) { - for (const std::string &name : in_component.attribute_names()) { - ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name); + for (const AttributeIDRef &attribute_id : in_component.attribute_ids()) { + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); 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 @@ -69,7 +65,7 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, } OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( - name, ATTR_DOMAIN_POINT, data_type); + attribute_id, ATTR_DOMAIN_POINT, data_type); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); @@ -168,8 +164,9 @@ void register_node_type_geo_point_separate() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_point_instance_declare; ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; ntype.geometry_node_execute_supports_laziness = true; nodeRegisterType(&ntype); 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 293f151fe19..f2fce45c57b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc @@ -19,17 +19,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_point_translate_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Translation")}, - {SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_point_translate_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_point_translate_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Translation"); + b.add_input<decl::Vector>("Translation", "Translation_001").subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -38,8 +36,6 @@ static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE); } -namespace blender::nodes { - static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component) { OutputAttribute_Typed<float3> position_attribute = @@ -99,15 +95,16 @@ void register_node_type_geo_point_translate() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_point_translate_in, geo_node_point_translate_out); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_point_translate_init); node_type_update(&ntype, blender::nodes::geo_node_point_translate_update); node_type_storage(&ntype, "NodeGeometryPointTranslate", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_point_translate_declare; ntype.geometry_node_execute = blender::nodes::geo_node_point_translate_exec; - ntype.draw_buttons = geo_node_point_translate_layout; + ntype.draw_buttons = blender::nodes::geo_node_point_translate_layout; nodeRegisterType(&ntype); } 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 65306b1c452..d920c8de9f0 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 @@ -28,20 +28,18 @@ #include "UI_interface.h" #include "UI_resources.h" -static bNodeSocketTemplate geo_node_points_to_volume_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, - {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, - {SOCK_STRING, N_("Radius")}, - {SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_points_to_volume_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f); + b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); + b.add_input<decl::String>("Radius"); + b.add_input<decl::Float>("Radius", "Radius_001").default_value(0.5f).min(0.0f); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_points_to_volume_layout(uiLayout *layout, bContext *UNUSED(C), @@ -53,8 +51,6 @@ static void geo_node_points_to_volume_layout(uiLayout *layout, uiItemR(layout, ptr, "input_type_radius", 0, IFACE_("Radius"), ICON_NONE); } -namespace blender::nodes { - static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( @@ -267,8 +263,7 @@ void register_node_type_geo_points_to_volume() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_points_to_volume_out); + &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); node_type_storage(&ntype, "NodeGeometryPointsToVolume", node_free_standard_storage, @@ -276,7 +271,8 @@ void register_node_type_geo_points_to_volume() node_type_size(&ntype, 170, 120, 700); node_type_init(&ntype, blender::nodes::geo_node_points_to_volume_init); node_type_update(&ntype, blender::nodes::geo_node_points_to_volume_update); + ntype.declare = blender::nodes::geo_node_points_to_volume_declare; ntype.geometry_node_execute = blender::nodes::geo_node_points_to_volume_exec; - ntype.draw_buttons = geo_node_points_to_volume_layout; + ntype.draw_buttons = blender::nodes::geo_node_points_to_volume_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 88e3bf17d43..401a478f04c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -24,26 +24,28 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_raycast_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_GEOMETRY, N_("Target Geometry")}, - {SOCK_STRING, N_("Ray Direction")}, - {SOCK_VECTOR, N_("Ray Direction"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("Ray Length")}, - {SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE}, - {SOCK_STRING, N_("Target Attribute")}, - {SOCK_STRING, N_("Is Hit")}, - {SOCK_STRING, N_("Hit Position")}, - {SOCK_STRING, N_("Hit Normal")}, - {SOCK_STRING, N_("Hit Distance")}, - {SOCK_STRING, N_("Hit Attribute")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_raycast_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_raycast_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>("Target Geometry"); + b.add_input<decl::String>("Ray Direction"); + b.add_input<decl::Vector>("Ray Direction", "Ray Direction_001") + .default_value({0.0f, 0.0f, 1.0f}); + b.add_input<decl::String>("Ray Length"); + b.add_input<decl::Float>("Ray Length", "Ray Length_001") + .default_value(100.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input<decl::String>("Target Attribute"); + b.add_input<decl::String>("Is Hit"); + b.add_input<decl::String>("Hit Position"); + b.add_input<decl::String>("Hit Normal"); + b.add_input<decl::String>("Hit Distance"); + b.add_input<decl::String>("Hit Attribute"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -63,8 +65,6 @@ static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -namespace blender::nodes { - static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; @@ -308,14 +308,14 @@ void register_node_type_geo_raycast() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_init(&ntype, geo_node_raycast_init); + node_type_init(&ntype, blender::nodes::geo_node_raycast_init); node_type_update(&ntype, blender::nodes::geo_node_raycast_update); node_type_storage( &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_raycast_declare; ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec; - ntype.draw_buttons = geo_node_raycast_layout; + ntype.draw_buttons = blender::nodes::geo_node_raycast_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc index bdc3e56783c..c63e4ec49d9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc @@ -16,21 +16,17 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_join_geometry_in[]{ - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_join_geometry_out[]{ - {SOCK_GEOMETRY, N_("Mesh")}, - {SOCK_GEOMETRY, N_("Point Cloud")}, - {SOCK_GEOMETRY, N_("Curve")}, - {SOCK_GEOMETRY, N_("Volume")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>("Mesh"); + b.add_output<decl::Geometry>("Point Cloud"); + b.add_output<decl::Geometry>("Curve"); + b.add_output<decl::Geometry>("Volume"); +} + static void geo_node_separate_components_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -71,7 +67,7 @@ void register_node_type_geo_separate_components() geo_node_type_base( &ntype, GEO_NODE_SEPARATE_COMPONENTS, "Separate Components", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_join_geometry_in, geo_node_join_geometry_out); + ntype.declare = blender::nodes::geo_node_join_geometry_declare; ntype.geometry_node_execute = blender::nodes::geo_node_separate_components_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc new file mode 100644 index 00000000000..4c754ddb643 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -0,0 +1,79 @@ +/* + * 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 "DEG_depsgraph_query.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_position_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Vector>("Position"); + b.add_input<decl::Bool>("Selection").default_value(true).hide_value(); + b.add_output<decl::Geometry>("Geometry"); +} + +static void set_position_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<float3> &position_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + fn::FieldEvaluator position_evaluator{field_context, &selection}; + position_evaluator.add_with_destination(position_field, positions.varray()); + position_evaluator.evaluate(); + positions.save(); +} + +static void geo_node_set_position_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); + geometry = geometry_set_realize_instances(geometry); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<float3> position_field = params.extract_input<Field<float3>>("Position"); + + for (const GeometryComponentType type : + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { + if (geometry.has(type)) { + set_position_in_component( + geometry.get_component_for_write(type), selection_field, position_field); + } + } + + params.set_output("Geometry", std::move(geometry)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_position() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_POSITION, "Set Position", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_position_exec; + ntype.declare = blender::nodes::geo_node_set_position_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 4f70252ae75..4541bf3569f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -23,30 +23,27 @@ #include "UI_resources.h" #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_subdivision_surface_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6}, - {SOCK_BOOLEAN, N_("Use Creases")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_subdivision_surface_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); + b.add_input<decl::Bool>("Use Creases"); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_subdivision_surface_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { -#ifndef WITH_OPENSUBDIV - UNUSED_VARS(ptr); - uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR); -#else +#ifdef WITH_OPENSUBDIV uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); +#else + UNUSED_VARS(layout, ptr); #endif } @@ -59,7 +56,6 @@ static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *n node->storage = data; } -namespace blender::nodes { static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -138,11 +134,10 @@ void register_node_type_geo_subdivision_surface() geo_node_type_base( &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates( - &ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out); + ntype.declare = blender::nodes::geo_node_subdivision_surface_declare; ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; - ntype.draw_buttons = geo_node_subdivision_surface_layout; - node_type_init(&ntype, geo_node_subdivision_surface_init); + ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout; + node_type_init(&ntype, blender::nodes::geo_node_subdivision_surface_init); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_storage(&ntype, "NodeGeometrySubdivisionSurface", diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index c9ce2de1ea1..ca857c4d2e3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -19,48 +19,47 @@ #include "UI_interface.h" #include "UI_resources.h" -static bNodeSocketTemplate geo_node_switch_in[] = { - {SOCK_BOOLEAN, N_("Switch")}, +namespace blender::nodes { + +static void geo_node_switch_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Bool>("Switch"); - {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_BOOLEAN, N_("False")}, - {SOCK_BOOLEAN, N_("True")}, - {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_STRING, N_("False")}, - {SOCK_STRING, N_("True")}, - {SOCK_GEOMETRY, N_("False")}, - {SOCK_GEOMETRY, N_("True")}, - {SOCK_OBJECT, N_("False")}, - {SOCK_OBJECT, N_("True")}, - {SOCK_COLLECTION, N_("False")}, - {SOCK_COLLECTION, N_("True")}, - {SOCK_TEXTURE, N_("False")}, - {SOCK_TEXTURE, N_("True")}, - {SOCK_MATERIAL, N_("False")}, - {SOCK_MATERIAL, N_("True")}, - {-1, ""}, -}; + b.add_input<decl::Float>("False"); + b.add_input<decl::Float>("True"); + b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000); + b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000); + b.add_input<decl::Bool>("False", "False_002"); + b.add_input<decl::Bool>("True", "True_002"); + b.add_input<decl::Vector>("False", "False_003"); + b.add_input<decl::Vector>("True", "True_003"); + b.add_input<decl::Color>("False", "False_004").default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>("True", "True_004").default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::String>("False", "False_005"); + b.add_input<decl::String>("True", "True_005"); + b.add_input<decl::Geometry>("False", "False_006"); + b.add_input<decl::Geometry>("True", "True_006"); + b.add_input<decl::Object>("False", "False_007"); + b.add_input<decl::Object>("True", "True_007"); + b.add_input<decl::Collection>("False", "False_008"); + b.add_input<decl::Collection>("True", "True_008"); + b.add_input<decl::Texture>("False", "False_009"); + b.add_input<decl::Texture>("True", "True_009"); + b.add_input<decl::Material>("False", "False_010"); + b.add_input<decl::Material>("True", "True_010"); -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")}, - {SOCK_TEXTURE, N_("Output")}, - {SOCK_MATERIAL, N_("Output")}, - {-1, ""}, -}; + b.add_output<decl::Float>("Output"); + b.add_output<decl::Int>("Output", "Output_001"); + b.add_output<decl::Bool>("Output", "Output_002"); + b.add_output<decl::Vector>("Output", "Output_003"); + b.add_output<decl::Color>("Output", "Output_004"); + b.add_output<decl::String>("Output", "Output_005"); + b.add_output<decl::Geometry>("Output", "Output_006"); + b.add_output<decl::Object>("Output", "Output_007"); + b.add_output<decl::Collection>("Output", "Output_008"); + b.add_output<decl::Texture>("Output", "Output_009"); + b.add_output<decl::Material>("Output", "Output_010"); +} static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -74,8 +73,6 @@ static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -namespace blender::nodes { - static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeSwitch *node_storage = (NodeSwitch *)node->storage; @@ -180,12 +177,12 @@ void register_node_type_geo_switch() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); - node_type_init(&ntype, geo_node_switch_init); + ntype.declare = blender::nodes::geo_node_switch_declare; + node_type_init(&ntype, blender::nodes::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.geometry_node_execute_supports_laziness = true; - ntype.draw_buttons = geo_node_switch_layout; + ntype.draw_buttons = blender::nodes::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 1ad5dbea492..d5eb067cad0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -32,21 +32,17 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_transform_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, - {SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_transform_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_transform_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER); + b.add_input<decl::Vector>("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ); + b.add_output<decl::Geometry>("Geometry"); +} + static bool use_translate(const float3 rotation, const float3 scale) { if (compare_ff(rotation.length_squared(), 0.0f, 1e-9f) != 1) { @@ -73,8 +69,7 @@ void transform_mesh(Mesh *mesh, else { const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); BKE_mesh_transform(mesh, matrix.values, false); - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } } @@ -206,7 +201,7 @@ void register_node_type_geo_transform() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_transform_in, geo_node_transform_out); + ntype.declare = blender::nodes::geo_node_transform_declare; ntype.geometry_node_execute = blender::nodes::geo_node_transform_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 03581b28f18..8886c7194db 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -27,16 +27,14 @@ Mesh *triangulate_mesh(Mesh *mesh, const int flag); } -static bNodeSocketTemplate geo_node_triangulate_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_INT, N_("Minimum Vertices"), 4, 0, 0, 0, 4, 10000}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate geo_node_triangulate_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +static void geo_node_triangulate_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Int>("Minimum Vertices").default_value(4).min(4).max(10000); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_triangulate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -50,7 +48,6 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node) node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY; } -namespace blender::nodes { static void geo_node_triangulate_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -79,9 +76,9 @@ void register_node_type_geo_triangulate() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_triangulate_in, geo_node_triangulate_out); - node_type_init(&ntype, geo_triangulate_init); + ntype.declare = blender::nodes::geo_node_triangulate_declare; + node_type_init(&ntype, blender::nodes::geo_triangulate_init); ntype.geometry_node_execute = blender::nodes::geo_node_triangulate_exec; - ntype.draw_buttons = geo_node_triangulate_layout; + ntype.draw_buttons = blender::nodes::geo_node_triangulate_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index f0b91f49f52..3331962341f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -16,16 +16,18 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_viewer_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { +static void geo_node_viewer_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); +} +} // namespace blender::nodes void register_node_type_geo_viewer() { static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0); - node_type_socket_templates(&ntype, geo_node_viewer_in, nullptr); + ntype.declare = blender::nodes::geo_node_viewer_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 4c1151bf6c2..229a35e0007 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -35,20 +35,18 @@ #include "UI_interface.h" #include "UI_resources.h" -static bNodeSocketTemplate geo_node_volume_to_mesh_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Density")}, - {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE}, - {SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, - {SOCK_FLOAT, N_("Threshold"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, - {SOCK_FLOAT, N_("Adaptivity"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_volume_to_mesh_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::String>("Density"); + b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); + b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f); + b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Geometry>("Geometry"); +} static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -57,8 +55,6 @@ static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C) uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); } -namespace blender::nodes { - static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( @@ -164,13 +160,13 @@ void register_node_type_geo_volume_to_mesh() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_volume_to_mesh_in, geo_node_volume_to_mesh_out); + ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; node_type_storage( &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); node_type_size(&ntype, 200, 120, 700); node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; - ntype.draw_buttons = geo_node_volume_to_mesh_layout; + ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 7487f11d77d..3b3b643d0ae 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -161,8 +161,10 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful { bke::geometry_set_instances_attribute_foreach( geometry_set, - [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) { - this->attributes_.append({attribute_name, meta_data.domain, meta_data.data_type}); + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_named()) { + this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); + } return true; }, 8); diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc new file mode 100644 index 00000000000..f6b6cc49b2e --- /dev/null +++ b/source/blender/nodes/intern/node_declaration.cc @@ -0,0 +1,94 @@ +/* + * 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 "NOD_node_declaration.hh" + +#include "BKE_node.h" + +namespace blender::nodes { + +void NodeDeclaration::build(bNodeTree &ntree, bNode &node) const +{ + for (const SocketDeclarationPtr &decl : inputs_) { + decl->build(ntree, node, SOCK_IN); + } + for (const SocketDeclarationPtr &decl : outputs_) { + decl->build(ntree, node, SOCK_OUT); + } +} + +bool NodeDeclaration::matches(const bNode &node) const +{ + auto check_sockets = [&](ListBase sockets, Span<SocketDeclarationPtr> socket_decls) { + const int tot_sockets = BLI_listbase_count(&sockets); + if (tot_sockets != socket_decls.size()) { + return false; + } + int i; + LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &sockets, i) { + const SocketDeclaration &socket_decl = *socket_decls[i]; + if (!socket_decl.matches(*socket)) { + return false; + } + } + return true; + }; + + if (!check_sockets(node.inputs, inputs_)) { + return false; + } + if (!check_sockets(node.outputs, outputs_)) { + return false; + } + return true; +} + +bNodeSocket &SocketDeclaration::update_or_build(bNodeTree &ntree, + bNode &node, + bNodeSocket &socket) const +{ + /* By default just rebuild. */ + return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); +} + +void SocketDeclaration::set_common_flags(bNodeSocket &socket) const +{ + SET_FLAG_FROM_TEST(socket.flag, hide_value_, SOCK_HIDE_VALUE); + SET_FLAG_FROM_TEST(socket.flag, hide_label_, SOCK_HIDE_LABEL); + SET_FLAG_FROM_TEST(socket.flag, is_multi_input_, SOCK_MULTI_INPUT); +} + +bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const +{ + if (socket.name != name_) { + return false; + } + if (socket.identifier != identifier_) { + return false; + } + if (((socket.flag & SOCK_HIDE_VALUE) != 0) != hide_value_) { + return false; + } + if (((socket.flag & SOCK_HIDE_LABEL) != 0) != hide_label_) { + return false; + } + if (((socket.flag & SOCK_MULTI_INPUT) != 0) != is_multi_input_) { + return false; + } + return true; +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 528616eb23a..31260f95242 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -44,9 +44,14 @@ #include "MEM_guardedalloc.h" +#include "NOD_node_declaration.hh" #include "NOD_socket.h" #include "FN_cpp_type_make.hh" +#include "FN_field.hh" + +using namespace blender; +using blender::nodes::SocketDeclarationPtr; struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree, struct bNode *node, @@ -182,9 +187,94 @@ static void verify_socket_template_list(bNodeTree *ntree, } } -void node_verify_socket_templates(bNodeTree *ntree, bNode *node) +static void refresh_socket_list(bNodeTree &ntree, + bNode &node, + ListBase &sockets, + Span<SocketDeclarationPtr> socket_decls, + const eNodeSocketInOut in_out, + const bool do_id_user) +{ + Vector<bNodeSocket *> old_sockets = sockets; + VectorSet<bNodeSocket *> new_sockets; + for (const SocketDeclarationPtr &socket_decl : socket_decls) { + /* Try to find a socket that corresponds to the declaration. */ + bNodeSocket *old_socket_with_same_identifier = nullptr; + for (const int i : old_sockets.index_range()) { + bNodeSocket &old_socket = *old_sockets[i]; + if (old_socket.identifier == socket_decl->identifier()) { + old_sockets.remove_and_reorder(i); + old_socket_with_same_identifier = &old_socket; + break; + } + } + bNodeSocket *new_socket = nullptr; + if (old_socket_with_same_identifier == nullptr) { + /* Create a completely new socket. */ + new_socket = &socket_decl->build(ntree, node, in_out); + } + else { + STRNCPY(old_socket_with_same_identifier->name, socket_decl->name().c_str()); + if (socket_decl->matches(*old_socket_with_same_identifier)) { + /* The existing socket matches exactly, just use it. */ + new_socket = old_socket_with_same_identifier; + } + else { + /* Clear out identifier to avoid name collisions when a new socket is created. */ + old_socket_with_same_identifier->identifier[0] = '\0'; + new_socket = &socket_decl->update_or_build(ntree, node, *old_socket_with_same_identifier); + + if (new_socket == old_socket_with_same_identifier) { + /* The existing socket has been updated, set the correct identifier again. */ + STRNCPY(new_socket->identifier, socket_decl->identifier().c_str()); + } + else { + /* Move links to new socket with same identifier. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (link->fromsock == old_socket_with_same_identifier) { + link->fromsock = new_socket; + } + else if (link->tosock == old_socket_with_same_identifier) { + link->tosock = new_socket; + } + } + } + } + } + new_sockets.add_new(new_socket); + } + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &sockets) { + if (!new_sockets.contains(old_socket)) { + nodeRemoveSocketEx(&ntree, &node, old_socket, do_id_user); + } + } + BLI_listbase_clear(&sockets); + for (bNodeSocket *socket : new_sockets) { + BLI_addtail(&sockets, socket); + } +} + +static void refresh_node(bNodeTree &ntree, + bNode &node, + blender::nodes::NodeDeclaration &node_decl, + bool do_id_user) +{ + refresh_socket_list(ntree, node, node.inputs, node_decl.inputs(), SOCK_IN, do_id_user); + refresh_socket_list(ntree, node, node.outputs, node_decl.outputs(), SOCK_OUT, do_id_user); +} + +void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user) { bNodeType *ntype = node->typeinfo; + if (ntype == nullptr) { + return; + } + if (ntype->declare != nullptr) { + nodeDeclarationEnsure(ntree, node); + if (!node->declaration->matches(*node)) { + refresh_node(*ntree, *node, *node->declaration, do_id_user); + } + return; + } /* Don't try to match socket lists when there are no templates. * This prevents dynamically generated sockets to be removed, like for * group, image or render layer nodes. We have an explicit check for the @@ -610,8 +700,14 @@ static bNodeSocketType *make_socket_type_bool() socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get<blender::fn::Field<bool>>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + bool value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field<bool>(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -622,8 +718,14 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype) socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get<blender::fn::Field<float>>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + float value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field<float>(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -634,8 +736,14 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype) socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get<blender::fn::Field<int>>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + int value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field<int>(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -646,8 +754,14 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get<blender::fn::Field<blender::float3>>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + blender::float3 value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field<blender::float3>(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -660,8 +774,15 @@ static bNodeSocketType *make_socket_type_rgba() socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get<blender::fn::Field<blender::ColorGeometry4f>>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + blender::ColorGeometry4f value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) + blender::fn::Field<blender::ColorGeometry4f>(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -672,8 +793,15 @@ static bNodeSocketType *make_socket_type_string() socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value); }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get<blender::fn::Field<std::string>>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + std::string value; + value.~basic_string(); + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field<std::string>(blender::fn::make_constant_field(value)); + }; return socktype; } diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc new file mode 100644 index 00000000000..4b0dbad3cff --- /dev/null +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -0,0 +1,321 @@ +/* + * 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 "NOD_socket_declarations.hh" + +#include "BKE_node.h" + +#include "BLI_math_vector.h" + +namespace blender::nodes::decl { + +static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subtype) +{ + const char *idname = nodeStaticSocketType(socket.type, new_subtype); + BLI_strncpy(socket.idname, idname, sizeof(socket.idname)); + bNodeSocketType *socktype = nodeSocketTypeFind(idname); + socket.typeinfo = socktype; +} + +/* -------------------------------------------------------------------- + * Float. + */ + +bNodeSocket &Float::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +{ + bNodeSocket &socket = *nodeAddStaticSocket( + &ntree, &node, in_out, SOCK_FLOAT, subtype_, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value; + value.min = soft_min_value_; + value.max = soft_max_value_; + value.value = default_value_; + return socket; +} + +bool Float::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + return false; + } + if (socket.type != SOCK_FLOAT) { + return false; + } + if (socket.typeinfo->subtype != subtype_) { + return false; + } + bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value; + if (value.min != soft_min_value_) { + return false; + } + if (value.max != soft_max_value_) { + return false; + } + return true; +} + +bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const +{ + if (socket.type != SOCK_FLOAT) { + return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + } + if (socket.typeinfo->subtype != subtype_) { + modify_subtype_except_for_storage(socket, subtype_); + } + this->set_common_flags(socket); + bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value; + value.min = soft_min_value_; + value.max = soft_max_value_; + value.subtype = subtype_; + return socket; +} + +/* -------------------------------------------------------------------- + * Int. + */ + +bNodeSocket &Int::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +{ + bNodeSocket &socket = *nodeAddStaticSocket( + &ntree, &node, in_out, SOCK_INT, subtype_, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value; + value.min = soft_min_value_; + value.max = soft_max_value_; + value.value = default_value_; + return socket; +} + +bool Int::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + return false; + } + if (socket.type != SOCK_INT) { + return false; + } + if (socket.typeinfo->subtype != subtype_) { + return false; + } + bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value; + if (value.min != soft_min_value_) { + return false; + } + if (value.max != soft_max_value_) { + return false; + } + return true; +} + +bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const +{ + if (socket.type != SOCK_INT) { + return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + } + if (socket.typeinfo->subtype != subtype_) { + modify_subtype_except_for_storage(socket, subtype_); + } + this->set_common_flags(socket); + bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value; + value.min = soft_min_value_; + value.max = soft_max_value_; + value.subtype = subtype_; + return socket; +} + +/* -------------------------------------------------------------------- + * Vector. + */ + +bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +{ + bNodeSocket &socket = *nodeAddStaticSocket( + &ntree, &node, in_out, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value; + copy_v3_v3(value.value, default_value_); + value.min = soft_min_value_; + value.max = soft_max_value_; + return socket; +} + +bool Vector::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + return false; + } + if (socket.type != SOCK_VECTOR) { + return false; + } + if (socket.typeinfo->subtype != subtype_) { + return false; + } + return true; +} + +bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const +{ + if (socket.type != SOCK_VECTOR) { + return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + } + if (socket.typeinfo->subtype != subtype_) { + modify_subtype_except_for_storage(socket, subtype_); + } + this->set_common_flags(socket); + bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value; + value.subtype = subtype_; + STRNCPY(socket.name, name_.c_str()); + return socket; +} + +/* -------------------------------------------------------------------- + * Bool. + */ + +bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +{ + bNodeSocket &socket = *nodeAddStaticSocket( + &ntree, &node, in_out, SOCK_BOOLEAN, PROP_NONE, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + bNodeSocketValueBoolean &value = *(bNodeSocketValueBoolean *)socket.default_value; + value.value = default_value_; + return socket; +} + +bool Bool::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + return false; + } + if (socket.type != SOCK_BOOLEAN) { + return false; + } + return true; +} + +/* -------------------------------------------------------------------- + * Color. + */ + +bNodeSocket &Color::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +{ + bNodeSocket &socket = *nodeAddStaticSocket( + &ntree, &node, in_out, SOCK_RGBA, PROP_NONE, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + bNodeSocketValueRGBA &value = *(bNodeSocketValueRGBA *)socket.default_value; + copy_v4_v4(value.value, default_value_); + return socket; +} + +bool Color::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + if (socket.name != name_) { + return false; + } + if (socket.identifier != identifier_) { + return false; + } + } + if (socket.type != SOCK_RGBA) { + return false; + } + return true; +} + +/* -------------------------------------------------------------------- + * String. + */ + +bNodeSocket &String::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +{ + bNodeSocket &socket = *nodeAddStaticSocket( + &ntree, &node, in_out, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + return socket; +} + +bool String::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + return false; + } + if (socket.type != SOCK_STRING) { + return false; + } + return true; +} + +/* -------------------------------------------------------------------- + * IDSocketDeclaration. + */ + +bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, + bNode &node, + eNodeSocketInOut in_out) const +{ + bNodeSocket &socket = *nodeAddSocket( + &ntree, &node, in_out, idname_, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + return socket; +} + +bool IDSocketDeclaration::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + return false; + } + if (!STREQ(socket.idname, idname_)) { + return false; + } + return true; +} + +bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree, + bNode &node, + bNodeSocket &socket) const +{ + if (StringRef(socket.idname) != idname_) { + return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + } + this->set_common_flags(socket); + return socket; +} + +/* -------------------------------------------------------------------- + * Geometry. + */ + +bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +{ + bNodeSocket &socket = *nodeAddSocket( + &ntree, &node, in_out, "NodeSocketGeometry", identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + return socket; +} + +bool Geometry::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + return false; + } + if (socket.type != SOCK_GEOMETRY) { + return false; + } + return true; +} + +} // namespace blender::nodes::decl diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.c b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc index e3d4bad2bf8..0c0d75179a9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -43,7 +43,8 @@ static bNodeSocketTemplate sh_node_tex_gradient_out[] = { static void node_shader_init_tex_gradient(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexGradient *tex = MEM_callocN(sizeof(NodeTexGradient), "NodeTexGradient"); + NodeTexGradient *tex = (NodeTexGradient *)MEM_callocN(sizeof(NodeTexGradient), + "NodeTexGradient"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->gradient_type = SHD_BLEND_LINEAR; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index 420c5b75926..f5e9aef3aad 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -49,7 +49,8 @@ static bNodeSocketTemplate sh_node_tex_musgrave_out[] = { static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexMusgrave *tex = MEM_callocN(sizeof(NodeTexMusgrave), "NodeTexMusgrave"); + NodeTexMusgrave *tex = (NodeTexMusgrave *)MEM_callocN(sizeof(NodeTexMusgrave), + "NodeTexMusgrave"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->musgrave_type = SHD_MUSGRAVE_FBM; @@ -58,6 +59,41 @@ static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) node->storage = tex; } +static const char *gpu_shader_name_get(const int type, const int dimensions) +{ + BLI_assert(type >= 0 && type < 5); + BLI_assert(dimensions > 0 && dimensions < 5); + + switch (type) { + case SHD_MUSGRAVE_MULTIFRACTAL: + return std::array{"node_tex_musgrave_multi_fractal_1d", + "node_tex_musgrave_multi_fractal_2d", + "node_tex_musgrave_multi_fractal_3d", + "node_tex_musgrave_multi_fractal_4d"}[dimensions - 1]; + case SHD_MUSGRAVE_FBM: + return std::array{"node_tex_musgrave_fBm_1d", + "node_tex_musgrave_fBm_2d", + "node_tex_musgrave_fBm_3d", + "node_tex_musgrave_fBm_4d"}[dimensions - 1]; + case SHD_MUSGRAVE_HYBRID_MULTIFRACTAL: + return std::array{"node_tex_musgrave_hybrid_multi_fractal_1d", + "node_tex_musgrave_hybrid_multi_fractal_2d", + "node_tex_musgrave_hybrid_multi_fractal_3d", + "node_tex_musgrave_hybrid_multi_fractal_4d"}[dimensions - 1]; + case SHD_MUSGRAVE_RIDGED_MULTIFRACTAL: + return std::array{"node_tex_musgrave_ridged_multi_fractal_1d", + "node_tex_musgrave_ridged_multi_fractal_2d", + "node_tex_musgrave_ridged_multi_fractal_3d", + "node_tex_musgrave_ridged_multi_fractal_4d"}[dimensions - 1]; + case SHD_MUSGRAVE_HETERO_TERRAIN: + return std::array{"node_tex_musgrave_hetero_terrain_1d", + "node_tex_musgrave_hetero_terrain_2d", + "node_tex_musgrave_hetero_terrain_3d", + "node_tex_musgrave_hetero_terrain_4d"}[dimensions - 1]; + } + return nullptr; +} + static int node_shader_gpu_tex_musgrave(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -71,53 +107,9 @@ static int node_shader_gpu_tex_musgrave(GPUMaterial *mat, int dimensions = tex->dimensions; int type = tex->musgrave_type; - static const char *names[][5] = { - [SHD_MUSGRAVE_MULTIFRACTAL] = - { - "", - "node_tex_musgrave_multi_fractal_1d", - "node_tex_musgrave_multi_fractal_2d", - "node_tex_musgrave_multi_fractal_3d", - "node_tex_musgrave_multi_fractal_4d", - }, - [SHD_MUSGRAVE_FBM] = - { - "", - "node_tex_musgrave_fBm_1d", - "node_tex_musgrave_fBm_2d", - "node_tex_musgrave_fBm_3d", - "node_tex_musgrave_fBm_4d", - }, - [SHD_MUSGRAVE_HYBRID_MULTIFRACTAL] = - { - "", - "node_tex_musgrave_hybrid_multi_fractal_1d", - "node_tex_musgrave_hybrid_multi_fractal_2d", - "node_tex_musgrave_hybrid_multi_fractal_3d", - "node_tex_musgrave_hybrid_multi_fractal_4d", - }, - [SHD_MUSGRAVE_RIDGED_MULTIFRACTAL] = - { - "", - "node_tex_musgrave_ridged_multi_fractal_1d", - "node_tex_musgrave_ridged_multi_fractal_2d", - "node_tex_musgrave_ridged_multi_fractal_3d", - "node_tex_musgrave_ridged_multi_fractal_4d", - }, - [SHD_MUSGRAVE_HETERO_TERRAIN] = - { - "", - "node_tex_musgrave_hetero_terrain_1d", - "node_tex_musgrave_hetero_terrain_2d", - "node_tex_musgrave_hetero_terrain_3d", - "node_tex_musgrave_hetero_terrain_4d", - }, - }; - - BLI_assert(type >= 0 && type < 5); - BLI_assert(dimensions > 0 && dimensions < 5); + const char *name = gpu_shader_name_get(type, dimensions); - return GPU_stack_link(mat, node, names[type][dimensions], in, out); + return GPU_stack_link(mat, node, name, in, out); } static void node_shader_update_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 7b67c2d1f2e..de8e0916f4d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -48,7 +48,7 @@ static bNodeSocketTemplate sh_node_tex_noise_out[] = { static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexNoise *tex = MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise"); + NodeTexNoise *tex = (NodeTexNoise *)MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->dimensions = 3; @@ -56,6 +56,16 @@ static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) node->storage = tex; } +static const char *gpu_shader_get_name(const int dimensions) +{ + BLI_assert(dimensions >= 1 && dimensions <= 4); + return std::array{"node_noise_texture_1d", + "node_noise_texture_2d", + "node_noise_texture_3d", + "node_noise_texture_4d"}[dimensions - 1]; + return nullptr; +} + static int node_shader_gpu_tex_noise(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -66,14 +76,8 @@ static int node_shader_gpu_tex_noise(GPUMaterial *mat, node_shader_gpu_tex_mapping(mat, node, in, out); NodeTexNoise *tex = (NodeTexNoise *)node->storage; - static const char *names[] = { - "", - "node_noise_texture_1d", - "node_noise_texture_2d", - "node_noise_texture_3d", - "node_noise_texture_4d", - }; - return GPU_stack_link(mat, node, names[tex->dimensions], in, out); + const char *name = gpu_shader_get_name(tex->dimensions); + return GPU_stack_link(mat, node, name, in, out); } static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 64dc44fc67d..1cc715d99ea 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -69,7 +69,7 @@ static bNodeSocketTemplate sh_node_tex_voronoi_out[] = { static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexVoronoi *tex = MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi"); + NodeTexVoronoi *tex = (NodeTexVoronoi *)MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->dimensions = 3; @@ -79,6 +79,51 @@ static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) node->storage = tex; } +static const char *gpu_shader_get_name(const int feature, const int dimensions) +{ + BLI_assert(feature >= 0 && feature < 5); + BLI_assert(dimensions > 0 && dimensions < 5); + + switch (feature) { + case SHD_VORONOI_F1: + return std::array{ + "node_tex_voronoi_f1_1d", + "node_tex_voronoi_f1_2d", + "node_tex_voronoi_f1_3d", + "node_tex_voronoi_f1_4d", + }[dimensions - 1]; + case SHD_VORONOI_F2: + return std::array{ + "node_tex_voronoi_f2_1d", + "node_tex_voronoi_f2_2d", + "node_tex_voronoi_f2_3d", + "node_tex_voronoi_f2_4d", + }[dimensions - 1]; + case SHD_VORONOI_SMOOTH_F1: + return std::array{ + "node_tex_voronoi_smooth_f1_1d", + "node_tex_voronoi_smooth_f1_2d", + "node_tex_voronoi_smooth_f1_3d", + "node_tex_voronoi_smooth_f1_4d", + }[dimensions - 1]; + case SHD_VORONOI_DISTANCE_TO_EDGE: + return std::array{ + "node_tex_voronoi_distance_to_edge_1d", + "node_tex_voronoi_distance_to_edge_2d", + "node_tex_voronoi_distance_to_edge_3d", + "node_tex_voronoi_distance_to_edge_4d", + }[dimensions - 1]; + case SHD_VORONOI_N_SPHERE_RADIUS: + return std::array{ + "node_tex_voronoi_n_sphere_radius_1d", + "node_tex_voronoi_n_sphere_radius_2d", + "node_tex_voronoi_n_sphere_radius_3d", + "node_tex_voronoi_n_sphere_radius_4d", + }[dimensions - 1]; + } + return nullptr; +} + static int node_shader_gpu_tex_voronoi(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -88,57 +133,12 @@ static int node_shader_gpu_tex_voronoi(GPUMaterial *mat, node_shader_gpu_default_tex_coord(mat, node, &in[0].link); node_shader_gpu_tex_mapping(mat, node, in, out); - static const char *names[][5] = { - [SHD_VORONOI_F1] = - { - "", - "node_tex_voronoi_f1_1d", - "node_tex_voronoi_f1_2d", - "node_tex_voronoi_f1_3d", - "node_tex_voronoi_f1_4d", - }, - [SHD_VORONOI_F2] = - { - "", - "node_tex_voronoi_f2_1d", - "node_tex_voronoi_f2_2d", - "node_tex_voronoi_f2_3d", - "node_tex_voronoi_f2_4d", - }, - [SHD_VORONOI_SMOOTH_F1] = - { - "", - "node_tex_voronoi_smooth_f1_1d", - "node_tex_voronoi_smooth_f1_2d", - "node_tex_voronoi_smooth_f1_3d", - "node_tex_voronoi_smooth_f1_4d", - }, - [SHD_VORONOI_DISTANCE_TO_EDGE] = - { - "", - "node_tex_voronoi_distance_to_edge_1d", - "node_tex_voronoi_distance_to_edge_2d", - "node_tex_voronoi_distance_to_edge_3d", - "node_tex_voronoi_distance_to_edge_4d", - }, - [SHD_VORONOI_N_SPHERE_RADIUS] = - { - "", - "node_tex_voronoi_n_sphere_radius_1d", - "node_tex_voronoi_n_sphere_radius_2d", - "node_tex_voronoi_n_sphere_radius_3d", - "node_tex_voronoi_n_sphere_radius_4d", - }, - }; - NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage; float metric = tex->distance; - BLI_assert(tex->feature >= 0 && tex->feature < 5); - BLI_assert(tex->dimensions > 0 && tex->dimensions < 5); + const char *name = gpu_shader_get_name(tex->feature, tex->dimensions); - return GPU_stack_link( - mat, node, names[tex->feature][tex->dimensions], in, out, GPU_constant(&metric)); + return GPU_stack_link(mat, node, name, in, out, GPU_constant(&metric)); } static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index 60a3392c761..6e973189065 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -37,25 +37,23 @@ static void node_shader_init_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *no node->custom1 = 3; } +static const char *gpu_shader_get_name(const int dimensions) +{ + BLI_assert(dimensions >= 1 && dimensions <= 4); + return std::array{"node_white_noise_1d", + "node_white_noise_2d", + "node_white_noise_3d", + "node_white_noise_4d"}[dimensions - 1]; +} + static int gpu_shader_tex_white_noise(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - static const char *names[] = { - "", - "node_white_noise_1d", - "node_white_noise_2d", - "node_white_noise_3d", - "node_white_noise_4d", - }; - - if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) { - return GPU_stack_link(mat, node, names[node->custom1], in, out); - } - - return 0; + const char *name = gpu_shader_get_name(node->custom1); + return GPU_stack_link(mat, node, name, in, out); } static void node_shader_update_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index 10cc1e8b113..144487cb020 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC idprop_py_api.c idprop_py_ui_api.c imbuf_py_api.c + py_capi_rna.c py_capi_utils.c bgl.h @@ -47,6 +48,7 @@ set(SRC idprop_py_api.h idprop_py_ui_api.h imbuf_py_api.h + py_capi_rna.h py_capi_utils.h # header-only diff --git a/source/blender/python/generic/bpy_threads.c b/source/blender/python/generic/bpy_threads.c index bd707f728a1..8aa8c5c5d92 100644 --- a/source/blender/python/generic/bpy_threads.c +++ b/source/blender/python/generic/bpy_threads.c @@ -29,8 +29,12 @@ /* analogue of PyEval_SaveThread() */ BPy_ThreadStatePtr BPY_thread_save(void) { - /* The thread-state can be NULL when quitting Blender. */ - if (_PyThreadState_UncheckedGet()) { + /* Use `_PyThreadState_UncheckedGet()` instead of `PyThreadState_Get()`, to avoid a fatal error + * issued when a thread state is NULL (the thread state can be NULL when quitting Blender). + * + * `PyEval_SaveThread()` will release the GIL, so this thread has to have the GIL to begin with + * or badness will ensue. */ + if (_PyThreadState_UncheckedGet() && PyGILState_Check()) { return (BPy_ThreadStatePtr)PyEval_SaveThread(); } return NULL; diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 58a947943b1..8dc0d2fb857 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -533,6 +533,7 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) for (i = 0; i < val.array.len; i++) { item = ob_seq_fast_items[i]; if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) { + IDP_FreeProperty(prop); return NULL; } } @@ -545,6 +546,7 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) for (i = 0; i < val.array.len; i++) { item = ob_seq_fast_items[i]; if (((prop_data[i] = PyC_Long_AsI32(item)) == -1) && PyErr_Occurred()) { + IDP_FreeProperty(prop); return NULL; } } @@ -555,6 +557,7 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) for (i = 0; i < val.array.len; i++) { item = ob_seq_fast_items[i]; if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) { + IDP_FreeProperty(prop); return NULL; } } @@ -714,8 +717,12 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, prop->next = prop_exist->next; prop->flag = prop_exist->flag; + /* Don't free and reset the existing property's UI data, since this only assigns a value. */ + IDPropertyUIData *ui_data = prop_exist->ui_data; + prop_exist->ui_data = NULL; IDP_FreePropertyContent(prop_exist); *prop_exist = *prop; + prop_exist->ui_data = ui_data; MEM_freeN(prop); } else { diff --git a/source/blender/python/generic/idprop_py_ui_api.c b/source/blender/python/generic/idprop_py_ui_api.c index 92cc3b8e1dd..7827bd48dfe 100644 --- a/source/blender/python/generic/idprop_py_ui_api.c +++ b/source/blender/python/generic/idprop_py_ui_api.c @@ -34,13 +34,12 @@ #include "RNA_access.h" #include "RNA_enum_types.h" -#include "../intern/bpy_rna.h" - #define USE_STRING_COERCE #ifdef USE_STRING_COERCE # include "py_capi_utils.h" #endif +#include "py_capi_rna.h" #include "python_utildefines.h" @@ -184,7 +183,7 @@ static bool idprop_ui_data_update_int(IDProperty *idprop, PyObject *args, PyObje } } - /* Write back to the proeprty's UI data. */ + /* Write back to the property's UI data. */ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); *ui_data_orig = ui_data; return true; @@ -311,7 +310,7 @@ static bool idprop_ui_data_update_float(IDProperty *idprop, PyObject *args, PyOb } } - /* Write back to the proeprty's UI data. */ + /* Write back to the property's UI data. */ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); *ui_data_orig = ui_data; return true; @@ -349,7 +348,7 @@ static bool idprop_ui_data_update_string(IDProperty *idprop, PyObject *args, PyO ui_data.default_value = BLI_strdup(default_value); } - /* Write back to the proeprty's UI data. */ + /* Write back to the property's UI data. */ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); *ui_data_orig = ui_data; return true; @@ -377,7 +376,7 @@ static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObjec return false; } - /* Write back to the proeprty's UI data. */ + /* Write back to the property's UI data. */ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); *ui_data_orig = ui_data; return true; @@ -469,7 +468,7 @@ static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict) Py_DECREF(list); } else { - PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->step)); + PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->default_value)); Py_DECREF(item); } } @@ -489,7 +488,7 @@ static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict) Py_DECREF(item); PyDict_SetItemString(dict, "step", item = PyFloat_FromDouble((double)ui_data->step)); Py_DECREF(item); - PyDict_SetItemString(dict, "precision", item = PyFloat_FromDouble((double)ui_data->precision)); + PyDict_SetItemString(dict, "precision", item = PyLong_FromDouble((double)ui_data->precision)); Py_DECREF(item); if (property->type == IDP_ARRAY) { PyObject *list = PyList_New(ui_data->default_array_len); @@ -500,7 +499,7 @@ static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict) Py_DECREF(list); } else { - PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->step)); + PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->default_value)); Py_DECREF(item); } } diff --git a/source/blender/python/generic/py_capi_rna.c b/source/blender/python/generic/py_capi_rna.c new file mode 100644 index 00000000000..235b136ca74 --- /dev/null +++ b/source/blender/python/generic/py_capi_rna.c @@ -0,0 +1,267 @@ +/* + * 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 pygen + * + * Python/RNA utilities. + * + * RNA functions that aren't part of the `bpy_rna.c` API. + */ + +/* Future-proof, See https://docs.python.org/3/c-api/arg.html#strings-and-buffers */ +#define PY_SSIZE_T_CLEAN + +#include <Python.h> +#include <stdbool.h> + +#include "py_capi_rna.h" + +#include "BLI_bitmap.h" +#include "BLI_dynstr.h" + +#include "RNA_access.h" + +#include "MEM_guardedalloc.h" + +/* -------------------------------------------------------------------- */ +/** \name Enum Utilities + * \{ */ + +/** + * Convert all items into a single comma separated string. + * Use for creating useful error messages. + */ +char *pyrna_enum_repr(const EnumPropertyItem *item) +{ + DynStr *dynstr = BLI_dynstr_new(); + + /* We can't compare with the first element in the array + * since it may be a category (without an identifier). */ + for (bool is_first = true; item->identifier; item++) { + if (item->identifier[0]) { + BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier); + is_first = false; + } + } + + char *cstring = BLI_dynstr_get_cstring(dynstr); + BLI_dynstr_free(dynstr); + return cstring; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Enum Conversion Utilities + * \{ */ + +/** + * Same as #RNA_enum_value_from_id, but raises an exception. + */ +int pyrna_enum_value_from_id(const EnumPropertyItem *item, + const char *identifier, + int *r_value, + const char *error_prefix) +{ + if (RNA_enum_value_from_id(item, identifier, r_value) == 0) { + const char *enum_str = pyrna_enum_repr(item); + PyErr_Format( + PyExc_ValueError, "%s: '%.200s' not found in (%s)", error_prefix, identifier, enum_str); + MEM_freeN((void *)enum_str); + return -1; + } + + return 0; +} + +/** + * Takes a set of strings and map it to and array of booleans. + * + * Useful when the values aren't flags. + * + * \param type_convert_sign: Maps signed to unsigned range, + * needed when we want to use the full range of a signed short/char. + */ +BLI_bitmap *pyrna_enum_bitmap_from_set(const EnumPropertyItem *items, + PyObject *value, + int type_size, + bool type_convert_sign, + int bitmap_size, + const char *error_prefix) +{ + /* Set looping. */ + Py_ssize_t pos = 0; + Py_ssize_t hash = 0; + PyObject *key; + + BLI_bitmap *bitmap = BLI_BITMAP_NEW(bitmap_size, __func__); + + while (_PySet_NextEntry(value, &pos, &key, &hash)) { + const char *param = PyUnicode_AsUTF8(key); + if (param == NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s expected a string, not %.200s", + error_prefix, + Py_TYPE(key)->tp_name); + goto error; + } + + int ret; + if (pyrna_enum_value_from_id(items, param, &ret, error_prefix) == -1) { + goto error; + } + + int index = ret; + + if (type_convert_sign) { + if (type_size == 2) { + union { + signed short as_signed; + ushort as_unsigned; + } ret_convert; + ret_convert.as_signed = (signed short)ret; + index = (int)ret_convert.as_unsigned; + } + else if (type_size == 1) { + union { + signed char as_signed; + uchar as_unsigned; + } ret_convert; + ret_convert.as_signed = (signed char)ret; + index = (int)ret_convert.as_unsigned; + } + else { + BLI_assert_unreachable(); + } + } + BLI_assert(index < bitmap_size); + BLI_BITMAP_ENABLE(bitmap, index); + } + + return bitmap; + +error: + MEM_freeN(bitmap); + return NULL; +} + +/** + * 'value' _must_ be a set type, error check before calling. + */ +int pyrna_enum_bitfield_from_set(const EnumPropertyItem *items, + PyObject *value, + int *r_value, + const char *error_prefix) +{ + /* Set of enum items, concatenate all values with OR. */ + int ret, flag = 0; + + /* Set looping. */ + Py_ssize_t pos = 0; + Py_ssize_t hash = 0; + PyObject *key; + + *r_value = 0; + + while (_PySet_NextEntry(value, &pos, &key, &hash)) { + const char *param = PyUnicode_AsUTF8(key); + + if (param == NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s expected a string, not %.200s", + error_prefix, + Py_TYPE(key)->tp_name); + return -1; + } + + if (pyrna_enum_value_from_id(items, param, &ret, error_prefix) == -1) { + return -1; + } + + flag |= ret; + } + + *r_value = flag; + return 0; +} + +PyObject *pyrna_enum_bitfield_as_set(const EnumPropertyItem *items, int value) +{ + PyObject *ret = PySet_New(NULL); + const char *identifier[RNA_ENUM_BITFLAG_SIZE + 1]; + + if (RNA_enum_bitflag_identifiers(items, value, identifier)) { + PyObject *item; + int index; + for (index = 0; identifier[index]; index++) { + item = PyUnicode_FromString(identifier[index]); + PySet_Add(ret, item); + Py_DECREF(item); + } + } + + return ret; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Argument Parsing Helpers + * \{ */ + +/** + * Use with #PyArg_ParseTuple's `O&` formatting. + */ +int pyrna_enum_value_parse_string(PyObject *o, void *p) +{ + const char *identifier = PyUnicode_AsUTF8(o); + if (identifier == NULL) { + PyErr_Format(PyExc_TypeError, "expected a string enum, not %.200s", Py_TYPE(o)->tp_name); + return 0; + } + struct BPy_EnumProperty_Parse *parse_data = p; + if (pyrna_enum_value_from_id( + parse_data->items, identifier, &parse_data->value, "enum identifier") == -1) { + return 0; + } + + parse_data->value_orig = o; + parse_data->is_set = true; + return 1; +} + +/** + * Use with #PyArg_ParseTuple's `O&` formatting. + */ +int pyrna_enum_bitfield_parse_set(PyObject *o, void *p) +{ + if (!PySet_Check(o)) { + PyErr_Format(PyExc_TypeError, "expected a set, not %.200s", Py_TYPE(o)->tp_name); + return 0; + } + + struct BPy_EnumProperty_Parse *parse_data = p; + if (pyrna_enum_bitfield_from_set( + parse_data->items, o, &parse_data->value, "enum identifier set") == -1) { + return 0; + } + parse_data->value_orig = o; + parse_data->is_set = true; + return 1; +} + +/** \} */ diff --git a/source/blender/python/generic/py_capi_rna.h b/source/blender/python/generic/py_capi_rna.h new file mode 100644 index 00000000000..326ca777a4b --- /dev/null +++ b/source/blender/python/generic/py_capi_rna.h @@ -0,0 +1,66 @@ +/* + * 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 pygen + */ + +#pragma once + +#include "BLI_sys_types.h" + +struct EnumPropertyItem; + +char *pyrna_enum_repr(const struct EnumPropertyItem *item); + +int pyrna_enum_value_from_id(const struct EnumPropertyItem *item, + const char *identifier, + int *value, + const char *error_prefix); + +unsigned int *pyrna_enum_bitmap_from_set(const struct EnumPropertyItem *items, + PyObject *value, + int type_size, + bool type_convert_sign, + int bitmap_size, + const char *error_prefix); + +int pyrna_enum_bitfield_from_set(const struct EnumPropertyItem *items, + PyObject *value, + int *r_value, + const char *error_prefix); + +PyObject *pyrna_enum_bitfield_as_set(const struct EnumPropertyItem *items, int value); + +/** + * Data for #pyrna_enum_value_parse_string & #pyrna_enum_bitfield_parse_set parsing utilities. + * Use with #PyArg_ParseTuple's `O&` formatting. + */ +struct BPy_EnumProperty_Parse { + const struct EnumPropertyItem *items; + /** + * Set when the value was successfully parsed. + * Useful if the input ever needs to be included in an error message. + * (if the value is not supported under certain conditions). + */ + PyObject *value_orig; + + int value; + bool is_set; +}; +int pyrna_enum_value_parse_string(PyObject *o, void *p); +int pyrna_enum_bitfield_parse_set(PyObject *o, void *p); diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 51e20a31ba8..9dac5a4f21f 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -1362,7 +1362,7 @@ void *PyC_RNA_AsPointer(PyObject *value, const char *type_name) * Convert to/from Python set of strings to an int flag. * \{ */ -PyObject *PyC_FlagSet_AsString(PyC_FlagSet *item) +PyObject *PyC_FlagSet_AsString(const PyC_FlagSet *item) { PyObject *py_items = PyList_New(0); for (; item->identifier; item++) { @@ -1373,7 +1373,7 @@ PyObject *PyC_FlagSet_AsString(PyC_FlagSet *item) return py_string; } -int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int *r_value) +int PyC_FlagSet_ValueFromID_int(const PyC_FlagSet *item, const char *identifier, int *r_value) { for (; item->identifier; item++) { if (STREQ(item->identifier, identifier)) { @@ -1385,7 +1385,7 @@ int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int * return 0; } -int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, +int PyC_FlagSet_ValueFromID(const PyC_FlagSet *item, const char *identifier, int *r_value, const char *error_prefix) @@ -1401,7 +1401,7 @@ int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, return 0; } -int PyC_FlagSet_ToBitfield(PyC_FlagSet *items, +int PyC_FlagSet_ToBitfield(const PyC_FlagSet *items, PyObject *value, int *r_value, const char *error_prefix) diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index bfe0e66e393..a8695d2330a 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -119,13 +119,13 @@ typedef struct PyC_FlagSet { const char *identifier; } PyC_FlagSet; -PyObject *PyC_FlagSet_AsString(PyC_FlagSet *item); -int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int *r_value); -int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, +PyObject *PyC_FlagSet_AsString(const PyC_FlagSet *item); +int PyC_FlagSet_ValueFromID_int(const PyC_FlagSet *item, const char *identifier, int *r_value); +int PyC_FlagSet_ValueFromID(const PyC_FlagSet *item, const char *identifier, int *r_value, const char *error_prefix); -int PyC_FlagSet_ToBitfield(PyC_FlagSet *items, +int PyC_FlagSet_ToBitfield(const PyC_FlagSet *items, PyObject *value, int *r_value, const char *error_prefix); diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index 0fef59d6352..abfde7b48c8 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -485,7 +485,7 @@ static int pygpu_buffer__sq_ass_item(BPyGPUBuffer *self, int i, PyObject *v) case GPU_DATA_UINT: case GPU_DATA_UINT_24_8: case GPU_DATA_10_11_11_REV: - return PyArg_Parse(v, "b:Expected ints", &self->buf.as_uint[i]) ? 0 : -1; + return PyArg_Parse(v, "I:Expected unsigned ints", &self->buf.as_uint[i]) ? 0 : -1; default: return 0; /* should never happen */ } diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index c7b59ee0d6e..1bdf9766c1b 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -105,12 +105,13 @@ static PyObject *pygpu_shader__tp_new(PyTypeObject *UNUSED(type), PyObject *args const char *geocode; const char *libcode; const char *defines; + const char *name; } params = {0}; static const char *_keywords[] = { - "vertexcode", "fragcode", "geocode", "libcode", "defines", NULL}; + "vertexcode", "fragcode", "geocode", "libcode", "defines", "name", NULL}; - static _PyArg_Parser _parser = {"ss|$sss:GPUShader.__new__", _keywords, 0}; + static _PyArg_Parser _parser = {"ss|$ssss:GPUShader.__new__", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, @@ -118,12 +119,17 @@ static PyObject *pygpu_shader__tp_new(PyTypeObject *UNUSED(type), PyObject *args ¶ms.fragcode, ¶ms.geocode, ¶ms.libcode, - ¶ms.defines)) { + ¶ms.defines, + ¶ms.name)) { return NULL; } - GPUShader *shader = GPU_shader_create_from_python( - params.vertexcode, params.fragcode, params.geocode, params.libcode, params.defines); + GPUShader *shader = GPU_shader_create_from_python(params.vertexcode, + params.fragcode, + params.geocode, + params.libcode, + params.defines, + params.name); if (shader == NULL) { PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details"); @@ -639,6 +645,13 @@ static struct PyMethodDef pygpu_shader__tp_methods[] = { {NULL, NULL, 0, NULL}, }; +PyDoc_STRVAR(pygpu_shader_name_doc, + "The name of the shader object for debugging purposes (read-only).\n\n:type: str"); +static PyObject *pygpu_shader_name(BPyGPUShader *self) +{ + return PyUnicode_FromString(GPU_shader_get_name(self->shader)); +} + PyDoc_STRVAR( pygpu_shader_program_doc, "The name of the program object for use by the OpenGL API (read-only).\n\n:type: int"); @@ -649,6 +662,7 @@ static PyObject *pygpu_shader_program_get(BPyGPUShader *self, void *UNUSED(closu static PyGetSetDef pygpu_shader__tp_getseters[] = { {"program", (getter)pygpu_shader_program_get, (setter)NULL, pygpu_shader_program_doc, NULL}, + {"name", (getter)pygpu_shader_name, (setter)NULL, pygpu_shader_name_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; @@ -662,10 +676,11 @@ static void pygpu_shader__tp_dealloc(BPyGPUShader *self) PyDoc_STRVAR( pygpu_shader__tp_doc, - ".. class:: GPUShader(vertexcode, fragcode, geocode=None, libcode=None, defines=None)\n" + ".. class:: GPUShader(vertexcode, fragcode, geocode=None, libcode=None, defines=None, " + "name='pyGPUShader')\n" "\n" " GPUShader combines multiple GLSL shaders into a program used for drawing.\n" - " It must contain a vertex and fragment shaders, with an optional geometry shader.\n" + " It must contain at least a vertex and fragment shaders.\n" "\n" " The GLSL ``#version`` directive is automatically included at the top of shaders,\n" " and set to 330. Some preprocessor directives are automatically added according to\n" @@ -688,6 +703,8 @@ PyDoc_STRVAR( " :param libcode: Code with functions and presets to be shared between shaders.\n" " :type value: str\n" " :param defines: Preprocessor directives.\n" + " :type value: str\n" + " :param name: Name of shader code, for debugging purposes.\n" " :type value: str\n"); PyTypeObject BPyGPUShader_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUShader", @@ -722,9 +739,10 @@ PyDoc_STRVAR(pygpu_shader_from_builtin_doc, " Shaders that are embedded in the blender internal code.\n" " They all read the uniform ``mat4 ModelViewProjectionMatrix``,\n" " which can be edited by the :mod:`gpu.matrix` module.\n" + "\n" " You can also choose a shader configuration that uses clip_planes by setting the " "``CLIPPED`` value to the config parameter. Note that in this case you also need to " - "manually set the value of ``ModelMatrix``.\n" + "manually set the value of ``mat4 ModelMatrix``.\n" "\n" " For more details, you can check the shader code with the\n" " :func:`gpu.shader.code_from_builtin` function.\n" @@ -733,6 +751,7 @@ PyDoc_STRVAR(pygpu_shader_from_builtin_doc, "\n" PYDOC_BUILTIN_SHADER_LIST " :type shader_name: str\n" " :param config: One of these types of shader configuration:\n" + "\n" " - ``DEFAULT``\n" " - ``CLIPPED``\n" " :type config: str\n" @@ -766,7 +785,7 @@ static PyObject *pygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg PyDoc_STRVAR(pygpu_shader_code_from_builtin_doc, ".. function:: code_from_builtin(pygpu_shader_name)\n" "\n" - " Exposes the internal shader code for query.\n" + " Exposes the internal shader code for consultation.\n" "\n" " :param pygpu_shader_name: One of these builtin shader names:\n" "\n" PYDOC_BUILTIN_SHADER_LIST @@ -828,27 +847,28 @@ PyDoc_STRVAR(pygpu_shader_module__tp_doc, ".. rubric:: Built-in shaders\n" "\n" "All built-in shaders have the ``mat4 ModelViewProjectionMatrix`` uniform.\n" - "The value of it can only be modified using the :class:`gpu.matrix` module.\n" "\n" - "2D_UNIFORM_COLOR\n" + "Its value must be modified using the :class:`gpu.matrix` module.\n" + "\n" + "``2D_UNIFORM_COLOR``\n" " :Attributes: vec3 pos\n" " :Uniforms: vec4 color\n" - "2D_FLAT_COLOR\n" + "``2D_FLAT_COLOR``\n" " :Attributes: vec3 pos, vec4 color\n" " :Uniforms: none\n" - "2D_SMOOTH_COLOR\n" + "``2D_SMOOTH_COLOR``\n" " :Attributes: vec3 pos, vec4 color\n" " :Uniforms: none\n" - "2D_IMAGE\n" + "``2D_IMAGE``\n" " :Attributes: vec3 pos, vec2 texCoord\n" " :Uniforms: sampler2D image\n" - "3D_UNIFORM_COLOR\n" + "``3D_UNIFORM_COLOR``\n" " :Attributes: vec3 pos\n" " :Uniforms: vec4 color\n" - "3D_FLAT_COLOR\n" + "``3D_FLAT_COLOR``\n" " :Attributes: vec3 pos, vec4 color\n" " :Uniforms: none\n" - "3D_SMOOTH_COLOR\n" + "``3D_SMOOTH_COLOR``\n" " :Attributes: vec3 pos, vec4 color\n" " :Uniforms: none\n"); static PyModuleDef pygpu_shader_module_def = { diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c index 757c787882b..26905c87de2 100644 --- a/source/blender/python/gpu/gpu_py_state.c +++ b/source/blender/python/gpu/gpu_py_state.c @@ -126,7 +126,7 @@ static PyObject *pygpu_state_blend_get(PyObject *UNUSED(self)) PyDoc_STRVAR(pygpu_state_clip_distances_set_doc, ".. function:: clip_distances_set(distances_enabled)\n" "\n" - " Sets number of `gl_ClipDistance`s that will be used for clip geometry.\n" + " Sets the number of `gl_ClipDistance` planes used for clip geometry.\n" "\n" " :param distances_enabled: Number of clip distances enabled.\n" " :type distances_enabled: int\n"); diff --git a/source/blender/python/intern/bpy_capi_utils.c b/source/blender/python/intern/bpy_capi_utils.c index b2670161d92..8e9d8c02c03 100644 --- a/source/blender/python/intern/bpy_capi_utils.c +++ b/source/blender/python/intern/bpy_capi_utils.c @@ -38,24 +38,6 @@ #include "../generic/py_capi_utils.h" -char *BPy_enum_as_string(const EnumPropertyItem *item) -{ - DynStr *dynstr = BLI_dynstr_new(); - - /* We can't compare with the first element in the array - * since it may be a category (without an identifier). */ - for (bool is_first = true; item->identifier; item++) { - if (item->identifier[0]) { - BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier); - is_first = false; - } - } - - char *cstring = BLI_dynstr_get_cstring(dynstr); - BLI_dynstr_free(dynstr); - return cstring; -} - short BPy_reports_to_error(ReportList *reports, PyObject *exception, const bool clear) { char *report_str; diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h index d7ee1eedabb..80e92d918ac 100644 --- a/source/blender/python/intern/bpy_capi_utils.h +++ b/source/blender/python/intern/bpy_capi_utils.h @@ -31,8 +31,6 @@ extern "C" { struct EnumPropertyItem; struct ReportList; -char *BPy_enum_as_string(const struct EnumPropertyItem *item); - /* error reporting */ short BPy_reports_to_error(struct ReportList *reports, PyObject *exception, const bool clear); void BPy_reports_write_stdout(const struct ReportList *reports, const char *header); diff --git a/source/blender/python/intern/bpy_gizmo_wrap.c b/source/blender/python/intern/bpy_gizmo_wrap.c index a05ec6b7000..8d9b9e86589 100644 --- a/source/blender/python/intern/bpy_gizmo_wrap.c +++ b/source/blender/python/intern/bpy_gizmo_wrap.c @@ -41,6 +41,8 @@ #include "bpy_intern_string.h" #include "bpy_rna.h" +#include "../generic/py_capi_rna.h" + /* we may want to add, but not now */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 1a308414bc3..7a93a076621 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -706,7 +706,7 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * ptr = &(((BPy_StructRNA *)item)->ptr); // result->ptr = ((BPy_StructRNA *)item)->ptr; - CTX_data_pointer_set(result, ptr->owner_id, ptr->type, ptr->data); + CTX_data_pointer_set_ptr(result, ptr); CTX_data_type_set(result, CTX_DATA_TYPE_POINTER); done = true; } @@ -732,7 +732,7 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * BLI_addtail(&result->list, link); #endif ptr = &(((BPy_StructRNA *)list_item)->ptr); - CTX_data_list_add(result, ptr->owner_id, ptr->type, ptr->data); + CTX_data_list_add_ptr(result, ptr); } else { CLOG_INFO(BPY_LOG_CONTEXT, @@ -753,7 +753,7 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not a valid type", member); } else { - CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not found\n", member); + CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not found", member); } } else { diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c index 04220cf3105..75a5f6f72ae 100644 --- a/source/blender/python/intern/bpy_msgbus.c +++ b/source/blender/python/intern/bpy_msgbus.c @@ -21,6 +21,7 @@ #include <Python.h> +#include "../generic/py_capi_rna.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" #include "../mathutils/mathutils.h" @@ -260,7 +261,7 @@ static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args } if (py_options && - (pyrna_set_to_enum_bitfield(py_options_enum, py_options, &options, error_prefix)) == -1) { + (pyrna_enum_bitfield_from_set(py_options_enum, py_options, &options, error_prefix)) == -1) { return NULL; } diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index d5afb2334f0..5ae123f3254 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -32,6 +32,7 @@ #include "BLI_listbase.h" #include "BLI_utildefines.h" +#include "../generic/py_capi_rna.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" #include "BPY_extern.h" @@ -107,7 +108,7 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) if (context_str) { if (RNA_enum_value_from_id(rna_enum_operator_context_items, context_str, &context) == 0) { - char *enum_str = BPy_enum_as_string(rna_enum_operator_context_items); + char *enum_str = pyrna_enum_repr(rna_enum_operator_context_items); PyErr_Format(PyExc_TypeError, "Calling operator \"bpy.ops.%s.poll\" error, " "expected a string enum in (%s)", @@ -209,7 +210,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) if (context_str) { if (RNA_enum_value_from_id(rna_enum_operator_context_items, context_str, &context) == 0) { - char *enum_str = BPy_enum_as_string(rna_enum_operator_context_items); + char *enum_str = pyrna_enum_repr(rna_enum_operator_context_items); PyErr_Format(PyExc_TypeError, "Calling operator \"bpy.ops.%s\" error, " "expected a string enum in (%s)", @@ -346,7 +347,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) BPY_modules_update(); /* return operator_ret as a bpy enum */ - return pyrna_enum_bitfield_to_py(rna_enum_operator_return_items, operator_ret); + return pyrna_enum_bitfield_as_set(rna_enum_operator_return_items, operator_ret); } static PyObject *pyop_as_string(PyObject *UNUSED(self), PyObject *args) @@ -459,7 +460,7 @@ static PyObject *pyop_get_bl_options(PyObject *UNUSED(self), PyObject *value) if ((ot = ot_lookup_from_py_string(value, "get_bl_options")) == NULL) { return NULL; } - return pyrna_enum_bitfield_to_py(rna_enum_operator_type_flag_items, ot->flag); + return pyrna_enum_bitfield_as_set(rna_enum_operator_type_flag_items, ot->flag); } static struct PyMethodDef bpy_ops_methods[] = { diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index f89a4809587..ac624d365fa 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -46,6 +46,7 @@ #include "DNA_ID.h" /* MAX_IDPROP_NAME */ +#include "../generic/py_capi_rna.h" #include "../generic/py_capi_utils.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index ac1fdeb2bad..35acb56e66a 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -74,6 +74,7 @@ #include "../generic/idprop_py_api.h" /* For IDprop lookups. */ #include "../generic/idprop_py_ui_api.h" +#include "../generic/py_capi_rna.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" @@ -782,66 +783,6 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) return ret; } -/** - * Same as #RNA_enum_value_from_id, but raises an exception. - */ -int pyrna_enum_value_from_id(const EnumPropertyItem *item, - const char *identifier, - int *r_value, - const char *error_prefix) -{ - if (RNA_enum_value_from_id(item, identifier, r_value) == 0) { - const char *enum_str = BPy_enum_as_string(item); - PyErr_Format( - PyExc_ValueError, "%s: '%.200s' not found in (%s)", error_prefix, identifier, enum_str); - MEM_freeN((void *)enum_str); - return -1; - } - - return 0; -} - -/** - * Use with #PyArg_ParseTuple's `O&` formatting. - */ -int pyrna_enum_value_parse_string(PyObject *o, void *p) -{ - const char *identifier = PyUnicode_AsUTF8(o); - if (identifier == NULL) { - PyErr_Format(PyExc_TypeError, "expected a string enum, not %.200s", Py_TYPE(o)->tp_name); - return 0; - } - struct BPy_EnumProperty_Parse *parse_data = p; - if (pyrna_enum_value_from_id( - parse_data->items, identifier, &parse_data->value, "enum identifier") == -1) { - return 0; - } - - parse_data->value_orig = o; - parse_data->is_set = true; - return 1; -} - -/** - * Use with #PyArg_ParseTuple's `O&` formatting. - */ -int pyrna_enum_bitfield_parse_set(PyObject *o, void *p) -{ - if (!PySet_Check(o)) { - PyErr_Format(PyExc_TypeError, "expected a set, not %.200s", Py_TYPE(o)->tp_name); - return 0; - } - - struct BPy_EnumProperty_Parse *parse_data = p; - if (pyrna_set_to_enum_bitfield( - parse_data->items, o, &parse_data->value, "enum identifier set") == -1) { - return 0; - } - parse_data->value_orig = o; - parse_data->is_set = true; - return 1; -} - /* NOTE(campbell): Regarding comparison `__cmp__`: * checking the 'ptr->data' matches works in almost all cases, * however there are a few RNA properties that are fake sub-structs and @@ -1272,7 +1213,7 @@ static const char *pyrna_enum_as_string(PointerRNA *ptr, PropertyRNA *prop) RNA_property_enum_items(BPY_context_get(), ptr, prop, &item, NULL, &free); if (item) { - result = BPy_enum_as_string(item); + result = pyrna_enum_repr(item); } else { result = ""; @@ -1312,115 +1253,6 @@ static int pyrna_string_to_enum( return 0; } -/** - * Takes a set of strings and map it to and array of booleans. - * - * Useful when the values aren't flags. - * - * \param type_convert_sign: Maps signed to unsigned range, - * needed when we want to use the full range of a signed short/char. - */ -BLI_bitmap *pyrna_set_to_enum_bitmap(const EnumPropertyItem *items, - PyObject *value, - int type_size, - bool type_convert_sign, - int bitmap_size, - const char *error_prefix) -{ - /* Set looping. */ - Py_ssize_t pos = 0; - Py_ssize_t hash = 0; - PyObject *key; - - BLI_bitmap *bitmap = BLI_BITMAP_NEW(bitmap_size, __func__); - - while (_PySet_NextEntry(value, &pos, &key, &hash)) { - const char *param = PyUnicode_AsUTF8(key); - if (param == NULL) { - PyErr_Format(PyExc_TypeError, - "%.200s expected a string, not %.200s", - error_prefix, - Py_TYPE(key)->tp_name); - goto error; - } - - int ret; - if (pyrna_enum_value_from_id(items, param, &ret, error_prefix) == -1) { - goto error; - } - - int index = ret; - - if (type_convert_sign) { - if (type_size == 2) { - union { - signed short as_signed; - ushort as_unsigned; - } ret_convert; - ret_convert.as_signed = (signed short)ret; - index = (int)ret_convert.as_unsigned; - } - else if (type_size == 1) { - union { - signed char as_signed; - uchar as_unsigned; - } ret_convert; - ret_convert.as_signed = (signed char)ret; - index = (int)ret_convert.as_unsigned; - } - else { - BLI_assert_unreachable(); - } - } - BLI_assert(index < bitmap_size); - BLI_BITMAP_ENABLE(bitmap, index); - } - - return bitmap; - -error: - MEM_freeN(bitmap); - return NULL; -} - -/* 'value' _must_ be a set type, error check before calling. */ -int pyrna_set_to_enum_bitfield(const EnumPropertyItem *items, - PyObject *value, - int *r_value, - const char *error_prefix) -{ - /* Set of enum items, concatenate all values with OR. */ - int ret, flag = 0; - - /* Set looping. */ - Py_ssize_t pos = 0; - Py_ssize_t hash = 0; - PyObject *key; - - *r_value = 0; - - while (_PySet_NextEntry(value, &pos, &key, &hash)) { - const char *param = PyUnicode_AsUTF8(key); - - if (param == NULL) { - PyErr_Format(PyExc_TypeError, - "%.200s expected a string, not %.200s", - error_prefix, - Py_TYPE(key)->tp_name); - return -1; - } - - if (pyrna_enum_value_from_id(items, param, &ret, error_prefix) == -1) { - return -1; - } - - flag |= ret; - } - - *r_value = flag; - return 0; -} - static int pyrna_prop_to_enum_bitfield( PointerRNA *ptr, PropertyRNA *prop, PyObject *value, int *r_value, const char *error_prefix) { @@ -1443,7 +1275,7 @@ static int pyrna_prop_to_enum_bitfield( RNA_property_enum_items(BPY_context_get(), ptr, prop, &item, NULL, &free); if (item) { - ret = pyrna_set_to_enum_bitfield(item, value, r_value, error_prefix); + ret = pyrna_enum_bitfield_from_set(item, value, r_value, error_prefix); } else { if (PySet_GET_SIZE(value)) { @@ -1465,24 +1297,6 @@ static int pyrna_prop_to_enum_bitfield( return ret; } -PyObject *pyrna_enum_bitfield_to_py(const EnumPropertyItem *items, int value) -{ - PyObject *ret = PySet_New(NULL); - const char *identifier[RNA_ENUM_BITFLAG_SIZE + 1]; - - if (RNA_enum_bitflag_identifiers(items, value, identifier)) { - PyObject *item; - int index; - for (index = 0; identifier[index]; index++) { - item = PyUnicode_FromString(identifier[index]); - PySet_Add(ret, item); - Py_DECREF(item); - } - } - - return ret; -} - static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val) { PyObject *item, *ret = NULL; @@ -3151,7 +2965,6 @@ static int prop_subscript_ass_array_slice(PointerRNA *ptr, { const int length_flat = RNA_property_array_length(ptr, prop); PyObject *value; - PyObject **value_items; void *values_alloc = NULL; int ret = 0; @@ -3187,7 +3000,7 @@ static int prop_subscript_ass_array_slice(PointerRNA *ptr, } } - value_items = PySequence_Fast_ITEMS(value); + PyObject **value_items = PySequence_Fast_ITEMS(value); switch (RNA_property_type(prop)) { case PROP_FLOAT: { float values_stack[PYRNA_STACK_ARRAY]; @@ -3289,11 +3102,9 @@ static int prop_subscript_ass_array_int(BPy_PropertyArrayRNA *self, Py_ssize_t keynum, PyObject *value) { - int len; - PYRNA_PROP_CHECK_INT((BPy_PropertyRNA *)self); - len = pyrna_prop_array_length(self); + int len = pyrna_prop_array_length(self); if (keynum < 0) { keynum += len; @@ -3438,7 +3249,6 @@ static int pyrna_prop_collection_contains(BPy_PropertyRNA *self, PyObject *key) static int pyrna_struct_contains(BPy_StructRNA *self, PyObject *value) { - IDProperty *group; const char *name = PyUnicode_AsUTF8(value); PYRNA_STRUCT_CHECK_INT(self); @@ -3453,7 +3263,7 @@ static int pyrna_struct_contains(BPy_StructRNA *self, PyObject *value) return -1; } - group = RNA_struct_idprops(&self->ptr, 0); + IDProperty *group = RNA_struct_idprops(&self->ptr, 0); if (!group) { return 0; @@ -3508,7 +3318,6 @@ static PySequenceMethods pyrna_struct_as_sequence = { static PyObject *pyrna_struct_subscript(BPy_StructRNA *self, PyObject *key) { /* Mostly copied from BPy_IDGroup_Map_GetItem. */ - IDProperty *group, *idprop; const char *name = PyUnicode_AsUTF8(key); PYRNA_STRUCT_CHECK_OBJ(self); @@ -3524,14 +3333,14 @@ static PyObject *pyrna_struct_subscript(BPy_StructRNA *self, PyObject *key) return NULL; } - group = RNA_struct_idprops(&self->ptr, 0); + IDProperty *group = RNA_struct_idprops(&self->ptr, 0); if (group == NULL) { PyErr_Format(PyExc_KeyError, "bpy_struct[key]: key \"%s\" not found", name); return NULL; } - idprop = IDP_GetPropertyFromGroup(group, name); + IDProperty *idprop = IDP_GetPropertyFromGroup(group, name); if (idprop == NULL) { PyErr_Format(PyExc_KeyError, "bpy_struct[key]: key \"%s\" not found", name); @@ -3543,11 +3352,9 @@ static PyObject *pyrna_struct_subscript(BPy_StructRNA *self, PyObject *key) static int pyrna_struct_ass_subscript(BPy_StructRNA *self, PyObject *key, PyObject *value) { - IDProperty *group; - PYRNA_STRUCT_CHECK_INT(self); - group = RNA_struct_idprops(&self->ptr, 1); + IDProperty *group = RNA_struct_idprops(&self->ptr, 1); #ifdef USE_PEDANTIC_WRITE if (rna_disallow_writes && rna_id_write_error(&self->ptr, key)) { @@ -3594,15 +3401,13 @@ PyDoc_STRVAR(pyrna_struct_keys_doc, "\n" BPY_DOC_ID_PROP_TYPE_NOTE); static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self) { - IDProperty *group; - if (RNA_struct_idprops_check(self->ptr.type) == 0) { PyErr_SetString(PyExc_TypeError, "bpy_struct.keys(): this type doesn't support IDProperties"); return NULL; } /* `group` may be NULL. */ - group = RNA_struct_idprops(&self->ptr, 0); + IDProperty *group = RNA_struct_idprops(&self->ptr, 0); return BPy_Wrap_GetKeys_View_WithID(self->ptr.owner_id, group); } @@ -3617,15 +3422,13 @@ PyDoc_STRVAR(pyrna_struct_items_doc, "\n" BPY_DOC_ID_PROP_TYPE_NOTE); static PyObject *pyrna_struct_items(BPy_PropertyRNA *self) { - IDProperty *group; - if (RNA_struct_idprops_check(self->ptr.type) == 0) { PyErr_SetString(PyExc_TypeError, "bpy_struct.items(): this type doesn't support IDProperties"); return NULL; } /* `group` may be NULL. */ - group = RNA_struct_idprops(&self->ptr, 0); + IDProperty *group = RNA_struct_idprops(&self->ptr, 0); return BPy_Wrap_GetItems_View_WithID(self->ptr.owner_id, group); } @@ -3640,8 +3443,6 @@ PyDoc_STRVAR(pyrna_struct_values_doc, "\n" BPY_DOC_ID_PROP_TYPE_NOTE); static PyObject *pyrna_struct_values(BPy_PropertyRNA *self) { - IDProperty *group; - if (RNA_struct_idprops_check(self->ptr.type) == 0) { PyErr_SetString(PyExc_TypeError, "bpy_struct.values(): this type doesn't support IDProperties"); @@ -3649,7 +3450,7 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self) } /* `group` may be NULL. */ - group = RNA_struct_idprops(&self->ptr, 0); + IDProperty *group = RNA_struct_idprops(&self->ptr, 0); return BPy_Wrap_GetValues_View_WithID(self->ptr.owner_id, group); } @@ -3862,7 +3663,7 @@ static PyObject *pyrna_struct_path_resolve(BPy_StructRNA *self, PyObject *args) return NULL; } - if (RNA_path_resolve_full(&self->ptr, path, &r_ptr, &r_prop, &index)) { + if (RNA_path_resolve_full_maybe_null(&self->ptr, path, &r_ptr, &r_prop, &index)) { if (r_prop) { if (index != -1) { if (index >= RNA_property_array_length(&r_ptr, r_prop) || index < 0) { diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index fd468bed470..f6a05a86311 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -202,42 +202,6 @@ int pyrna_pydict_to_props(PointerRNA *ptr, const char *error_prefix); PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop); -uint *pyrna_set_to_enum_bitmap(const struct EnumPropertyItem *items, - PyObject *value, - int type_size, - bool type_convert_sign, - int bitmap_size, - const char *error_prefix); -PyObject *pyrna_enum_bitfield_to_py(const struct EnumPropertyItem *items, int value); -int pyrna_set_to_enum_bitfield(const struct EnumPropertyItem *items, - PyObject *value, - int *r_value, - const char *error_prefix); - -/** - * Data for #pyrna_enum_value_parse_string & #pyrna_enum_bitfield_parse_set parsing utilities. - * Use with #PyArg_ParseTuple's `O&` formatting. - */ -struct BPy_EnumProperty_Parse { - const EnumPropertyItem *items; - /** - * Set when the value was successfully parsed. - * Useful if the input ever needs to be included in an error message. - * (if the value is not supported under certain conditions). - */ - PyObject *value_orig; - - int value; - bool is_set; -}; -int pyrna_enum_value_parse_string(PyObject *o, void *p); -int pyrna_enum_bitfield_parse_set(PyObject *o, void *p); - -int pyrna_enum_value_from_id(const EnumPropertyItem *item, - const char *identifier, - int *value, - const char *error_prefix); - int pyrna_deferred_register_class(struct StructRNA *srna, PyTypeObject *py_class); void pyrna_struct_type_extend_capi(struct StructRNA *srna, diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index a716f4ab9e5..2096734ef96 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -54,6 +54,7 @@ #include "bpy_rna.h" #include "bpy_rna_anim.h" +#include "../generic/py_capi_rna.h" #include "../generic/python_utildefines.h" #include "DEG_depsgraph_build.h" @@ -265,7 +266,7 @@ static int pyrna_struct_keyframe_parse(PointerRNA *ptr, /* flag may be null (no option currently for remove keyframes e.g.). */ if (r_options) { if (pyoptions && - (pyrna_set_to_enum_bitfield( + (pyrna_enum_bitfield_from_set( rna_enum_keying_flag_items_api, pyoptions, r_options, error_prefix) == -1)) { return -1; } @@ -334,6 +335,11 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb return NULL; } + ReportList reports; + bool result = false; + + BKE_reports_init(&reports, RPT_STORE); + /* This assumes that keyframes are only added on original data & using the active depsgraph. If * it turns out to be necessary for some reason to insert keyframes on evaluated objects, we can * revisit this and add an explicit `depsgraph` keyword argument to the function call. @@ -351,15 +357,11 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb * strips themselves. These are stored separately or else the properties will * not have any effect. */ - ReportList reports; - bool result = false; PointerRNA ptr = self->ptr; PropertyRNA *prop = NULL; const char *prop_name; - BKE_reports_init(&reports, RPT_STORE); - /* Retrieve the property identifier from the full path, since we can't get it any other way */ prop_name = strrchr(path_full, '.'); if ((prop_name >= path_full) && (prop_name + 1 < path_full + strlen(path_full))) { @@ -375,33 +377,24 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb else { BKE_reportf(&reports, RPT_ERROR, "Could not resolve path (%s)", path_full); } - MEM_freeN((void *)path_full); - - if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1) { - return NULL; - } - - return PyBool_FromLong(result); } + else { + ID *id = self->ptr.owner_id; - ID *id = self->ptr.owner_id; - ReportList reports; - bool result; - - BKE_reports_init(&reports, RPT_STORE); + BLI_assert(BKE_id_is_in_global_main(id)); + result = (insert_keyframe(G_MAIN, + &reports, + id, + NULL, + group_name, + path_full, + index, + &anim_eval_context, + keytype, + NULL, + options) != 0); + } - BLI_assert(BKE_id_is_in_global_main(id)); - result = (insert_keyframe(G_MAIN, - &reports, - id, - NULL, - group_name, - path_full, - index, - &anim_eval_context, - keytype, - NULL, - options) != 0); MEM_freeN((void *)path_full); if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1) { @@ -452,20 +445,22 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb NULL) == -1) { return NULL; } + + ReportList reports; + bool result = false; + + BKE_reports_init(&reports, RPT_STORE); + if (self->ptr.type == &RNA_NlaStrip) { /* Handle special properties for NLA Strips, whose F-Curves are stored on the * strips themselves. These are stored separately or else the properties will * not have any effect. */ - ReportList reports; - bool result = false; PointerRNA ptr = self->ptr; PropertyRNA *prop = NULL; const char *prop_name; - BKE_reports_init(&reports, RPT_STORE); - /* Retrieve the property identifier from the full path, since we can't get it any other way */ prop_name = strrchr(path_full, '.'); if ((prop_name >= path_full) && (prop_name + 1 < path_full + strlen(path_full))) { @@ -509,22 +504,12 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb else { BKE_reportf(&reports, RPT_ERROR, "Could not resolve path (%s)", path_full); } - MEM_freeN((void *)path_full); - - if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1) { - return NULL; - } - - return PyBool_FromLong(result); + } + else { + result = (delete_keyframe( + G.main, &reports, self->ptr.owner_id, NULL, path_full, index, cfra) != 0); } - bool result; - ReportList reports; - - BKE_reports_init(&reports, RPT_STORE); - - result = (delete_keyframe(G.main, &reports, self->ptr.owner_id, NULL, path_full, index, cfra) != - 0); MEM_freeN((void *)path_full); if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1) { diff --git a/source/blender/python/intern/bpy_rna_callback.c b/source/blender/python/intern/bpy_rna_callback.c index adc0f425b52..c19d9ba47b1 100644 --- a/source/blender/python/intern/bpy_rna_callback.c +++ b/source/blender/python/intern/bpy_rna_callback.c @@ -23,6 +23,7 @@ #include <Python.h> +#include "../generic/py_capi_rna.h" #include "../generic/python_utildefines.h" #include "DNA_space_types.h" diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index 7bdb0e30410..2e684faf61b 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -45,6 +45,7 @@ #include "bpy_capi_utils.h" #include "bpy_rna_id_collection.h" +#include "../generic/py_capi_rna.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" @@ -178,7 +179,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject * } if (key_types) { - key_types_bitmap = pyrna_set_to_enum_bitmap( + key_types_bitmap = pyrna_enum_bitmap_from_set( rna_enum_id_type_items, key_types, sizeof(short), true, USHRT_MAX, "key types"); if (key_types_bitmap == NULL) { goto error; @@ -186,7 +187,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject * } if (val_types) { - val_types_bitmap = pyrna_set_to_enum_bitmap( + val_types_bitmap = pyrna_enum_bitmap_from_set( rna_enum_id_type_items, val_types, sizeof(short), true, USHRT_MAX, "value types"); if (val_types_bitmap == NULL) { goto error; diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index be7dae6871b..0043fc36162 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -599,6 +599,15 @@ uchar Mathutils_RegisterCallback(Mathutils_Callback *cb) return i; } +int _BaseMathObject_CheckCallback(BaseMathObject *self) +{ + Mathutils_Callback *cb = mathutils_callbacks[self->cb_type]; + if (LIKELY(cb->check(self) != -1)) { + return 0; + } + return -1; +} + /* use macros to check for NULL */ int _BaseMathObject_ReadCallback(BaseMathObject *self) { @@ -687,6 +696,13 @@ PyObject *BaseMathObject_is_frozen_get(BaseMathObject *self, void *UNUSED(closur return PyBool_FromLong((self->flag & BASE_MATH_FLAG_IS_FROZEN) != 0); } +char BaseMathObject_is_valid_doc[] = + "True when the owner of this data is valid.\n\n:type: boolean"; +PyObject *BaseMathObject_is_valid_get(BaseMathObject *self, void *UNUSED(closure)) +{ + return PyBool_FromLong(BaseMath_CheckCallback(self) == 0); +} + char BaseMathObject_freeze_doc[] = ".. function:: freeze()\n" "\n" diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h index 80be841785a..4aa26dcc5be 100644 --- a/source/blender/python/mathutils/mathutils.h +++ b/source/blender/python/mathutils/mathutils.h @@ -28,6 +28,7 @@ struct DynStr; extern char BaseMathObject_is_wrapped_doc[]; extern char BaseMathObject_is_frozen_doc[]; +extern char BaseMathObject_is_valid_doc[]; extern char BaseMathObject_owner_doc[]; #define BASE_MATH_NEW(struct_name, root_type, base_type) \ @@ -81,6 +82,7 @@ typedef struct { PyObject *BaseMathObject_owner_get(BaseMathObject *self, void *); PyObject *BaseMathObject_is_wrapped_get(BaseMathObject *self, void *); PyObject *BaseMathObject_is_frozen_get(BaseMathObject *self, void *); +PyObject *BaseMathObject_is_valid_get(BaseMathObject *self, void *); extern char BaseMathObject_freeze_doc[]; PyObject *BaseMathObject_freeze(BaseMathObject *self); @@ -117,6 +119,7 @@ struct Mathutils_Callback { unsigned char Mathutils_RegisterCallback(Mathutils_Callback *cb); +int _BaseMathObject_CheckCallback(BaseMathObject *self); int _BaseMathObject_ReadCallback(BaseMathObject *self); int _BaseMathObject_WriteCallback(BaseMathObject *self); int _BaseMathObject_ReadIndexCallback(BaseMathObject *self, int index); @@ -126,6 +129,8 @@ void _BaseMathObject_RaiseFrozenExc(const BaseMathObject *self); void _BaseMathObject_RaiseNotFrozenExc(const BaseMathObject *self); /* since this is called so often avoid where possible */ +#define BaseMath_CheckCallback(_self) \ + (((_self)->cb_user ? _BaseMathObject_CheckCallback((BaseMathObject *)_self) : 0)) #define BaseMath_ReadCallback(_self) \ (((_self)->cb_user ? _BaseMathObject_ReadCallback((BaseMathObject *)_self) : 0)) #define BaseMath_WriteCallback(_self) \ diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 7546f2ef730..13d712bddb0 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -866,6 +866,11 @@ static PyGetSetDef Color_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c index 595d03b533b..1033d186fca 100644 --- a/source/blender/python/mathutils/mathutils_Euler.c +++ b/source/blender/python/mathutils/mathutils_Euler.c @@ -699,6 +699,11 @@ static PyGetSetDef Euler_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 36b8b0b6d35..ce04a143aae 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -3148,6 +3148,11 @@ static PyGetSetDef Matrix_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index 77a30dcd447..525b2da7d06 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -1505,6 +1505,11 @@ static PyGetSetDef Quaternion_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index efcaa9b6a51..23758c5603e 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -2551,6 +2551,11 @@ static PyGetSetDef Vector_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, /* Auto-generated swizzle attributes, see Python script above. */ diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 481a6662cc0..5728b784714 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -365,6 +365,17 @@ RenderResult *RE_engine_begin_result( return result; } +static void re_ensure_passes_allocated_thread_safe(Render *re) +{ + if (!re->result->passes_allocated) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if (!re->result->passes_allocated) { + render_result_passes_allocated_ensure(re->result); + } + BLI_rw_mutex_unlock(&re->resultmutex); + } +} + void RE_engine_update_result(RenderEngine *engine, RenderResult *result) { if (engine->bake.pixels) { @@ -375,6 +386,7 @@ void RE_engine_update_result(RenderEngine *engine, RenderResult *result) Render *re = engine->re; if (result) { + re_ensure_passes_allocated_thread_safe(re); render_result_merge(re->result, result); result->renlay = result->layers.first; /* weak, draws first layer always */ re->display_update(re->duh, result, NULL); @@ -412,13 +424,7 @@ void RE_engine_end_result( return; } - if (!re->result->passes_allocated) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - if (!re->result->passes_allocated) { - render_result_passes_allocated_ensure(re->result); - } - BLI_rw_mutex_unlock(&re->resultmutex); - } + re_ensure_passes_allocated_thread_safe(re); /* merge. on break, don't merge in result for preview renders, looks nicer */ if (!highlight) { diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index 0452be68822..930c7580f32 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -978,7 +978,7 @@ static void apply_tangmat_callback(DerivedMesh *lores_dm, #if 0 /* **************** Ambient Occlusion Baker **************** */ -// must be a power of two +/* Must be a power of two. */ # define MAX_NUMBER_OF_AO_RAYS 1024 static unsigned short ao_random_table_1[MAX_NUMBER_OF_AO_RAYS]; diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index bf42adbab87..5418f4035b1 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1041,6 +1041,7 @@ static void render_result_uncrop(Render *re) render_result_disprect_to_full_resolution(re); rres = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + render_result_passes_allocated_ensure(rres); rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data); render_result_clone_passes(re, rres, NULL); @@ -1870,6 +1871,21 @@ static void render_pipeline_free(Render *re) } /* Destroy the opengl context in the correct thread. */ RE_gl_context_destroy(re); + + /* In the case the engine did not mark tiles as finished (un-highlight, which could happen in the + * case of cancelled render) ensure the storage is empty. */ + if (re->highlighted_tiles != NULL) { + BLI_mutex_lock(&re->highlighted_tiles_mutex); + + /* Rendering is supposed to be finished here, so no new tiles are expected to be written. + * Only make it so possible read-only access to the highlighted tiles is thread-safe. */ + BLI_assert(re->highlighted_tiles); + + BLI_gset_free(re->highlighted_tiles, MEM_freeN); + re->highlighted_tiles = NULL; + + BLI_mutex_unlock(&re->highlighted_tiles_mutex); + } } /* general Blender frame render call */ diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index c29ab342ed7..6cb6aabe885 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -250,6 +250,9 @@ RenderPass *render_layer_add_pass(RenderResult *rr, BLI_addtail(&rl->passes, rpass); + /* The result contains non-allocated pass now, so tag it as such. */ + rr->passes_allocated = false; + return rpass; } diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index 7eab9f84b50..4f7d603fd6a 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -41,7 +41,7 @@ struct Sequence; * The `__LINE__` is defined at the invocation of the `SEQ_ITERATOR_FOREACH` and is not changed * afterwards. This makes it safe to expand it several times in the `SEQ_ITERATOR_FOREACH`. * - * This allows to have nested foreach loops. + * This allows to have nested `foreach` loops. * * NOTE: Putting nested loop to a wrapper macro is not supported. */ #define _SEQ_ITERATOR_NAME_JOIN(x, y) x##_##y diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 15fe0be3571..d7800d208a4 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -65,7 +65,7 @@ short SEQ_tool_settings_snap_mode_get(struct Scene *scene); int SEQ_tool_settings_snap_distance_get(struct Scene *scene); eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(struct Scene *scene); struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings); -struct Editing *SEQ_editing_get(struct Scene *scene, bool alloc); +struct Editing *SEQ_editing_get(const struct Scene *scene); struct Editing *SEQ_editing_ensure(struct Scene *scene); void SEQ_editing_free(struct Scene *scene, const bool do_id_user); struct ListBase *SEQ_active_seqbase_get(const struct Editing *ed); diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 37bac523645..4448db013fe 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -3186,10 +3186,12 @@ float seq_speed_effect_target_frame_get(Scene *scene, float target_frame = 0.0f; switch (s->speed_control_type) { case SEQ_SPEED_STRETCH: { - const float target_content_length = seq_effect_speed_get_strip_content_length(source); - const float target_strip_length = source->enddisp - source->startdisp; - const float ratio = target_content_length / target_strip_length; - target_frame = frame_index * ratio; + /* Only right handle controls effect speed! */ + const float target_content_length = seq_effect_speed_get_strip_content_length(source) - + source->startofs; + const float speed_effetct_length = seq_speed->enddisp - seq_speed->startdisp; + const float ratio = frame_index / speed_effetct_length; + target_frame = target_content_length * ratio; break; } case SEQ_SPEED_MULTIPLY: { diff --git a/source/blender/sequencer/intern/prefetch.c b/source/blender/sequencer/intern/prefetch.c index dd2d828415c..3e0b4738db1 100644 --- a/source/blender/sequencer/intern/prefetch.c +++ b/source/blender/sequencer/intern/prefetch.c @@ -491,7 +491,7 @@ static void *seq_prefetch_frames(void *job) */ pfjob->scene_eval->ed->prefetch_job = pfjob; - ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(pfjob->scene, false)); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(pfjob->scene)); if (seq_prefetch_must_skip_frame(pfjob, seqbase)) { pfjob->num_frames_prefetched++; continue; diff --git a/source/blender/sequencer/intern/proxy_job.c b/source/blender/sequencer/intern/proxy_job.c index aea66da4cc4..afdac04d998 100644 --- a/source/blender/sequencer/intern/proxy_job.c +++ b/source/blender/sequencer/intern/proxy_job.c @@ -79,7 +79,7 @@ static void proxy_startjob(void *pjv, short *stop, short *do_update, float *prog static void proxy_endjob(void *pjv) { ProxyJob *pj = pjv; - Editing *ed = SEQ_editing_get(pj->scene, false); + Editing *ed = SEQ_editing_get(pj->scene); LinkData *link; for (link = pj->queue.first; link; link = link->next) { diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 0c07a25e2e2..6c4502a3608 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1921,7 +1921,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame, int chanshown) { Scene *scene = context->scene; - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); ListBase *seqbasep; if (ed == NULL) { diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index a62b76cd832..bf5942090c9 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -240,11 +240,8 @@ void seq_free_sequence_recurse(Scene *scene, seq_sequence_free_ex(scene, seq, false, do_id_user, do_clean_animdata); } -Editing *SEQ_editing_get(Scene *scene, bool alloc) +Editing *SEQ_editing_get(const Scene *scene) { - if (alloc) { - SEQ_editing_ensure(scene); - } return scene->ed; } diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 17ff1c90be8..cfac243e68f 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -227,7 +227,7 @@ bool SEQ_edit_move_strip_to_meta(Scene *scene, const char **error_str) { /* Find the appropriate seqbase */ - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, src_seq); if (dst_seqm->type != SEQ_TYPE_META) { @@ -267,6 +267,7 @@ bool SEQ_edit_move_strip_to_meta(Scene *scene, SEQ_relations_invalidate_cache_preprocessed(scene, seq); /* Update meta. */ + SEQ_time_update_meta_strip_range(scene, dst_seqm); SEQ_time_update_sequence(scene, dst_seqm); if (SEQ_transform_test_overlap(&dst_seqm->seqbase, seq)) { SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, seq, scene); diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c index b840b19f244..46fdd2c3d14 100644 --- a/source/blender/sequencer/intern/strip_relations.c +++ b/source/blender/sequencer/intern/strip_relations.c @@ -134,7 +134,7 @@ static bool seq_relations_find_and_invalidate_metas(Scene *scene, ListBase *seqbase; if (meta_seq == NULL) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); seqbase = &ed->seqbase; } else { @@ -342,7 +342,7 @@ void SEQ_relations_update_changed_seq_and_deps(Scene *scene, int len_change, int ibuf_change) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; if (ed == NULL) { @@ -370,7 +370,7 @@ static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame) /* Unused */ void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return; } @@ -401,7 +401,7 @@ static Sequence *sequencer_check_scene_recursion(Scene *scene, ListBase *seqbase bool SEQ_relations_check_scene_recursion(Scene *scene, ReportList *reports) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return false; } diff --git a/source/blender/sequencer/intern/strip_select.c b/source/blender/sequencer/intern/strip_select.c index 7cdd756e2d9..8927e092864 100644 --- a/source/blender/sequencer/intern/strip_select.c +++ b/source/blender/sequencer/intern/strip_select.c @@ -34,7 +34,7 @@ Sequence *SEQ_select_active_get(Scene *scene) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return NULL; @@ -45,7 +45,7 @@ Sequence *SEQ_select_active_get(Scene *scene) void SEQ_select_active_set(Scene *scene, Sequence *seq) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); if (ed == NULL) { return; @@ -56,7 +56,7 @@ void SEQ_select_active_set(Scene *scene, Sequence *seq) int SEQ_select_active_get_pair(Scene *scene, Sequence **r_seq_act, Sequence **r_seq_other) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); *r_seq_act = SEQ_select_active_get(scene); diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index b73ac631693..fd6c0805c23 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -249,7 +249,7 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) seq_time_update_meta_strip(scene, seq); } - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); MetaStack *ms = SEQ_meta_stack_active_get(ed); if (ms != NULL) { SEQ_time_update_meta_strip_range(scene, ms->parseq); @@ -266,7 +266,7 @@ int SEQ_time_find_next_prev_edit(Scene *scene, const bool do_center, const bool do_unselected) { - Editing *ed = SEQ_editing_get(scene, false); + Editing *ed = SEQ_editing_get(scene); Sequence *seq; int dist, best_dist, best_frame = timeline_frame; diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index e4eecaf552c..1d3e7e4a223 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -257,7 +257,7 @@ ListBase *SEQ_get_seqbase_from_sequence(Sequence *seq, int *r_offset) } case SEQ_TYPE_SCENE: { if (seq->flag & SEQ_SCENE_STRIPS && seq->scene) { - Editing *ed = SEQ_editing_get(seq->scene, false); + Editing *ed = SEQ_editing_get(seq->scene); if (ed) { seqbase = &ed->seqbase; *r_offset = seq->scene->r.sfra; diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index ca1610a8101..189a231616e 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -262,8 +262,9 @@ struct wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *han wmKeyMap *keymap, int priority); -typedef struct wmKeyMap *(wmEventHandler_KeymapDynamicFn)( - wmWindowManager *wm, struct wmEventHandler_Keymap *handler)ATTR_WARN_UNUSED_RESULT; +typedef struct wmKeyMap *(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm, + struct wmEventHandler_Keymap *handler) + ATTR_WARN_UNUSED_RESULT; struct wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback( struct wmWindowManager *wm, struct wmEventHandler_Keymap *handler); @@ -471,6 +472,12 @@ int WM_operator_call_py(struct bContext *C, struct ReportList *reports, const bool is_undo); +void WM_operator_name_call_ptr_with_depends_on_cursor(struct bContext *C, + wmOperatorType *ot, + short opcontext, + PointerRNA *properties, + const char *drawstr); + /* Used for keymap and macro items. */ void WM_operator_properties_alloc(struct PointerRNA **ptr, struct IDProperty **properties, @@ -573,7 +580,11 @@ void WM_operator_py_idname(char *to, const char *from); bool WM_operator_py_idname_ok_or_report(struct ReportList *reports, const char *classname, const char *idname); -const char *WM_context_member_from_ptr(struct bContext *C, const struct PointerRNA *ptr); +char *WM_context_path_resolve_property_full(struct bContext *C, + const PointerRNA *ptr, + PropertyRNA *prop, + int index); +char *WM_context_path_resolve_full(struct bContext *C, const PointerRNA *ptr); /* wm_operator_type.c */ struct wmOperatorType *WM_operatortype_find(const char *idname, bool quiet); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 8a1ff67b37c..df6dc3af3cb 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -184,6 +184,17 @@ enum { OPTYPE_LOCK_BYPASS = (1 << 9), /** Special type of undo which doesn't store itself multiple times. */ OPTYPE_UNDO_GROUPED = (1 << 10), + + /** + * Depends on the cursor location, when activated from a menu wait for mouse press. + * + * In practice these operators often end up being accessed: + * - Directly from key bindings. + * - As tools in the toolbar. + * + * Even so, accessing from the menu should behave usefully. + */ + OPTYPE_DEPENDS_ON_CURSOR = (1 << 11), }; /** For #WM_cursor_grab_enable wrap axis. */ @@ -228,16 +239,16 @@ typedef enum eOperatorPropTags { #define KM_CTRL 2 #define KM_ALT 4 #define KM_OSKEY 8 -/* means modifier should be pressed 2nd */ -#define KM_SHIFT2 16 -#define KM_CTRL2 32 -#define KM_ALT2 64 -#define KM_OSKEY2 128 + +/* Used for key-map item creation function arguments (never stored in DNA). */ +#define KM_SHIFT_ANY 16 +#define KM_CTRL_ANY 32 +#define KM_ALT_ANY 64 +#define KM_OSKEY_ANY 128 /* KM_MOD_ flags for `wmKeyMapItem` and `wmEvent.alt/shift/oskey/ctrl`. */ /* note that KM_ANY and KM_NOTHING are used with these defines too */ -#define KM_MOD_FIRST 1 -#define KM_MOD_SECOND 2 +#define KM_MOD_HELD 1 /* type: defined in wm_event_types.c */ #define KM_TEXTINPUT -2 @@ -925,7 +936,7 @@ typedef struct wmDragID { } wmDragID; typedef struct wmDragAsset { - /* Note: Can't store the AssetHandle here, since the FileDirEntry it wraps may be freed while + /* NOTE: Can't store the #AssetHandle here, since the #FileDirEntry it wraps may be freed while * dragging. So store necessary data here directly. */ char name[64]; /* MAX_NAME */ diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index eab62ffce4c..a1edc4196dc 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -115,8 +115,15 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { WM_GIZMOGROUPTYPE_SELECT = (1 << 3), /** The gizmo group is to be kept (not removed on loading a new file for eg). */ WM_GIZMOGROUPTYPE_PERSISTENT = (1 << 4), - /** Show all other gizmos when interacting. */ + /** + * Show all other gizmos when interacting. + * Also show this group when another group is being interacted with. + */ WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL = (1 << 5), + + /** Don't draw this gizmo group when it is modal. */ + WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE = (1 << 6), + /** * When used with tool, only run when activating the tool, * instead of linking the gizmo while the tool is active. @@ -127,7 +134,7 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { * when a tool can activate multiple operators based on the key-map. * We could even move the options into the key-map item. * ~ campbell. */ - WM_GIZMOGROUPTYPE_TOOL_INIT = (1 << 6), + WM_GIZMOGROUPTYPE_TOOL_INIT = (1 << 7), /** * This gizmo type supports using the fallback tools keymap. @@ -135,7 +142,7 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { * * Often useful in combination with #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK */ - WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP = (1 << 7), + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP = (1 << 8), /** * Use this from a gizmos refresh callback so we can postpone the refresh operation @@ -146,14 +153,14 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { * for selection operations. This means gizmos that use this check don't interfere * with click drag events by popping up under the cursor and catching the tweak event. */ - WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK = (1 << 8), + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK = (1 << 9), /** * Cause continuous redraws, i.e. set the region redraw flag on every main loop iteration. This * should really be avoided by using proper region redraw tagging, notifiers and the message-bus, * however for VR it's sometimes needed. */ - WM_GIZMOGROUPTYPE_VR_REDRAWS = (1 << 9), + WM_GIZMOGROUPTYPE_VR_REDRAWS = (1 << 10), } eWM_GizmoFlagGroupTypeFlag; /** diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 6f6a2402d89..295196c701b 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -381,31 +381,31 @@ static void gizmomap_prepare_drawing(wmGizmoMap *gzmap, wmGizmo *gz_modal = gzmap->gzmap_context.modal; - /* only active gizmo needs updating */ - if (gz_modal) { - if ((gz_modal->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) { - if ((gz_modal->parent_gzgroup->hide.any == 0) && - wm_gizmogroup_is_visible_in_drawstep(gz_modal->parent_gzgroup, drawstep)) { - if (gizmo_prepare_drawing(gzmap, gz_modal, C, draw_gizmos, drawstep)) { - gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_PREPARE_DRAW; - } - } - /* don't draw any other gizmos */ - return; - } - } - /* Allow refresh functions to ask to be refreshed again, clear before the loop below. */ const bool do_refresh = gzmap->update_flag[drawstep] & GIZMOMAP_IS_REFRESH_CALLBACK; gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_REFRESH_CALLBACK; LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) { /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */ - if (!wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep) || - !WM_gizmo_group_type_poll(C, gzgroup->type)) { + if (!wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep)) { continue; } + if (gz_modal && (gzgroup == gz_modal->parent_gzgroup)) { + if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE) { + continue; + } + } + else { /* Don't poll modal gizmo since some poll functions unlink. */ + if (!WM_gizmo_group_type_poll(C, gzgroup->type)) { + continue; + } + /* When modal only show other gizmo groups tagged with #WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL. */ + if (gz_modal && ((gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0)) { + continue; + } + } + /* Needs to be initialized on first draw. */ /* XXX weak: Gizmo-group may skip refreshing if it's invisible * (map gets untagged nevertheless). */ diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index e11ef52eb84..0b7d5e5f1f4 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -266,8 +266,7 @@ IDTypeInfo IDType_ID_WM = { .name = "WindowManager", .name_plural = "window_managers", .translation_context = BLT_I18NCONTEXT_ID_WINDOWMANAGER, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL | - IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index 50d3a856cbe..9c020b16234 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -1146,5 +1146,31 @@ void wm_init_cursor_data(void) BlenderCursor[WM_CURSOR_ZOOM_OUT] = &ZoomOutCursor; END_CURSOR_BLOCK; + /********************** Area Pick Cursor ***********************/ + BEGIN_CURSOR_BLOCK; + + static char pick_area_bitmap[] = { + 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x10, + 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0xbf, 0x00, 0x81, 0x00, 0x81, + 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x80, 0x00, 0xff, + }; + + static char pick_area_mask[] = { + 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0xff, 0x01, 0xff, 0x01, 0xff, + 0x01, 0x38, 0x00, 0xb8, 0x7f, 0xb8, 0xff, 0x80, 0xc1, 0x80, 0xc1, + 0x80, 0xc1, 0x80, 0xc1, 0x80, 0xc1, 0x80, 0xff, 0x00, 0xff, + }; + + static BCursor PickAreaCursor = { + pick_area_bitmap, + pick_area_mask, + 4, + 4, + false, + }; + + BlenderCursor[WM_CURSOR_PICK_AREA] = &PickAreaCursor; + END_CURSOR_BLOCK; + /********************** Put the cursors in the array ***********************/ } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 1f47b152a2b..ae09786356a 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1673,6 +1673,172 @@ int WM_operator_call_py(bContext *C, /** \} */ /* -------------------------------------------------------------------- */ +/** \name Operator Wait For Input + * + * Delay executing operators that depend on cursor location. + * + * See: #OPTYPE_DEPENDS_ON_CURSOR doc-string for more information. + * \{ */ + +typedef struct uiOperatorWaitForInput { + ScrArea *area; + wmOperatorCallParams optype_params; + bContextStore *context; +} uiOperatorWaitForInput; + +static void ui_handler_wait_for_input_remove(bContext *C, void *userdata) +{ + uiOperatorWaitForInput *opwait = userdata; + if (opwait->optype_params.opptr) { + if (opwait->optype_params.opptr->data) { + IDP_FreeProperty(opwait->optype_params.opptr->data); + } + MEM_freeN(opwait->optype_params.opptr); + } + if (opwait->context) { + CTX_store_free(opwait->context); + } + + if (opwait->area != NULL) { + ED_area_status_text(opwait->area, NULL); + } + else { + ED_workspace_status_text(C, NULL); + } + + MEM_freeN(opwait); +} + +static int ui_handler_wait_for_input(bContext *C, const wmEvent *event, void *userdata) +{ + uiOperatorWaitForInput *opwait = userdata; + enum { CONTINUE = 0, EXECUTE, CANCEL } state = CONTINUE; + state = CONTINUE; + + switch (event->type) { + case LEFTMOUSE: { + if (event->val == KM_PRESS) { + state = EXECUTE; + } + break; + } + /* Useful if the operator isn't convenient to access while the mouse button is held. + * If it takes numeric input for example. */ + case EVT_SPACEKEY: + case EVT_RETKEY: { + if (event->val == KM_PRESS) { + state = EXECUTE; + } + break; + } + case RIGHTMOUSE: { + if (event->val == KM_PRESS) { + state = CANCEL; + } + break; + } + case EVT_ESCKEY: { + if (event->val == KM_PRESS) { + state = CANCEL; + } + break; + } + } + + if (state != CONTINUE) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_modal_restore(win); + + if (state == EXECUTE) { + CTX_store_set(C, opwait->context); + WM_operator_name_call_ptr(C, + opwait->optype_params.optype, + opwait->optype_params.opcontext, + opwait->optype_params.opptr); + CTX_store_set(C, NULL); + } + + WM_event_remove_ui_handler(&win->modalhandlers, + ui_handler_wait_for_input, + ui_handler_wait_for_input_remove, + opwait, + false); + + ui_handler_wait_for_input_remove(C, opwait); + + return WM_UI_HANDLER_BREAK; + } + + return WM_UI_HANDLER_CONTINUE; +} + +void WM_operator_name_call_ptr_with_depends_on_cursor( + bContext *C, wmOperatorType *ot, short opcontext, PointerRNA *properties, const char *drawstr) +{ + int flag = ot->flag; + + LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) { + wmOperatorType *otm = WM_operatortype_find(macro->idname, 0); + if (otm != NULL) { + flag |= otm->flag; + } + } + + if ((flag & OPTYPE_DEPENDS_ON_CURSOR) == 0) { + WM_operator_name_call_ptr(C, ot, opcontext, properties); + return; + } + + wmWindow *win = CTX_wm_window(C); + ScrArea *area = CTX_wm_area(C); + + { + char header_text[UI_MAX_DRAW_STR]; + SNPRINTF(header_text, + "%s %s", + IFACE_("Input pending "), + (drawstr && drawstr[0]) ? drawstr : CTX_IFACE_(ot->translation_context, ot->name)); + if (area != NULL) { + ED_area_status_text(area, header_text); + } + else { + ED_workspace_status_text(C, header_text); + } + } + + WM_cursor_modal_set(win, WM_CURSOR_PICK_AREA); + + uiOperatorWaitForInput *opwait = MEM_callocN(sizeof(*opwait), __func__); + opwait->optype_params.optype = ot; + opwait->optype_params.opcontext = opcontext; + opwait->optype_params.opptr = properties; + + opwait->area = area; + + if (properties) { + opwait->optype_params.opptr = MEM_mallocN(sizeof(*opwait->optype_params.opptr), __func__); + *opwait->optype_params.opptr = *properties; + if (properties->data != NULL) { + opwait->optype_params.opptr->data = IDP_CopyProperty(properties->data); + } + } + + bContextStore *store = CTX_store_get(C); + if (store) { + opwait->context = CTX_store_copy(store); + } + + WM_event_add_ui_handler(C, + &win->modalhandlers, + ui_handler_wait_for_input, + ui_handler_wait_for_input_remove, + opwait, + WM_HANDLER_BLOCKING); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Handler Types * * General API for different handler types. @@ -3749,7 +3915,7 @@ wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, const char *keymap_id = NULL; /* Support for the gizmo owning the tool keymap. */ - if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\n') { + if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\0') { wmGizmoMap *gzmap = NULL; wmGizmoGroup *gzgroup = NULL; LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { @@ -4692,47 +4858,27 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case EVT_LEFTSHIFTKEY: case EVT_RIGHTSHIFTKEY: if (event.val == KM_PRESS) { - if (event_state->ctrl || event_state->alt || event_state->oskey) { - keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND); - } - else { - keymodifier = KM_MOD_FIRST; - } + keymodifier = KM_MOD_HELD; } event.shift = event_state->shift = keymodifier; break; case EVT_LEFTCTRLKEY: case EVT_RIGHTCTRLKEY: if (event.val == KM_PRESS) { - if (event_state->shift || event_state->alt || event_state->oskey) { - keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND); - } - else { - keymodifier = KM_MOD_FIRST; - } + keymodifier = KM_MOD_HELD; } event.ctrl = event_state->ctrl = keymodifier; break; case EVT_LEFTALTKEY: case EVT_RIGHTALTKEY: if (event.val == KM_PRESS) { - if (event_state->ctrl || event_state->shift || event_state->oskey) { - keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND); - } - else { - keymodifier = KM_MOD_FIRST; - } + keymodifier = KM_MOD_HELD; } event.alt = event_state->alt = keymodifier; break; case EVT_OSKEY: if (event.val == KM_PRESS) { - if (event_state->ctrl || event_state->alt || event_state->shift) { - keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND); - } - else { - keymodifier = KM_MOD_FIRST; - } + keymodifier = KM_MOD_HELD; } event.oskey = event_state->oskey = keymodifier; break; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index f83511e76f0..2ce2bcc2f3c 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1515,14 +1515,68 @@ static void wm_history_file_update(void) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Save Main Blend-File (internal) +/** \name Save Main Blend-File (internal) Screen-Shot + * + * Screen-shot the active window. + * \{ */ + +static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **thumb_pt) +{ + if (*thumb_pt) { + /* We are given a valid thumbnail data, so just generate image from it. */ + return BKE_main_thumbnail_to_imbuf(NULL, *thumb_pt); + } + + /* The window to capture should be a main window (without parent). */ + wmWindow *win = CTX_wm_window(C); + while (win && win->parent) { + win = win->parent; + } + + int win_size[2]; + uint *buffer = WM_window_pixels_read(CTX_wm_manager(C), win, win_size); + ImBuf *ibuf = IMB_allocFromBufferOwn(buffer, NULL, win_size[0], win_size[1], 24); + + if (ibuf) { + int ex, ey; + if (ibuf->x > ibuf->y) { + ex = BLEN_THUMB_SIZE; + ey = max_ii(1, (int)(((float)ibuf->y / (float)ibuf->x) * BLEN_THUMB_SIZE)); + } + else { + ex = max_ii(1, (int)(((float)ibuf->x / (float)ibuf->y) * BLEN_THUMB_SIZE)); + ey = BLEN_THUMB_SIZE; + } + + /* File-system thumbnail image can be 256x256. */ + IMB_scaleImBuf(ibuf, ex * 2, ey * 2); + + /* Thumbnail inside blend should be 128x128. */ + ImBuf *thumb_ibuf = IMB_dupImBuf(ibuf); + IMB_scaleImBuf(thumb_ibuf, ex, ey); + + BlendThumbnail *thumb = BKE_main_thumbnail_from_imbuf(NULL, thumb_ibuf); + IMB_freeImBuf(thumb_ibuf); + *thumb_pt = thumb; + } + + /* Must be freed by caller. */ + return ibuf; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Save Main Blend-File (internal) Camera View + * + * Render the current scene with the active camera. * \{ */ /* screen can be NULL */ -static ImBuf *blend_file_thumb(const bContext *C, - Scene *scene, - bScreen *screen, - BlendThumbnail **thumb_pt) +static ImBuf *blend_file_thumb_from_camera(const bContext *C, + Scene *scene, + bScreen *screen, + BlendThumbnail **thumb_pt) { /* will be scaled down, but gives some nice oversampling */ ImBuf *ibuf; @@ -1548,10 +1602,9 @@ static ImBuf *blend_file_thumb(const bContext *C, return NULL; } - if ((scene->camera == NULL) && (screen != NULL)) { + if (screen != NULL) { area = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0); - region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); - if (region) { + if (area) { v3d = area->spacedata.first; } } @@ -1570,13 +1623,14 @@ static ImBuf *blend_file_thumb(const bContext *C, if (scene->camera) { ibuf = ED_view3d_draw_offscreen_imbuf_simple(depsgraph, scene, - NULL, - OB_SOLID, + (v3d) ? &v3d->shading : NULL, + (v3d) ? v3d->shading.type : OB_SOLID, scene->camera, - BLEN_THUMB_SIZE * 2, - BLEN_THUMB_SIZE * 2, + PREVIEW_RENDER_LARGE_HEIGHT * 2, + PREVIEW_RENDER_LARGE_HEIGHT * 2, IB_rect, - V3D_OFSDRAW_NONE, + (v3d) ? V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS : + V3D_OFSDRAW_NONE, R_ALPHAPREMUL, NULL, NULL, @@ -1588,8 +1642,8 @@ static ImBuf *blend_file_thumb(const bContext *C, OB_SOLID, v3d, region, - BLEN_THUMB_SIZE * 2, - BLEN_THUMB_SIZE * 2, + PREVIEW_RENDER_LARGE_HEIGHT * 2, + PREVIEW_RENDER_LARGE_HEIGHT * 2, IB_rect, R_ALPHAPREMUL, NULL, @@ -1610,8 +1664,14 @@ static ImBuf *blend_file_thumb(const bContext *C, if (ibuf) { /* dirty oversampling */ - IMB_scaleImBuf(ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE); - thumb = BKE_main_thumbnail_from_imbuf(NULL, ibuf); + ImBuf *thumb_ibuf; + thumb_ibuf = IMB_dupImBuf(ibuf); + /* BLEN_THUMB_SIZE is size of thumbnail inside blend file: 128x128. */ + IMB_scaleImBuf(thumb_ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE); + thumb = BKE_main_thumbnail_from_imbuf(NULL, thumb_ibuf); + IMB_freeImBuf(thumb_ibuf); + /* Thumbnail saved to file-system should be 256x256. */ + IMB_scaleImBuf(ibuf, PREVIEW_RENDER_LARGE_HEIGHT, PREVIEW_RENDER_LARGE_HEIGHT); } else { /* '*thumb_pt' needs to stay NULL to prevent a bad thumbnail from being handled */ @@ -1693,13 +1753,42 @@ static bool wm_file_write(bContext *C, /* Enforce full override check/generation on file save. */ BKE_lib_override_library_main_operations_create(bmain, true); + if (!G.background) { + /* Redraw to remove menus that might be open. */ + WM_redraw_windows(C); + } + + /* don't forget not to return without! */ + WM_cursor_wait(true); + /* blend file thumbnail */ /* Save before exit_editmode, otherwise derivedmeshes for shared data corrupt T27765. */ /* Main now can store a '.blend' thumbnail, useful for background mode * or thumbnail customization. */ main_thumb = thumb = bmain->blen_thumb; - if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) { - ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb); + if (BLI_thread_is_main() && U.file_preview_type != USER_FILE_PREVIEW_NONE) { + + int file_preview_type = U.file_preview_type; + + if (file_preview_type == USER_FILE_PREVIEW_AUTO) { + Scene *scene = CTX_data_scene(C); + bool do_render = (scene != NULL && scene->camera != NULL && + (BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0) != NULL)); + file_preview_type = do_render ? USER_FILE_PREVIEW_CAMERA : USER_FILE_PREVIEW_SCREENSHOT; + } + + switch (file_preview_type) { + case USER_FILE_PREVIEW_SCREENSHOT: { + ibuf_thumb = blend_file_thumb_from_screenshot(C, &thumb); + break; + } + case USER_FILE_PREVIEW_CAMERA: { + ibuf_thumb = blend_file_thumb_from_camera(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb); + break; + } + default: + BLI_assert_unreachable(); + } } /* operator now handles overwrite checks */ @@ -1708,9 +1797,6 @@ static bool wm_file_write(bContext *C, BKE_packedfile_pack_all(bmain, reports, false); } - /* don't forget not to return without! */ - WM_cursor_wait(true); - ED_editors_flush_edits(bmain); /* First time saving. */ diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 606c9252ff9..d0117d9d57a 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -35,7 +35,9 @@ #include "MEM_guardedalloc.h" #include "DNA_ID.h" +#include "DNA_collection_types.h" #include "DNA_key_types.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" @@ -50,15 +52,21 @@ #include "BLO_readfile.h" +#include "BKE_armature.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_override.h" +#include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" #include "BKE_report.h" +#include "BKE_rigidbody.h" +#include "BKE_scene.h" #include "BKE_idtype.h" @@ -137,6 +145,14 @@ static short wm_link_append_flag(wmOperator *op) if (RNA_boolean_get(op->ptr, "link")) { flag |= FILE_LINK; } + else { + if (RNA_boolean_get(op->ptr, "use_recursive")) { + flag |= FILE_APPEND_RECURSIVE; + } + if (RNA_boolean_get(op->ptr, "set_fake")) { + flag |= FILE_APPEND_SET_FAKEUSER; + } + } if (RNA_boolean_get(op->ptr, "instance_collections")) { flag |= FILE_COLLECTION_INSTANCE; } @@ -153,6 +169,10 @@ typedef struct WMLinkAppendDataItem { *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ short idcode; + /** Type of action to do to append this item, and other append-specific information. */ + char append_action; + char append_tag; + ID *new_id; void *customdata; } WMLinkAppendDataItem; @@ -167,10 +187,32 @@ typedef struct WMLinkAppendData { */ int flag; + /** Allows to easily find an existing items from an ID pointer. Used by append code. */ + GHash *new_id_to_item; + /* Internal 'private' data */ MemArena *memarena; } WMLinkAppendData; +typedef struct WMLinkAppendDataCallBack { + WMLinkAppendData *lapp_data; + WMLinkAppendDataItem *item; + ReportList *reports; + +} WMLinkAppendDataCallBack; + +enum { + WM_APPEND_ACT_UNSET = 0, + WM_APPEND_ACT_KEEP_LINKED, + WM_APPEND_ACT_REUSE_LOCAL, + WM_APPEND_ACT_MAKE_LOCAL, + WM_APPEND_ACT_COPY_LOCAL, +}; + +enum { + WM_APPEND_TAG_INDIRECT = 1 << 0, +}; + static WMLinkAppendData *wm_link_append_data_new(const int flag) { MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -184,6 +226,10 @@ static WMLinkAppendData *wm_link_append_data_new(const int flag) static void wm_link_append_data_free(WMLinkAppendData *lapp_data) { + if (lapp_data->new_id_to_item != NULL) { + BLI_ghash_free(lapp_data->new_id_to_item, NULL, NULL); + } + BLI_memarena_free(lapp_data->memarena); } @@ -213,6 +259,7 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add(WMLinkAppendData *lapp item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); item->new_id = NULL; + item->append_action = WM_APPEND_ACT_UNSET; item->customdata = customdata; BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); @@ -221,6 +268,568 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add(WMLinkAppendData *lapp return item; } +/* -------------------------------------------------------------------- */ +/** \name Library appending helper functions. + * + * FIXME: Deduplicate code with similar one in readfile.c + * \{ */ + +static bool object_in_any_scene(Main *bmain, Object *ob) +{ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { + if (BKE_scene_object_find(sce, ob)) { + return true; + } + } + + return false; +} + +static bool object_in_any_collection(Main *bmain, Object *ob) +{ + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_has_object(collection, ob)) { + return true; + } + } + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->master_collection != NULL && + BKE_collection_has_object(scene->master_collection, ob)) { + return true; + } + } + + return false; +} + +/** + * Shared operations to perform on the object's base after adding it to the scene. + */ +static void wm_append_loose_data_instantiate_object_base_instance_init( + Object *ob, bool set_selected, bool set_active, ViewLayer *view_layer, const View3D *v3d) +{ + Base *base = BKE_view_layer_base_find(view_layer, ob); + + if (v3d != NULL) { + base->local_view_bits |= v3d->local_view_uuid; + } + + if (set_selected) { + if (base->flag & BASE_SELECTABLE) { + base->flag |= BASE_SELECTED; + } + } + + if (set_active) { + view_layer->basact = base; + } + + BKE_scene_object_base_flag_sync_from_base(base); +} + +static ID *wm_append_loose_data_instantiate_process_check(WMLinkAppendDataItem *item) +{ + /* We consider that if we either kept it linked, or re-used already local data, instantiation + * status of those should not be modified. */ + if (!ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_MAKE_LOCAL)) { + return NULL; + } + + ID *id = item->new_id; + if (id == NULL) { + return NULL; + } + + if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) { + BLI_assert(ID_IS_LINKED(id)); + id = id->newid; + if (id == NULL) { + return NULL; + } + + BLI_assert(!ID_IS_LINKED(id)); + return id; + } + + BLI_assert(!ID_IS_LINKED(id)); + return id; +} + +static void wm_append_loose_data_instantiate_ensure_active_collection( + WMLinkAppendData *lapp_data, + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Collection **r_active_collection) +{ + /* Find or add collection as needed. */ + if (*r_active_collection == NULL) { + if (lapp_data->flag & FILE_ACTIVE_COLLECTION) { + LayerCollection *lc = BKE_layer_collection_get_active(view_layer); + *r_active_collection = lc->collection; + } + else { + *r_active_collection = BKE_collection_add(bmain, scene->master_collection, NULL); + } + } +} + +/* TODO: De-duplicate this code with the one in readfile.c, think we need some utils code for that + * in BKE. */ +static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + const View3D *v3d) +{ + if (scene == NULL) { + /* In some cases, like the asset drag&drop e.g., the caller code manages instantiation itself. + */ + return; + } + + LinkNode *itemlink; + Collection *active_collection = NULL; + const bool do_obdata = (lapp_data->flag & FILE_OBDATA_INSTANCE) != 0; + + const bool object_set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; + /* Do NOT make base active here! screws up GUI stuff, + * if you want it do it at the editor level. */ + const bool object_set_active = false; + + /* First pass on obdata to enable their instantiation by default, then do a second pass on + * objects to clear it for any obdata already in use. */ + if (do_obdata) { + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL) { + continue; + } + const ID_Type idcode = GS(id->name); + if (!OB_DATA_SUPPORT_ID(idcode)) { + continue; + } + + id->tag |= LIB_TAG_DOIT; + } + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = item->new_id; + if (id == NULL || GS(id->name) != ID_OB) { + continue; + } + + Object *ob = (Object *)id; + Object *new_ob = (Object *)id->newid; + if (ob->data != NULL) { + ((ID *)(ob->data))->tag &= ~LIB_TAG_DOIT; + } + if (new_ob != NULL && new_ob->data != NULL) { + ((ID *)(new_ob->data))->tag &= ~LIB_TAG_DOIT; + } + } + } + + /* First do collections, then objects, then obdata. */ + + /* NOTE: For collections we only view_layer-instantiate duplicated collections that have + * non-instantiated objects in them. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL || GS(id->name) != ID_GR) { + continue; + } + + /* We do not want to force instantiation of indirectly appended collections. Users can now + * easily instantiate collections (and their objects) as needed by themselves. See T67032. */ + /* We need to check that objects in that collections are already instantiated in a scene. + * Otherwise, it's better to add the collection to the scene's active collection, than to + * instantiate its objects in active scene's collection directly. See T61141. + * + * NOTE: We only check object directly into that collection, not recursively into its + * children. + */ + Collection *collection = (Collection *)id; + bool do_add_collection = false; + LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { + Object *ob = coll_ob->ob; + if (!object_in_any_scene(bmain, ob)) { + do_add_collection = true; + break; + } + } + if (do_add_collection) { + wm_append_loose_data_instantiate_ensure_active_collection( + lapp_data, bmain, scene, view_layer, &active_collection); + + /* In case user requested instantiation of collections as empties, we do so for the one they + * explicitly selected (originally directly linked IDs). */ + if ((lapp_data->flag & FILE_COLLECTION_INSTANCE) != 0 && + (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0) { + /* BKE_object_add(...) messes with the selection. */ + Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2); + ob->type = OB_EMPTY; + ob->empty_drawsize = U.collection_instance_empty_size; + + BKE_collection_object_add(bmain, active_collection, ob); + + const bool set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; + /* TODO: why is it OK to make this active here but not in other situations? + * See other callers of #object_base_instance_init */ + const bool set_active = set_selected; + wm_append_loose_data_instantiate_object_base_instance_init( + ob, set_selected, set_active, view_layer, v3d); + + /* Assign the collection. */ + ob->instance_collection = collection; + id_us_plus(&collection->id); + ob->transflag |= OB_DUPLICOLLECTION; + copy_v3_v3(ob->loc, scene->cursor.location); + } + else { + /* Add collection as child of active collection. */ + BKE_collection_child_add(bmain, active_collection, collection); + + if ((lapp_data->flag & FILE_AUTOSELECT) != 0) { + LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { + Object *ob = coll_ob->ob; + Base *base = BKE_view_layer_base_find(view_layer, ob); + if (base) { + base->flag |= BASE_SELECTED; + BKE_scene_object_base_flag_sync_from_base(base); + } + } + } + } + } + } + + /* NOTE: For objects we only view_layer-instantiate duplicated objects that are not yet used + * anywhere. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL || GS(id->name) != ID_OB) { + continue; + } + + Object *ob = (Object *)id; + + if (object_in_any_collection(bmain, ob)) { + continue; + } + + wm_append_loose_data_instantiate_ensure_active_collection( + lapp_data, bmain, scene, view_layer, &active_collection); + + CLAMP_MIN(ob->id.us, 0); + ob->mode = OB_MODE_OBJECT; + + BKE_collection_object_add(bmain, active_collection, ob); + + wm_append_loose_data_instantiate_object_base_instance_init( + ob, object_set_selected, object_set_active, view_layer, v3d); + } + + if (!do_obdata) { + return; + } + + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL) { + continue; + } + const ID_Type idcode = GS(id->name); + if (!OB_DATA_SUPPORT_ID(idcode)) { + continue; + } + if ((id->tag & LIB_TAG_DOIT) == 0) { + continue; + } + + wm_append_loose_data_instantiate_ensure_active_collection( + lapp_data, bmain, scene, view_layer, &active_collection); + + const int type = BKE_object_obdata_to_type(id); + BLI_assert(type != -1); + Object *ob = BKE_object_add_only_object(bmain, type, id->name + 2); + ob->data = id; + id_us_plus(id); + BKE_object_materials_test(bmain, ob, ob->data); + + BKE_collection_object_add(bmain, active_collection, ob); + + wm_append_loose_data_instantiate_object_base_instance_init( + ob, object_set_selected, object_set_active, view_layer, v3d); + + copy_v3_v3(ob->loc, scene->cursor.location); + } +} + +/** \} */ + +static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) +{ + if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK)) { + return IDWALK_RET_NOP; + } + + WMLinkAppendDataCallBack *data = cb_data->user_data; + ID *id = *cb_data->id_pointer; + + if (id == NULL) { + return IDWALK_RET_NOP; + } + + if (!BKE_idtype_idcode_is_linkable(GS(id->name))) { + return IDWALK_RET_NOP; + } + + WMLinkAppendDataItem *item = BLI_ghash_lookup(data->lapp_data->new_id_to_item, id); + if (item == NULL) { + item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL); + item->new_id = id; + /* Since we did not have an item for that ID yet, we now user did not selected it explicitly, + * it was rather linked indirectly. This info is important for instantiation of collections. */ + item->append_tag |= WM_APPEND_TAG_INDIRECT; + BLI_ghash_insert(data->lapp_data->new_id_to_item, id, item); + } + + /* NOTE: currently there is no need to do anything else here, but in the future this would be + * the place to add specific per-usage decisions on how to append an ID. */ + + return IDWALK_RET_NOP; +} + +/* Perform append operation, using modern ID usage looper to detect which ID should be kept linked, + * made local, duplicated as local, re-used from local etc. + * + * TODO: Expose somehow this logic to the two other parts of code performing actual append + * (i.e. copy/paste and `bpy` link/append API). + * Then we can heavily simplify #BKE_library_make_local(). */ +static void wm_append_do(WMLinkAppendData *lapp_data, + ReportList *reports, + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + const View3D *v3d) +{ + BLI_assert((lapp_data->flag & FILE_LINK) == 0); + + const bool do_recursive = (lapp_data->flag & FILE_APPEND_RECURSIVE) != 0; + const bool set_fakeuser = (lapp_data->flag & FILE_APPEND_SET_FAKEUSER) != 0; + + LinkNode *itemlink; + + /* Generate a mapping between newly linked IDs and their items. */ + lapp_data->new_id_to_item = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = item->new_id; + if (id == NULL) { + continue; + } + BLI_ghash_insert(lapp_data->new_id_to_item, id, item); + } + + /* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect + * dependencies), this list will grow and we will process those IDs later, leading to a flatten + * recursive processing of all the linked dependencies. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = item->new_id; + if (id == NULL) { + continue; + } + BLI_assert(item->customdata == NULL); + + /* Clear tag previously used to mark IDs needing post-processing (instantiation of loose + * objects etc.). */ + id->tag &= ~LIB_TAG_DOIT; + + if (item->append_action != WM_APPEND_ACT_UNSET) { + /* Already set, pass. */ + } + if (GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) { + CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); + item->append_action = WM_APPEND_ACT_KEEP_LINKED; + } + else if (id->tag & LIB_TAG_PRE_EXISTING) { + CLOG_INFO(&LOG, 3, "Appended ID '%s' was already linked, need to copy it...", id->name); + item->append_action = WM_APPEND_ACT_COPY_LOCAL; + } + else { + /* In future we could search for already existing matching local ID etc. */ + CLOG_INFO(&LOG, 3, "Appended ID '%s' will be made local...", id->name); + item->append_action = WM_APPEND_ACT_MAKE_LOCAL; + } + + /* Only check dependencies if we are not keeping linked data, nor re-using existing local data. + */ + if (do_recursive && + !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { + WMLinkAppendDataCallBack cb_data = { + .lapp_data = lapp_data, .item = item, .reports = reports}; + BKE_library_foreach_ID_link( + bmain, id, foreach_libblock_append_callback, &cb_data, IDWALK_NOP); + } + } + + /* Effectively perform required operation on every linked ID. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = item->new_id; + if (id == NULL) { + continue; + } + + ID *local_appended_new_id = NULL; + switch (item->append_action) { + case WM_APPEND_ACT_COPY_LOCAL: { + BKE_lib_id_make_local( + bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_COPY); + local_appended_new_id = id->newid; + break; + } + case WM_APPEND_ACT_MAKE_LOCAL: + BKE_lib_id_make_local(bmain, + id, + LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_LOCAL | + LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BLI_assert(id->newid == NULL); + local_appended_new_id = id; + break; + case WM_APPEND_ACT_KEEP_LINKED: + /* Nothing to do here. */ + break; + case WM_APPEND_ACT_REUSE_LOCAL: + /* We only need to set `newid` to ID found in previous loop, for proper remapping. */ + ID_NEW_SET(id, item->customdata); + /* This is not a 'new' local appended id, do not set `local_appended_new_id` here. */ + break; + case WM_APPEND_ACT_UNSET: + CLOG_ERROR( + &LOG, "Unexpected unset append action for '%s' ID, assuming 'keep link'", id->name); + break; + default: + BLI_assert(0); + } + + if (local_appended_new_id != NULL) { + if (GS(local_appended_new_id->name) == ID_OB) { + BKE_rigidbody_ensure_local_object(bmain, (Object *)local_appended_new_id); + } + if (set_fakeuser) { + if (!ELEM(GS(local_appended_new_id->name), ID_OB, ID_GR)) { + /* Do not set fake user on objects nor collections (instancing). */ + id_fake_user_set(local_appended_new_id); + } + } + } + } + + /* Remap IDs as needed. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + + if (item->append_action == WM_APPEND_ACT_KEEP_LINKED) { + continue; + } + + ID *id = item->new_id; + if (id == NULL) { + continue; + } + if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) { + BLI_assert(ID_IS_LINKED(id)); + id = id->newid; + if (id == NULL) { + continue; + } + } + + BLI_assert(!ID_IS_LINKED(id)); + + BKE_libblock_relink_to_newid_new(bmain, id); + } + + /* Instantiate newly created (duplicated) IDs as needed. */ + wm_append_loose_data_instantiate(lapp_data, bmain, scene, view_layer, v3d); + + /* Attempt to deal with object proxies. + * + * NOTE: Copied from `BKE_library_make_local`, but this is not really working (as in, not + * producing any useful result in any known use case), neither here nor in + * `BKE_library_make_local` currently. + * Proxies are end of life anyway, so not worth spending time on this. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + + if (item->append_action != WM_APPEND_ACT_COPY_LOCAL) { + continue; + } + + ID *id = item->new_id; + if (id == NULL) { + continue; + } + BLI_assert(ID_IS_LINKED(id)); + + /* Attempt to re-link copied proxy objects. This allows appending of an entire scene + * from another blend file into this one, even when that blend file contains proxified + * armatures that have local references. Since the proxified object needs to be linked + * (not local), this will only work when the "Localize all" checkbox is disabled. + * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ + if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { + Object *ob = (Object *)id; + Object *ob_new = (Object *)id->newid; + bool is_local = false, is_lib = false; + + /* Proxies only work when the proxified object is linked-in from a library. */ + if (!ID_IS_LINKED(ob->proxy)) { + CLOG_WARN(&LOG, + "Proxy object %s will lose its link to %s, because the " + "proxified object is local", + id->newid->name, + ob->proxy->id.name); + continue; + } + + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + + /* We can only switch the proxy'ing to a made-local proxy if it is no longer + * referred to from a library. Not checking for local use; if new local proxy + * was not used locally would be a nasty bug! */ + if (is_local || is_lib) { + CLOG_WARN(&LOG, + "Made-local proxy object %s will lose its link to %s, " + "because the linked-in proxy is referenced (is_local=%i, is_lib=%i)", + id->newid->name, + ob->proxy->id.name, + is_local, + is_lib); + } + else { + /* we can switch the proxy'ing from the linked-in to the made-local proxy. + * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that + * was already allocated by object_make_local() (which called BKE_object_copy). */ + ob_new->proxy = ob->proxy; + ob_new->proxy_group = ob->proxy_group; + ob_new->proxy_from = ob->proxy_from; + ob_new->proxy->proxy_from = ob_new; + ob->proxy = ob->proxy_from = ob->proxy_group = NULL; + } + } + } + + BKE_main_id_newptr_and_tag_clear(bmain); +} + static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, @@ -263,6 +872,11 @@ static void wm_link_do(WMLinkAppendData *lapp_data, struct LibraryLink_Params liblink_params; BLO_library_link_params_init_with_context( &liblink_params, bmain, flag, id_tag_extra, scene, view_layer, v3d); + /* In case of append, do not handle instantiation in linking process, but during append phase + * (see #wm_append_loose_data_instantiate ). */ + if ((flag & FILE_LINK) == 0) { + liblink_params.flag &= ~BLO_LIBLINK_NEEDS_ID_TAG_DOIT; + } mainl = BLO_library_link_begin(&bh, libname, &liblink_params); lib = mainl->curlib; @@ -325,9 +939,8 @@ static bool wm_link_append_item_poll(ReportList *reports, idcode = BKE_idtype_idcode_from_name(group); - /* XXX For now, we do a nasty exception for workspace, forbid linking them. - * Not nice, ultimately should be solved! */ - if (!BKE_idtype_idcode_is_linkable(idcode) && (do_append || idcode != ID_WS)) { + if (!BKE_idtype_idcode_is_linkable(idcode) || + (!do_append && BKE_idtype_idcode_is_only_appendable(idcode))) { if (reports) { if (do_append) { BKE_reportf(reports, @@ -501,28 +1114,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) /* append, rather than linking */ if (do_append) { - const bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); - const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive"); - - if (use_recursive) { - BKE_library_make_local(bmain, NULL, NULL, true, set_fake); - } - else { - LinkNode *itemlink; - GSet *done_libraries = BLI_gset_new_ex( - BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__, lapp_data->num_libraries); - - for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { - ID *new_id = ((WMLinkAppendDataItem *)(itemlink->link))->new_id; - - if (new_id && !BLI_gset_haskey(done_libraries, new_id->lib)) { - BKE_library_make_local(bmain, new_id->lib, NULL, true, set_fake); - BLI_gset_insert(done_libraries, new_id->lib); - } - } - - BLI_gset_free(done_libraries, NULL); - } + wm_append_do(lapp_data, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C)); } wm_link_append_data_free(lapp_data); @@ -652,20 +1244,20 @@ void WM_OT_append(wmOperatorType *ot) * * \{ */ -static ID *wm_file_link_datablock_ex(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - View3D *v3d, - const char *filepath, - const short id_code, - const char *id_name, - bool clear_pre_existing_flag) +static ID *wm_file_link_append_datablock_ex(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + View3D *v3d, + const char *filepath, + const short id_code, + const char *id_name, + const bool do_append) { /* Tag everything so we can make local only the new datablock. */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); /* Define working data, with just the one item we want to link. */ - WMLinkAppendData *lapp_data = wm_link_append_data_new(0); + WMLinkAppendData *lapp_data = wm_link_append_data_new(do_append ? FILE_APPEND_RECURSIVE : 0); wm_link_append_data_library_add(lapp_data, filepath); WMLinkAppendDataItem *item = wm_link_append_data_item_add(lapp_data, id_name, id_code, NULL); @@ -676,15 +1268,22 @@ static ID *wm_file_link_datablock_ex(Main *bmain, /* Get linked datablock and free working data. */ ID *id = item->new_id; - wm_link_append_data_free(lapp_data); - if (clear_pre_existing_flag) { - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + if (do_append) { + wm_append_do(lapp_data, NULL, bmain, scene, view_layer, v3d); } + wm_link_append_data_free(lapp_data); + + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + return id; } +/* + * NOTE: `scene` (and related `view_layer` and `v3d`) pointers may be NULL, in which case no + * instantiation of linked objects, collections etc. will be performed. + */ ID *WM_file_link_datablock(Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -693,10 +1292,14 @@ ID *WM_file_link_datablock(Main *bmain, const short id_code, const char *id_name) { - return wm_file_link_datablock_ex( - bmain, scene, view_layer, v3d, filepath, id_code, id_name, true); + return wm_file_link_append_datablock_ex( + bmain, scene, view_layer, v3d, filepath, id_code, id_name, false); } +/* + * NOTE: `scene` (and related `view_layer` and `v3d`) pointers may be NULL, in which case no + * instantiation of appended objects, collections etc. will be performed. + */ ID *WM_file_append_datablock(Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -705,14 +1308,8 @@ ID *WM_file_append_datablock(Main *bmain, const short id_code, const char *id_name) { - ID *id = wm_file_link_datablock_ex( - bmain, scene, view_layer, v3d, filepath, id_code, id_name, false); - - /* Make datablock local. */ - BKE_library_make_local(bmain, NULL, NULL, true, false); - - /* Clear pre existing tag. */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + ID *id = wm_file_link_append_datablock_ex( + bmain, scene, view_layer, v3d, filepath, id_code, id_name, true); return id; } diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 1c736647084..cc376d8f201 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -818,6 +818,8 @@ void WM_OT_lasso_gesture(wmOperatorType *ot) ot->poll = WM_operator_winactive; + ot->flag = OPTYPE_DEPENDS_ON_CURSOR; + prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_runtime(ot->srna, prop, &RNA_OperatorMousePath); } @@ -954,8 +956,9 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev break; } case GESTURE_MODAL_FLIP: { - /* Toggle snapping on/off. */ + /* Toggle flipping on/off. */ gesture->use_flip = !gesture->use_flip; + gesture_straightline_apply(C, op); break; } case GESTURE_MODAL_SELECT: { @@ -993,6 +996,7 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev if (gesture->use_snap) { wm_gesture_straightline_do_angle_snap(rect); + gesture_straightline_apply(C, op); } wm_gesture_tag_redraw(win); diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 25bcf1967ea..f955abaed53 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -460,8 +460,11 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap) if (UNLIKELY(BLI_listbase_is_empty(&keymap->items))) { /* Empty key-maps may be missing more there may be a typo in the name. - * Warn early to avoid losing time investigating each case. */ - CLOG_WARN(WM_LOG_KEYMAPS, "empty keymap '%s'", keymap->idname); + * Warn early to avoid losing time investigating each case. + * When developing a customized Blender though you may want empty keymaps. */ + if (!U.app_template[0]) { + CLOG_WARN(WM_LOG_KEYMAPS, "empty keymap '%s'", keymap->idname); + } } if (keymap->poll != NULL) { @@ -481,13 +484,20 @@ static void keymap_event_set( kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = KM_ANY; } else { - kmi->shift = (modifier & KM_SHIFT) ? KM_MOD_FIRST : - ((modifier & KM_SHIFT2) ? KM_MOD_SECOND : false); - kmi->ctrl = (modifier & KM_CTRL) ? KM_MOD_FIRST : - ((modifier & KM_CTRL2) ? KM_MOD_SECOND : false); - kmi->alt = (modifier & KM_ALT) ? KM_MOD_FIRST : ((modifier & KM_ALT2) ? KM_MOD_SECOND : false); - kmi->oskey = (modifier & KM_OSKEY) ? KM_MOD_FIRST : - ((modifier & KM_OSKEY2) ? KM_MOD_SECOND : false); + /* Only one of the flags should be set. */ + BLI_assert(((modifier & (KM_SHIFT | KM_SHIFT_ANY)) != (KM_SHIFT | KM_SHIFT_ANY)) && + ((modifier & (KM_CTRL | KM_CTRL_ANY)) != (KM_CTRL | KM_CTRL_ANY)) && + ((modifier & (KM_ALT | KM_ALT_ANY)) != (KM_ALT | KM_ALT_ANY)) && + ((modifier & (KM_OSKEY | KM_OSKEY_ANY)) != (KM_OSKEY | KM_OSKEY_ANY))); + + kmi->shift = ((modifier & KM_SHIFT) ? KM_MOD_HELD : + ((modifier & KM_SHIFT_ANY) ? KM_ANY : KM_NOTHING)); + kmi->ctrl = ((modifier & KM_CTRL) ? KM_MOD_HELD : + ((modifier & KM_CTRL_ANY) ? KM_ANY : KM_NOTHING)); + kmi->alt = ((modifier & KM_ALT) ? KM_MOD_HELD : + ((modifier & KM_ALT_ANY) ? KM_ANY : KM_NOTHING)); + kmi->oskey = ((modifier & KM_OSKEY) ? KM_MOD_HELD : + ((modifier & KM_OSKEY_ANY) ? KM_ANY : KM_NOTHING)); } } @@ -1161,7 +1171,6 @@ int WM_keymap_item_raw_to_string(const short shift, buf[0] = '\0'; - /* TODO: support order (KM_SHIFT vs. KM_SHIFT2) ? */ if (shift == KM_ANY && ctrl == KM_ANY && alt == KM_ANY && oskey == KM_ANY) { /* Don't show anything for any mapping. */ } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index a10652f12ff..443857c2661 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -40,6 +40,7 @@ #include "CLG_log.h" #include "DNA_ID.h" +#include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -55,8 +56,10 @@ #include "BLI_dial_2d.h" #include "BLI_dynstr.h" /* For #WM_operator_pystring. */ #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BKE_anim_data.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -347,7 +350,7 @@ bool WM_operator_pystring_abbreviate(char *str, int str_len_max) /* return NULL if no match is found */ #if 0 -static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr) +static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr, bool *r_is_id) { /* loop over all context items and do 2 checks * @@ -362,6 +365,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr const char *member_found = NULL; const char *member_id = NULL; + bool member_found_is_id = false; for (link = lb.first; link; link = link->next) { const char *identifier = link->data; @@ -373,14 +377,15 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr } if (ptr->owner_id == ctx_item_ptr.owner_id) { + const bool is_id = RNA_struct_is_ID(ctx_item_ptr.type); if ((ptr->data == ctx_item_ptr.data) && (ptr->type == ctx_item_ptr.type)) { /* found! */ member_found = identifier; + member_found_is_id = is_id; break; } - else if (RNA_struct_is_ID(ctx_item_ptr.type)) { - /* we found a reference to this ID, - * so fallback to it if there is no direct reference */ + if (is_id) { + /* Found a reference to this ID, so fallback to it if there is no direct reference. */ member_id = identifier; } } @@ -388,9 +393,11 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr BLI_freelistN(&lb); if (member_found) { + *r_is_id = member_found_is_id; return member_found; } else if (member_id) { + *r_is_id = true; return member_id; } else { @@ -402,11 +409,24 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr /* use hard coded checks for now */ -static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr) +/** + * \param: r_is_id: + * - When set to true, the returned member is an ID type. + * This is a signal that #RNA_path_from_ID_to_struct needs to be used to calculate + * the remainder of the RNA path. + * - When set to false, the returned member is not an ID type. + * In this case the context path *must* resolve to `ptr`, + * since there is no convenient way to calculate partial RNA paths. + * + * \note While the path to the ID is typically sufficient to calculate the remainder of the path, + * in practice this would cause #WM_context_path_resolve_property_full to crate a path such as: + * `object.data.bones["Bones"].use_deform` such paths are not useful for key-shortcuts, + * so this function supports returning data-paths directly to context members that aren't ID types. + */ +static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr, bool *r_is_id) { const char *member_id = NULL; - - if (ptr->owner_id) { + bool is_id = false; # define CTX_TEST_PTR_ID(C, member, idptr) \ { \ @@ -414,6 +434,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr PointerRNA ctx_item_ptr = CTX_data_pointer_get(C, ctx_member); \ if (ctx_item_ptr.owner_id == idptr) { \ member_id = ctx_member; \ + is_id = true; \ break; \ } \ } \ @@ -426,6 +447,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr PointerRNA ctx_item_ptr = CTX_data_pointer_get(C, ctx_member); \ if (ctx_item_ptr.owner_id && (ID *)cast(ctx_item_ptr.owner_id) == idptr) { \ member_id = ctx_member_full; \ + is_id = true; \ break; \ } \ } \ @@ -441,17 +463,62 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr } \ (void)0 - switch (GS(ptr->owner_id->name)) { + /* A version of #TEST_PTR_DATA_TYPE that calls `CTX_data_pointer_get_type(C, member)`. */ +# define TEST_PTR_DATA_TYPE_FROM_CONTEXT(member, rna_type, rna_ptr) \ + { \ + const char *ctx_member = member; \ + if (RNA_struct_is_a((rna_ptr)->type, &(rna_type)) && \ + (rna_ptr)->data == (CTX_data_pointer_get_type(C, ctx_member, &(rna_type)).data)) { \ + member_id = ctx_member; \ + break; \ + } \ + } \ + (void)0 + + /* General checks (multiple ID types). */ + if (ptr->owner_id) { + const ID_Type ptr_id_type = GS(ptr->owner_id->name); + + /* Support break in the macros for an early exit. */ + do { + /* Animation Data. */ + if (id_type_can_have_animdata(ptr_id_type)) { + TEST_PTR_DATA_TYPE_FROM_CONTEXT("active_nla_track", RNA_NlaTrack, ptr); + TEST_PTR_DATA_TYPE_FROM_CONTEXT("active_nla_strip", RNA_NlaStrip, ptr); + } + } while (0); + } + + /* Specific ID type checks. */ + if (ptr->owner_id && (member_id == NULL)) { + + const ID_Type ptr_id_type = GS(ptr->owner_id->name); + switch (ptr_id_type) { case ID_SCE: { + TEST_PTR_DATA_TYPE_FROM_CONTEXT("active_sequence_strip", RNA_Sequence, ptr); + CTX_TEST_PTR_ID(C, "scene", ptr->owner_id); break; } case ID_OB: { + TEST_PTR_DATA_TYPE_FROM_CONTEXT("active_pose_bone", RNA_PoseBone, ptr); + CTX_TEST_PTR_ID(C, "object", ptr->owner_id); break; } /* from rna_Main_objects_new */ case OB_DATA_SUPPORT_ID_CASE: { + + if (ptr_id_type == ID_AR) { + const bArmature *arm = (bArmature *)ptr->owner_id; + if (arm->edbo != NULL) { + TEST_PTR_DATA_TYPE("active_bone", RNA_EditBone, ptr, arm->act_edbone); + } + else { + TEST_PTR_DATA_TYPE("active_bone", RNA_Bone, ptr, arm->act_bone); + } + } + # define ID_CAST_OBDATA(id_pt) (((Object *)(id_pt))->data) CTX_TEST_PTR_ID_CAST(C, "object", "object.data", ID_CAST_OBDATA, ptr->owner_id); break; @@ -485,37 +552,38 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr const View3D *v3d = (View3D *)space_data; const View3DShading *shading = &v3d->shading; - TEST_PTR_DATA_TYPE("space_data", RNA_View3DOverlay, ptr, v3d); - TEST_PTR_DATA_TYPE("space_data", RNA_View3DShading, ptr, shading); + TEST_PTR_DATA_TYPE("space_data.overlay", RNA_View3DOverlay, ptr, v3d); + TEST_PTR_DATA_TYPE("space_data.shading", RNA_View3DShading, ptr, shading); break; } case SPACE_GRAPH: { const SpaceGraph *sipo = (SpaceGraph *)space_data; const bDopeSheet *ads = sipo->ads; - TEST_PTR_DATA_TYPE("space_data", RNA_DopeSheet, ptr, ads); + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); break; } case SPACE_FILE: { const SpaceFile *sfile = (SpaceFile *)space_data; const FileSelectParams *params = ED_fileselect_get_active_params(sfile); - TEST_PTR_DATA_TYPE("space_data", RNA_FileSelectParams, ptr, params); + TEST_PTR_DATA_TYPE("space_data.params", RNA_FileSelectParams, ptr, params); break; } case SPACE_IMAGE: { const SpaceImage *sima = (SpaceImage *)space_data; - TEST_PTR_DATA_TYPE("space_data", RNA_SpaceUVEditor, ptr, sima); + TEST_PTR_DATA_TYPE("space_data.overlay", RNA_SpaceImageOverlay, ptr, sima); + TEST_PTR_DATA_TYPE("space_data.uv_editor", RNA_SpaceUVEditor, ptr, sima); break; } case SPACE_NLA: { const SpaceNla *snla = (SpaceNla *)space_data; const bDopeSheet *ads = snla->ads; - TEST_PTR_DATA_TYPE("space_data", RNA_DopeSheet, ptr, ads); + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); break; } case SPACE_ACTION: { const SpaceAction *sact = (SpaceAction *)space_data; const bDopeSheet *ads = &sact->ads; - TEST_PTR_DATA_TYPE("space_data", RNA_DopeSheet, ptr, ads); + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); break; } } @@ -530,32 +598,71 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr # undef TEST_PTR_DATA_TYPE } + *r_is_id = is_id; + return member_id; } #endif +/** + * Calculate the path to `ptr` from context `C`, or return NULL if it can't be calculated. + */ +char *WM_context_path_resolve_property_full(bContext *C, + const PointerRNA *ptr, + PropertyRNA *prop, + int index) +{ + bool is_id; + const char *member_id = wm_context_member_from_ptr(C, ptr, &is_id); + char *member_id_data_path = NULL; + if (member_id != NULL) { + if (is_id && !RNA_struct_is_ID(ptr->type)) { + char *data_path = RNA_path_from_ID_to_struct(ptr); + if (data_path != NULL) { + if (prop != NULL) { + char *prop_str = RNA_path_property_py(ptr, prop, index); + member_id_data_path = BLI_string_join_by_sep_charN('.', member_id, data_path, prop_str); + MEM_freeN(prop_str); + } + else { + member_id_data_path = BLI_string_join_by_sep_charN('.', member_id, data_path); + } + MEM_freeN(data_path); + } + } + else { + if (prop != NULL) { + char *prop_str = RNA_path_property_py(ptr, prop, index); + member_id_data_path = BLI_string_join_by_sep_charN('.', member_id, prop_str); + MEM_freeN(prop_str); + } + else { + member_id_data_path = BLI_strdup(member_id); + } + } + } + return member_id_data_path; +} + +char *WM_context_path_resolve_full(bContext *C, const PointerRNA *ptr) +{ + return WM_context_path_resolve_property_full(C, ptr, NULL, -1); +} + static char *wm_prop_pystring_from_context(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index) { - const char *member_id = wm_context_member_from_ptr(C, ptr); + char *member_id_data_path = WM_context_path_resolve_property_full(C, ptr, prop, index); char *ret = NULL; - if (member_id != NULL) { - char *prop_str = RNA_path_struct_property_py(ptr, prop, index); - if (prop_str) { - ret = BLI_sprintfN("bpy.context.%s.%s", member_id, prop_str); - MEM_freeN(prop_str); - } + if (member_id_data_path != NULL) { + ret = BLI_sprintfN("bpy.context.%s", member_id_data_path); + MEM_freeN(member_id_data_path); } return ret; } -const char *WM_context_member_from_ptr(bContext *C, const PointerRNA *ptr) -{ - return wm_context_member_from_ptr(C, ptr); -} - char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index) { char *lhs = C ? wm_prop_pystring_from_context(C, ptr, prop, index) : NULL; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index e9d0adf6798..887aed7ffc7 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -808,16 +808,17 @@ wmWindow *WM_window_open(bContext *C, /* changes rect to fit within desktop */ wm_window_check_size(&rect); - /* Reuse temporary windows when they share the same title. */ + /* Reuse temporary windows when they share the same single area. */ wmWindow *win = NULL; if (temp) { LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) { - if (WM_window_is_temp_screen(win_iter)) { - char *wintitle = GHOST_GetTitle(win_iter->ghostwin); - if (STREQ(title, wintitle)) { + const bScreen *screen = WM_window_get_active_screen(win_iter); + if (screen && screen->temp && BLI_listbase_is_single(&screen->areabase)) { + ScrArea *area = screen->areabase.first; + if (space_type == (area->butspacetype ? area->butspacetype : area->spacetype)) { win = win_iter; + break; } - free(wintitle); } } } @@ -1196,7 +1197,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_SHIFT) { - win->eventstate->shift = KM_MOD_FIRST; + win->eventstate->shift = KM_MOD_HELD; } } #endif @@ -1209,7 +1210,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_CTRL) { - win->eventstate->ctrl = KM_MOD_FIRST; + win->eventstate->ctrl = KM_MOD_HELD; } } #endif @@ -1222,7 +1223,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_ALT) { - win->eventstate->alt = KM_MOD_FIRST; + win->eventstate->alt = KM_MOD_HELD; } } #endif @@ -1235,7 +1236,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_OSKEY) { - win->eventstate->oskey = KM_MOD_FIRST; + win->eventstate->oskey = KM_MOD_HELD; } } #endif @@ -1945,7 +1946,7 @@ wmWindow *WM_window_find_under_cursor(const wmWindowManager *wm, int scr_pos[2]; wm_window_screen_pos_get(win_iter, desk_pos, scr_pos); - if (scr_pos[0] >= 0 && win_iter->posy >= 0 && scr_pos[0] <= WM_window_pixels_x(win_iter) && + if (scr_pos[0] >= 0 && scr_pos[1] >= 0 && scr_pos[0] <= WM_window_pixels_x(win_iter) && scr_pos[1] <= WM_window_pixels_y(win_iter)) { copy_v2_v2_int(r_mval, scr_pos); diff --git a/source/blender/windowmanager/wm_cursors.h b/source/blender/windowmanager/wm_cursors.h index 2842538ebf1..d1694454490 100644 --- a/source/blender/windowmanager/wm_cursors.h +++ b/source/blender/windowmanager/wm_cursors.h @@ -74,6 +74,8 @@ typedef enum WMCursorType { WM_CURSOR_NONE, WM_CURSOR_MUTE, + WM_CURSOR_PICK_AREA, + /* --- ALWAYS LAST ----- */ WM_CURSOR_NUM, } WMCursorType; diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 297205d1e79..8891840cb75 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -35,6 +35,8 @@ #include "GHOST_C-api.h" +#include "GPU_platform.h" + #include "WM_api.h" #include "wm_surface.h" @@ -91,6 +93,11 @@ bool wm_xr_init(wmWindowManager *wm) if (G.debug & G_DEBUG_XR_TIME) { create_info.context_flag |= GHOST_kXrContextDebugTime; } +#ifdef WIN32 + if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_WIN, GPU_DRIVER_ANY)) { + create_info.context_flag |= GHOST_kXrContextGpuNVIDIA; + } +#endif if (!(context = GHOST_XrContextCreate(&create_info))) { return false; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index dc15b579e9d..88bf3ff453c 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -705,7 +705,7 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, } if (failure) { - CLOG_ERROR(&LOG, "Failed to get buffer, %s\n", err_out); + CLOG_ERROR(&LOG, "Failed to get buffer, %s", err_out); return false; } diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 85ba4eca307..943646daa81 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -1920,7 +1920,7 @@ static int arg_handle_python_use_system_env_set(int UNUSED(argc), static const char arg_handle_addons_set_doc[] = "<addon(s)>\n" - "\tComma separated list of add-ons (no spaces)."; + "\tComma separated list (no spaces) of add-ons to enable in addition to any default add-ons."; static int arg_handle_addons_set(int argc, const char **argv, void *data) { /* workaround for scripts not getting a bpy.context.scene, causes internal errors elsewhere */ diff --git a/source/tools b/source/tools -Subproject 5cf2fc3e5dc28025394b57d8743401295528f31 +Subproject c8579c5cf43229843df505da9644b5b0b720197 |